xref: /illumos-gate/usr/src/lib/libnisdb/nis_parse_ldap_map.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <locale.h>
36 
37 #include "ldap_parse.h"
38 #include "nis_parse_ldap_conf.h"
39 
40 /* other attribute functions */
41 static char *getIndex(const char **s_cur, const char *end_s);
42 static bool_t get_ttls(const char *s, const char *s_end,
43     __nis_table_mapping_t *t_mapping);
44 static __nis_object_dn_t *parse_object_dn(const char *s, const char *end);
45 static int	parse_name_fields(const char *name_s, const char *name_s_end,
46 	__nis_table_mapping_t *t_mapping);
47 static void get_mapping_rule(const char *s, int len,
48     __nis_table_mapping_t *tbl, bool_t to_ldap);
49 static bool_t get_deleteDisp(const char *s_begin, const char *s_end,
50     __nis_object_dn_t *obj_dn);
51 
52 /* mapping rule functions */
53 static const char *get_lhs(const char *s, const char *end_s,
54     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
55 static const char *get_lhs_match(const char *s, const char *end_s,
56     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
57 static const char *get_lhs_paren_item(const char *s, const char *end_s,
58     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
59 static const char *get_rhs(const char *s, const char *end_s,
60     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
61 static const char *get_mapping_item(const char *s, const char *end_s,
62     __nis_mapping_item_t *item, __nis_mapping_item_type_t type);
63 static const char *get_print_mapping_element(const char *s,
64     const char *end_s, char *fmt_string, __nis_mapping_element_t *e,
65     __nis_mapping_item_type_t item_type);
66 static const char *get_subElement(const char *s, const char *end_s,
67     __nis_mapping_sub_element_t *subelement,
68     __nis_mapping_item_type_t type);
69 static bool_t get_mapping_format(const char *fmt_string,
70     __nis_mapping_format_t **fmt, int *nfmt, int *numItems,
71     bool_t print_mapping);
72 extern __yp_domain_context_t ypDomains;
73 
74 /*
75  * FUNCTION:	add_mapping_attribute
76  *
77  *	Adds the attribute value to __nis_table_mapping_t
78  *	if the value is not yet set for the given database.
79  *
80  * RETURN VALUE:	0 on success, -1 on failure
81  *
82  * INPUT:		attribute number and value
83  */
84 
85 int
86 add_mapping_attribute(
87 	config_key		attrib_num,
88 	const char		*attrib_val,
89 	int			attrib_len,
90 	__nis_table_mapping_t	**table_mapping)
91 {
92 	const char		*s;
93 	const char		*attrib_end;
94 	const char		*db_id_end;
95 	const char		*begin_token;
96 	const char		*end_token;
97 	char			*index_string;
98 	__nis_object_dn_t	*objectDN;
99 	__nis_table_mapping_t	*t_mapping;
100 	__nis_table_mapping_t	*t;
101 
102 	bool_t			new_mapping	= FALSE;
103 	int				nm;
104 	char			*tmp_dbId;
105 
106 	attrib_end = attrib_val + attrib_len;
107 	for (s = attrib_val; s < attrib_end; s++)
108 		if (*s == COLON_CHAR)
109 			break;
110 
111 	if (s == attrib_end || *attrib_val == COLON_CHAR) {
112 		p_error = parse_unexpected_data_end_rule;
113 		return (-1);
114 	}
115 
116 	db_id_end = s;
117 	while (s > attrib_val && is_whitespace(s[-1]))
118 		s--;
119 
120 	if (s == attrib_val) {
121 		p_error = parse_unexpected_data_end_rule;
122 		return (-1);
123 	}
124 
125 	if (yp2ldap) {
126 		tmp_dbId = s_strndup(attrib_val, s - attrib_val);
127 		if (tmp_dbId == NULL) {
128 			p_error = parse_no_mem_error;
129 			return (-1);
130 		}
131 		if (strchr(tmp_dbId, COMMA_CHAR)) {
132 			/* domain explicitly specified */
133 			nm = check_domain_specific_order(tmp_dbId,
134 				attrib_num, *table_mapping, &ypDomains);
135 			/*
136 			 * No logging is needed here, as
137 			 * check_domain_specific_order
138 			 * will log any appropriate errors.
139 			 */
140 			if (nm != 0) {
141 				free(tmp_dbId);
142 				return (-1);
143 			}
144 		}
145 		free(tmp_dbId);
146 	}
147 
148 	if ((t_mapping = find_table_mapping(attrib_val,
149 			s - attrib_val, *table_mapping)) == NULL) {
150 		/* No mapping with this id, create one */
151 		t_mapping = (__nis_table_mapping_t *)
152 			s_calloc(1, sizeof (__nis_table_mapping_t));
153 
154 		if (t_mapping == NULL) {
155 			p_error = parse_no_mem_error;
156 			return (-1);
157 		}
158 		(void) initialize_table_mapping(t_mapping);
159 
160 		/* dbId is the label before the colon */
161 		t_mapping->dbId = s_strndup(attrib_val, s - attrib_val);
162 		if (t_mapping->dbId == NULL) {
163 			p_error = parse_no_mem_error;
164 			free(t_mapping);
165 			return (-1);
166 		}
167 		new_mapping = TRUE;
168 	} else {
169 		/* a table mapping already exists, use it */
170 		new_mapping = FALSE;
171 	}
172 
173 	s = db_id_end + 1;
174 	while (s < attrib_end && is_whitespace(*s))
175 		s++;
176 
177 	switch (attrib_num) {
178 		case key_yp_map_flags:
179 			if (t_mapping->usedns_flag != 0 ||
180 				t_mapping->securemap_flag != 0) {
181 				warn_duplicate_map(t_mapping->dbId,
182 					attrib_num);
183 				break;
184 			}
185 			while (is_whitespace(*s) && s < attrib_end)
186 				s++;
187 			while (s < attrib_end) {
188 				if (s < attrib_end && *s == 'b')
189 					t_mapping->usedns_flag = 1;
190 				if (s < attrib_end && *s == 's')
191 					t_mapping->securemap_flag = 1;
192 				s++;
193 			}
194 			break;
195 		case key_yp_comment_char:
196 			if (t_mapping->commentChar !=
197 				DEFAULT_COMMENT_CHAR) {
198 				warn_duplicate_map(t_mapping->dbId, attrib_num);
199 				break;
200 			}
201 			while (is_whitespace(*s) && s < attrib_end)
202 				s++;
203 			if (s < attrib_end && (s+1) < attrib_end &&
204 				(s+2) <= attrib_end) {
205 				while (is_whitespace(attrib_end[-1]))
206 					attrib_end--;
207 				while (*s != SINGLE_QUOTE_CHAR)
208 					s++;
209 				if (*s == SINGLE_QUOTE_CHAR &&
210 					*(s+2) == SINGLE_QUOTE_CHAR) {
211 					t_mapping->commentChar = *(s+1);
212 				} else if (*s == SINGLE_QUOTE_CHAR &&
213 					*(s+1) == SINGLE_QUOTE_CHAR) {
214 					t_mapping->commentChar = NULL;
215 				} else {
216 					/* anything else is an error */
217 					p_error = parse_bad_yp_comment_error;
218 				}
219 				break;
220 			} else {
221 				p_error = parse_bad_yp_comment_error;
222 				break;
223 			}
224 		case key_yp_repeated_field_separators:
225 			while (s < attrib_end && is_whitespace(*s))
226 				s++;
227 			if (s < attrib_end) {
228 				while (is_whitespace(attrib_end[-1]))
229 					attrib_end--;
230 				while (s < attrib_end &&
231 						*s != DOUBLE_QUOTE_CHAR)
232 					s++;
233 				s++;
234 				begin_token = s;
235 				while (s < attrib_end &&
236 						*s != DOUBLE_QUOTE_CHAR) {
237 					if (*s == ESCAPE_CHAR)
238 						s++;
239 					s++;
240 				}
241 				t_mapping->separatorStr =
242 					s_strndup(begin_token, s - begin_token);
243 				if (t_mapping->separatorStr == NULL)
244 					break;
245 			} else {
246 				p_error = parse_bad_field_separator_error;
247 			}
248 			break;
249 		case key_yp_name_fields:
250 		case key_yp_split_field:
251 			if (t_mapping->e || t_mapping->numSplits > 0) {
252 				warn_duplicate_map(t_mapping->dbId,
253 					attrib_num);
254 				break;
255 			}
256 			if (parse_name_fields(s, attrib_end, t_mapping)) {
257 				p_error = parse_bad_name_field;
258 			}
259 			break;
260 		case key_yp_db_id_map:
261 		case key_db_id_map:
262 			if (t_mapping->objName != NULL) {
263 				warn_duplicate_map(t_mapping->dbId, attrib_num);
264 				break;
265 			}
266 
267 			if (s < attrib_end && *s == OPEN_BRACKET) {
268 				index_string = getIndex(&s, attrib_end);
269 				if (index_string == NULL)
270 					break;
271 				(void) parse_index(index_string,
272 					index_string + strlen(index_string),
273 					&t_mapping->index);
274 				free(index_string);
275 				if (p_error != no_parse_error)
276 					break;
277 			}
278 			while (is_whitespace(*s) && s < attrib_end)
279 				s++;
280 			if (s < attrib_end) {
281 				while (is_whitespace(attrib_end[-1]))
282 					attrib_end--;
283 				t_mapping->objName =
284 					s_strndup_esc(s, attrib_end - s);
285 			} else {
286 				if (yp2ldap) {
287 					p_error = parse_bad_map_error;
288 				} else {
289 					t_mapping->objName = s_strndup(s, 0);
290 				}
291 			}
292 			break;
293 
294 		case key_yp_entry_ttl:
295 		case key_entry_ttl:
296 			if (t_mapping->initTtlLo != (time_t)NO_VALUE_SET) {
297 				warn_duplicate_map(t_mapping->dbId, attrib_num);
298 				break;
299 			}
300 
301 			if (!get_ttls(s, attrib_end, t_mapping))
302 				p_error = parse_bad_ttl_format_error;
303 			break;
304 
305 		case key_yp_ldap_object_dn:
306 		case key_ldap_object_dn:
307 			if (t_mapping->objectDN != NULL) {
308 				warn_duplicate_map(t_mapping->dbId, attrib_num);
309 				break;
310 			}
311 			objectDN = parse_object_dn(s, attrib_end);
312 			if (objectDN == NULL)
313 				break;
314 			t_mapping->objectDN = objectDN;
315 			t_mapping->seq_num = seq_num++;
316 			break;
317 
318 		case key_nis_to_ldap_map:
319 		case key_nisplus_to_ldap_map:
320 			if (t_mapping->ruleToLDAP != 0) {
321 				warn_duplicate_map(t_mapping->dbId, attrib_num);
322 				break;
323 			}
324 
325 			get_mapping_rule(s, attrib_end - s, t_mapping, TRUE);
326 			break;
327 
328 		case key_ldap_to_nis_map:
329 		case key_ldap_to_nisplus_map:
330 			if (t_mapping->ruleFromLDAP != NULL) {
331 				warn_duplicate_map(t_mapping->dbId, attrib_num);
332 				break;
333 			}
334 
335 			get_mapping_rule(s, attrib_end - s, t_mapping, FALSE);
336 			break;
337 
338 		default:
339 			p_error = parse_internal_error;
340 			break;
341 	}
342 	if (p_error == no_parse_error) {
343 		if (new_mapping) {
344 			if (*table_mapping == NULL)
345 				*table_mapping = t_mapping;
346 			else {
347 				for (t = *table_mapping; t->next != NULL;
348 				    t = t->next)
349 					;
350 				t->next = t_mapping;
351 			}
352 		}
353 	} else {
354 		if (new_mapping)
355 			free_table_mapping(t_mapping);
356 	}
357 	return (p_error == no_parse_error ? 0 : -1);
358 }
359 
360 /*
361  * FUNCTION:	add_ypdomains_attribute
362  *
363  * Adds the yp domains information to the __yp_domain_context_t
364  * structure.
365  *
366  * RETURN:		0 on success, -1 on failure
367  *
368  * INPUT:		attribute number and value
369  */
370 
371 int
372 add_ypdomains_attribute(
373 	config_key		attrib_num,
374 	const char		*attrib_val,
375 	int				attrib_len,
376 	__yp_domain_context_t	*ypDomains)
377 {
378 	const char 		*s;
379 	const char		*attrib_end;
380 	int				numDomains = 0;
381 	int 			i;
382 	char			*tmp_str;
383 	int				ret = 0;
384 
385 	attrib_end = attrib_val + attrib_len;
386 	for (s = attrib_val; s < attrib_end; s++) {
387 		if (*s == COLON_CHAR) {
388 			break;
389 		}
390 	}
391 	while (s > attrib_val && is_whitespace(s[-1]))
392 		s--;
393 
394 	if (s == attrib_val) {
395 		p_error = parse_unexpected_data_end_rule;
396 		return (-1);
397 	}
398 
399 	if (ypDomains == NULL) {
400 		/*
401 		 * No point allocating. We cant return the resulting structure,
402 		 * so just return failure. Should not ever happen because we
403 		 * are always called with a pointer to the global ypDomains
404 		 * structure.
405 		 */
406 		return (-1);
407 	}
408 
409 	switch (attrib_num) {
410 		case key_yp_domain_context:
411 			numDomains = ypDomains->numDomains;
412 			ypDomains->domainLabels =
413 				(char **)s_realloc(ypDomains->domainLabels,
414 				(numDomains + 1) *
415 				sizeof (ypDomains->domainLabels[0]));
416 			if (ypDomains->domainLabels == NULL) {
417 				p_error = parse_no_mem_error;
418 				free_yp_domain_context(ypDomains);
419 				break;
420 			}
421 			ypDomains->domainLabels[numDomains] =
422 				s_strndup(attrib_val, s - attrib_val);
423 			if (ypDomains->domainLabels[numDomains] == NULL) {
424 				p_error = parse_no_mem_error;
425 				free_yp_domain_context(ypDomains);
426 				break;
427 			}
428 			ypDomains->numDomains = numDomains + 1;
429 			while (s < attrib_end && is_whitespace(*s))
430 				s++;
431 			if (*s == COLON_CHAR)
432 				s++;
433 			while (s < attrib_end && is_whitespace(*s))
434 				s++;
435 			ypDomains->domains =
436 				(char **)s_realloc(ypDomains->domains,
437 				(numDomains + 1) *
438 				sizeof (ypDomains->domains[0]));
439 			if (ypDomains->domains == NULL) {
440 				p_error = parse_no_mem_error;
441 				free_yp_domain_context(ypDomains);
442 				break;
443 			}
444 
445 			if (s < attrib_end) {
446 				while (is_whitespace(attrib_end[-1]))
447 					attrib_end--;
448 				ypDomains->domains[numDomains] =
449 					s_strndup_esc(s, attrib_end - s);
450 				if (ypDomains->domains[numDomains] == NULL) {
451 					p_error = parse_no_mem_error;
452 					free_yp_domain_context(ypDomains);
453 					break;
454 				}
455 			} else {
456 				p_error = parse_unexpected_yp_domain_end_error;
457 				free(ypDomains->domainLabels[numDomains]);
458 				ypDomains->domainLabels[numDomains] = NULL;
459 				ypDomains->numDomains--;
460 				free_yp_domain_context(ypDomains);
461 			}
462 			break;
463 		case key_yppasswdd_domains:
464 			ypDomains->yppasswddDomainLabels =
465 				(char **)s_realloc(
466 				ypDomains->yppasswddDomainLabels,
467 				(ypDomains->numYppasswdd + 1) *
468 				sizeof (ypDomains->yppasswddDomainLabels[0]));
469 			if (ypDomains->yppasswddDomainLabels == NULL) {
470 				p_error = parse_no_mem_error;
471 				break;
472 			}
473 			ypDomains->yppasswddDomainLabels
474 				[ypDomains->numYppasswdd] =
475 				s_strndup(attrib_val, s - attrib_val);
476 			if (ypDomains->yppasswddDomainLabels
477 				[ypDomains->numYppasswdd] == NULL) {
478 				p_error = parse_no_mem_error;
479 			}
480 			ypDomains->numYppasswdd++;
481 			break;
482 	}
483 
484 	return (p_error == no_parse_error ? 0 : -1);
485 }
486 
487 /*
488  * FUNCTION:	get_ttls
489  *
490  *	Parse time to live attribute
491  *
492  * RETURN VALUE:	TRUE on success, FALSE on failure
493  *
494  * INPUT:		the attribute value
495  */
496 
497 static bool_t
498 get_ttls(
499 	const char		*s,
500 	const char		*s_end,
501 	__nis_table_mapping_t	*t_mapping)
502 {
503 	time_t		initTtlHi	= 0;
504 	time_t		initTtlLo	= 0;
505 	time_t		ttl		= 0;
506 	time_t		digit;
507 
508 	/*
509 	 * attribute should be of the form
510 	 * initialTTLlo ":" initialTTLhi ":" runningTTL
511 	 */
512 
513 	if (s == s_end) {
514 		p_error = parse_bad_ttl_format_error;
515 		return (FALSE);
516 	}
517 
518 	if (isdigit(*s)) {
519 		while (s < s_end && isdigit(*s)) {
520 			digit = (*s++) - '0';
521 			if (WILL_OVERFLOW_TIME(initTtlLo, digit))
522 				initTtlLo = TIME_MAX;
523 			else
524 				initTtlLo = initTtlLo * 10 + digit;
525 		}
526 	} else {
527 		initTtlLo = ONE_HOUR;
528 	}
529 
530 	while (s < s_end && is_whitespace(*s))
531 		s++;
532 	if (s + 1 >= s_end || *s++ != COLON_CHAR) {
533 		p_error = parse_bad_ttl_format_error;
534 		return (FALSE);
535 	}
536 
537 	while (s < s_end && is_whitespace(*s))
538 		s++;
539 	if (isdigit(*s)) {
540 		while (s < s_end && isdigit(*s)) {
541 			digit = (*s++) - '0';
542 			if (WILL_OVERFLOW_TIME(initTtlHi, digit))
543 				initTtlHi = TIME_MAX;
544 			else
545 				initTtlHi = initTtlHi * 10 + digit;
546 		}
547 	} else {
548 		initTtlHi = initTtlLo;
549 	}
550 
551 	while (s < s_end && is_whitespace(*s))
552 		s++;
553 	if (s >= s_end || *s++ != COLON_CHAR) {
554 		p_error = parse_bad_ttl_format_error;
555 		return (FALSE);
556 	}
557 
558 	while (s < s_end && is_whitespace(*s))
559 		s++;
560 	if (isdigit(*s)) {
561 		while (s < s_end && isdigit(*s)) {
562 			digit = (*s++) - '0';
563 			if (WILL_OVERFLOW_TIME(ttl, digit))
564 				ttl = TIME_MAX;
565 			else
566 				ttl = ttl * 10 + digit;
567 		}
568 	} else {
569 		ttl = ONE_HOUR;
570 	}
571 	while (s < s_end && is_whitespace(*s))
572 		s++;
573 	if (s != s_end) {
574 		p_error = parse_bad_ttl_format_error;
575 		return (FALSE);
576 	}
577 
578 	t_mapping->initTtlLo = initTtlLo;
579 	t_mapping->initTtlHi = initTtlHi;
580 	t_mapping->ttl = ttl;
581 	return (TRUE);
582 }
583 
584 /*
585  * FUNCTION:	parse_name_fields
586  *
587  * Parse yp name fields
588  *
589  * RETURN VALUE:	0 on success, non-zero on failure
590  *
591  * INPUTS:		attrib_value and attribute_end pointers.
592  */
593 
594 static int
595 parse_name_fields(const char *name_s,
596 	const char *name_s_end,
597 	__nis_table_mapping_t   *t_map)
598 {
599 	int	i, n = 0;
600 	int nElements = 0;
601 	int numSplits = 0;
602 	int parse_next_line = 1;
603 	int itm_count = 0;
604 	const char	*begin_fmt;
605 	const char	*end_fmt;
606 	const char	*begin_token;
607 	const char	*end_token;
608 	char	*fmt_string = NULL;
609 	__nis_mapping_format_t  *base = NULL;
610 	__nis_mapping_item_t    *item = NULL;
611 	__nis_mapping_element_t *elmnt = NULL;
612 	__nis_mapping_item_type_t   item_type = mit_nisplus;
613 	token_type	token;
614 
615 	t_map->numColumns = 0;
616 
617 	for (; parse_next_line > 0; parse_next_line--) {
618 		nElements = 0;
619 		item = NULL;
620 		base = NULL;
621 		while (name_s < name_s_end && *name_s != OPEN_PAREN_CHAR)
622 			name_s++;
623 		if (name_s == name_s_end) {
624 			p_error = parse_unexpected_data_end_rule;
625 			return (1);
626 		}
627 		while (name_s < name_s_end && *name_s != DOUBLE_QUOTE_CHAR)
628 			name_s++;
629 		if (name_s == name_s_end) {
630 			p_error = parse_unexpected_data_end_rule;
631 			return (1);
632 		}
633 		begin_fmt = ++name_s; /* start of format string */
634 		while (name_s < name_s_end && *name_s != DOUBLE_QUOTE_CHAR)
635 			name_s++;
636 		if (name_s == name_s_end) {
637 			p_error = parse_unexpected_data_end_rule;
638 			return (1);
639 		}
640 		end_fmt = name_s;
641 		fmt_string = s_strndup(begin_fmt, end_fmt - begin_fmt);
642 		if (fmt_string == NULL) {
643 			p_error = parse_no_mem_error;
644 			return (2);
645 		}
646 		if (!get_mapping_format(fmt_string, &base, &n, NULL, FALSE)) {
647 			p_error = parse_internal_error;
648 			free(fmt_string);
649 			fmt_string = NULL;
650 			return (3);
651 		}
652 		free(fmt_string);
653 		fmt_string = NULL;
654 		for (n = 0; base[n].type != mmt_end; n++) {
655 			if (base[n].type != mmt_item && base[n].type
656 				!= mmt_berstring) {
657 				if (base[n].type == mmt_berstring_null)
658 					base[n].type = mmt_berstring;
659 				continue;
660 			}
661 			while (name_s < name_s_end && *name_s != COMMA_CHAR)
662 				name_s++;
663 			name_s++;    /* now at comma char */
664 			while (name_s < name_s_end && is_whitespace(*name_s))
665 				name_s++;
666 			begin_token = name_s++;
667 			end_token = name_s_end;
668 			name_s = get_next_token(
669 				&begin_token, &end_token, &token);
670 			if (name_s == NULL) {
671 				p_error = parse_item_expected_error;
672 				return (4);
673 			}
674 			if (token != string_token) {
675 				p_error = parse_item_expected_error;
676 				return (5);
677 			}
678 			item = (__nis_mapping_item_t *)s_realloc(item,
679 				(nElements + 1) *
680 				sizeof (__nis_mapping_item_t));
681 			if (item == NULL) {
682 				p_error = parse_no_mem_error;
683 				return (2);
684 			}
685 			name_s = get_mapping_item(begin_token, name_s_end,
686 				&item[nElements], item_type);
687 			if (name_s == NULL) {
688 				p_error = parse_unmatched_escape;
689 				for (n = 0; n < (nElements + 1); n++)
690 					free_mapping_item(&item[n]);
691 				free_mapping_format(base);
692 				return (4);
693 			}
694 			nElements++;
695 		}
696 		if (p_error != no_parse_error) {
697 			for (n = 0; n < (nElements + 1); n++)
698 				free_mapping_item(&item[n]);
699 			free_mapping_format(base);
700 			return (6);
701 		}
702 		name_s = skip_token(name_s, name_s_end, close_paren_token);
703 		if (name_s == NULL) {
704 			p_error = parse_close_paren_expected_error;
705 			for (n = 0; n < (nElements + 1); n++)
706 				free_mapping_item(&item[n]);
707 			free_mapping_format(base);
708 			return (4);
709 		}
710 		while (name_s < name_s_end && is_whitespace(*name_s))
711 			name_s++;
712 		if (*name_s == COMMA_CHAR)
713 			parse_next_line++;
714 
715 		if (nElements == 0) {
716 			p_error = parse_no_match_item;
717 			for (n = 0; n < (nElements + 1); n++)
718 				free_mapping_item(&item[n]);
719 			free_mapping_format(base);
720 			return (7);
721 		}
722 		elmnt = (__nis_mapping_element_t *)s_realloc(elmnt,
723 			(numSplits + 1) *
724 			sizeof (__nis_mapping_element_t));
725 		if (elmnt == NULL) {
726 			for (n = 0; n < (nElements + 1); n++)
727 				free_mapping_item(&item[n]);
728 			free_mapping_format(base);
729 			p_error = parse_no_mem_error;
730 			return (2);
731 		}
732 		elmnt[numSplits].type = me_match;
733 		elmnt[numSplits].element.match.numItems = nElements;
734 		elmnt[numSplits].element.match.item = item;
735 		elmnt[numSplits].element.match.fmt = base;
736 		item = NULL;
737 		base = NULL;
738 
739 		t_map->e = elmnt;
740 		t_map->numSplits = numSplits;
741 		n = t_map->numColumns;
742 
743 		for (i = n, itm_count = 0; i < n + nElements; i++) {
744 			if (t_map->e[numSplits].element.
745 				match.item[itm_count].name) {
746 				if (!add_column(t_map,
747 					t_map->e[numSplits].element.
748 					match.item[itm_count].name))
749 					return (1);
750 				itm_count++;
751 			} else {
752 				p_error = parse_internal_error;
753 				for (n = 0; n < (nElements + 1); n++)
754 					free_mapping_item(&item[n]);
755 				free_mapping_format(base);
756 				free_mapping_element(elmnt);
757 				return (1);
758 			}
759 		}
760 		numSplits++;
761 	}
762 	elmnt = NULL;
763 
764 	if (item != NULL) {
765 		for (n = 0; n < t_map->numColumns; n++) {
766 			free_mapping_item(&item[n]);
767 		}
768 		free(item);
769 	}
770 	if (elmnt != NULL)
771 		free_mapping_element(elmnt);
772 	if (base != NULL)
773 		free_mapping_format(base);
774 
775 	return (p_error == no_parse_error ? 0 : -1);
776 }
777 
778 /*
779  * FUNCTION:	parse_object_dn
780  *
781  *	Parse object dn attribute
782  *
783  * RETURN VALUE:	__nis_object_dn_t on success
784  *			NULL on failure
785  *
786  * INPUT:		the attribute value
787  */
788 
789 static __nis_object_dn_t *
790 parse_object_dn(const char *s, const char *end)
791 {
792 	const char		*s_begin;
793 	const char		*s_end;
794 	object_dn_token		token;
795 	parse_object_dn_state	dn_state	= dn_begin_parse;
796 	__nis_object_dn_t	*obj_dn		= NULL;
797 	__nis_object_dn_t	*next		= NULL;
798 	__nis_object_dn_t	*last		= NULL;
799 
800 	/*
801 	 * The attribute should be of form
802 	 * objectDN *( ";" objectDN )
803 	 * objectDN = readObjectSpec [":"[writeObjectSpec]]
804 	 * readObjectSpec = [baseAndScope [filterAttrValList]]
805 	 * writeObjectSpec = [baseAndScope [attrValList [":" deleteDisp]]]
806 	 */
807 
808 	while (s < end) {
809 		s_begin = s;
810 		s_end = end;
811 		s = get_next_object_dn_token(&s_begin, &s_end, &token);
812 		if (s == NULL)
813 			break;
814 
815 		if (token == dn_no_token || token == dn_semi_token) {
816 			if (obj_dn == NULL)
817 				obj_dn = next;
818 			else
819 				last->next = next;
820 			last = next;
821 			next = NULL;
822 			if (token == dn_no_token)
823 				break;
824 			dn_state = dn_begin_parse;
825 		}
826 		if (next == NULL) {
827 			next = (__nis_object_dn_t *)
828 				s_calloc(1, sizeof (__nis_object_dn_t));
829 			if (next == NULL)
830 				break;
831 			next->read.scope = LDAP_SCOPE_ONELEVEL;
832 			next->write.scope = LDAP_SCOPE_UNKNOWN;
833 			next->delDisp = dd_always;
834 		}
835 		if (token == dn_semi_token)
836 			continue;
837 
838 		switch (dn_state) {
839 		    case dn_begin_parse:
840 			if (token == dn_ques_token)
841 				dn_state = dn_got_read_q_scope;
842 			else if (token == dn_colon_token) {
843 				dn_state = dn_got_write_colon;
844 				next->write.scope = LDAP_SCOPE_ONELEVEL;
845 			} else {
846 				if (!validate_dn(s_begin, s_end - s_begin))
847 					break;
848 				next->read.base =
849 					s_strndup_esc(s_begin, s_end - s_begin);
850 				dn_state = dn_got_read_dn;
851 			}
852 			break;
853 		    case dn_got_read_dn:
854 			if (token == dn_ques_token)
855 				dn_state = dn_got_read_q_scope;
856 			else if (token == dn_colon_token) {
857 				dn_state = dn_got_write_colon;
858 				next->write.scope = LDAP_SCOPE_ONELEVEL;
859 			} else
860 				p_error = parse_object_dn_syntax_error;
861 			break;
862 		    case dn_got_read_q_scope:
863 			if (token == dn_ques_token)
864 				dn_state = dn_got_read_q_filter;
865 			else if (token == dn_colon_token) {
866 				dn_state = dn_got_write_colon;
867 				next->write.scope = LDAP_SCOPE_ONELEVEL;
868 			} else if (token == dn_base_token) {
869 				next->read.scope = LDAP_SCOPE_BASE;
870 				dn_state = dn_got_read_scope;
871 			} else if (token == dn_one_token) {
872 				next->read.scope = LDAP_SCOPE_ONELEVEL;
873 				dn_state = dn_got_read_scope;
874 			} else if (token == dn_sub_token) {
875 				next->read.scope = LDAP_SCOPE_SUBTREE;
876 				dn_state = dn_got_read_scope;
877 			} else {
878 				p_error = parse_invalid_scope;
879 			}
880 			break;
881 		    case dn_got_read_scope:
882 			if (token == dn_ques_token)
883 				dn_state = dn_got_read_q_filter;
884 			else if (token == dn_colon_token) {
885 				dn_state = dn_got_write_colon;
886 				next->write.scope = LDAP_SCOPE_ONELEVEL;
887 			} else
888 				p_error = parse_object_dn_syntax_error;
889 			break;
890 		    case dn_got_read_q_filter:
891 			if (token == dn_ques_token) {
892 				p_error = parse_object_dn_syntax_error;
893 			} else if (token == dn_colon_token) {
894 				dn_state = dn_got_write_colon;
895 				next->write.scope = LDAP_SCOPE_ONELEVEL;
896 			} else {
897 				if (!validate_ldap_filter(s_begin, s_end))
898 					break;
899 				next->read.attrs =
900 					s_strndup_esc(s_begin, s_end - s_begin);
901 				dn_state = dn_got_read_filter;
902 			}
903 			break;
904 		    case dn_got_read_filter:
905 			if (token == dn_ques_token) {
906 				p_error = parse_object_dn_syntax_error;
907 			} else if (token == dn_colon_token) {
908 				dn_state = dn_got_write_colon;
909 				next->write.scope = LDAP_SCOPE_ONELEVEL;
910 			} else
911 				p_error = parse_object_dn_syntax_error;
912 			break;
913 		    case dn_got_write_colon:
914 			if (token == dn_ques_token)
915 				dn_state = dn_got_write_q_scope;
916 			else if (token == dn_colon_token) {
917 				dn_state = dn_got_delete_colon;
918 			} else {
919 				if (!validate_dn(s_begin, s_end - s_begin))
920 					break;
921 				next->write.base =
922 					s_strndup_esc(s_begin, s_end - s_begin);
923 				dn_state = dn_got_write_dn;
924 			}
925 			break;
926 		    case dn_got_write_dn:
927 			if (token == dn_ques_token)
928 				dn_state = dn_got_write_q_scope;
929 			else if (token == dn_colon_token) {
930 				dn_state = dn_got_delete_colon;
931 			} else
932 				p_error = parse_object_dn_syntax_error;
933 			break;
934 		    case dn_got_write_q_scope:
935 			if (token == dn_ques_token)
936 				dn_state = dn_got_write_q_filter;
937 			else if (token == dn_colon_token) {
938 				dn_state = dn_got_delete_colon;
939 			} else if (token == dn_base_token) {
940 				next->write.scope = LDAP_SCOPE_BASE;
941 				dn_state = dn_got_write_scope;
942 			} else if (token == dn_one_token) {
943 				next->write.scope = LDAP_SCOPE_ONELEVEL;
944 				dn_state = dn_got_write_scope;
945 			} else if (token == dn_sub_token) {
946 				next->write.scope = LDAP_SCOPE_SUBTREE;
947 				dn_state = dn_got_write_scope;
948 			} else {
949 				p_error = parse_invalid_scope;
950 			}
951 			break;
952 		    case dn_got_write_scope:
953 			if (token == dn_ques_token)
954 				dn_state = dn_got_write_q_filter;
955 			else if (token == dn_colon_token) {
956 				dn_state = dn_got_delete_colon;
957 			} else
958 				p_error = parse_object_dn_syntax_error;
959 			break;
960 		    case dn_got_write_q_filter:
961 			if (token == dn_ques_token) {
962 				p_error = parse_object_dn_syntax_error;
963 			} else if (token == dn_colon_token) {
964 				dn_state = dn_got_delete_colon;
965 			} else {
966 				if (!validate_ldap_filter(s_begin, s_end))
967 					break;
968 				next->write.attrs =
969 					s_strndup_esc(s_begin, s_end - s_begin);
970 				dn_state = dn_got_write_filter;
971 			}
972 			break;
973 		    case dn_got_write_filter:
974 			if (token == dn_ques_token) {
975 				p_error = parse_object_dn_syntax_error;
976 			} else if (token == dn_colon_token) {
977 				dn_state = dn_got_delete_colon;
978 
979 			} else
980 				p_error = parse_semi_expected_error;
981 			break;
982 		    case dn_got_delete_colon:
983 			if (token == dn_ques_token) {
984 				p_error = parse_object_dn_syntax_error;
985 			} else if (token == dn_colon_token) {
986 				p_error = parse_object_dn_syntax_error;
987 			} else {
988 				if (!get_deleteDisp(s_begin, s_end, next))
989 					break;
990 				dn_state = dn_got_delete_dsp;
991 			}
992 			break;
993 		    case dn_got_delete_dsp:
994 			p_error = parse_object_dn_syntax_error;
995 			break;
996 		}
997 
998 		if (p_error != no_parse_error)
999 			break;
1000 	}
1001 	if (p_error != no_parse_error) {
1002 		if (obj_dn != NULL)
1003 			free_object_dn(obj_dn);
1004 		if (next != NULL)
1005 			free_object_dn(next);
1006 		obj_dn = NULL;
1007 	} else if (next != NULL) {
1008 		if (obj_dn == NULL)
1009 			obj_dn = next;
1010 		else
1011 			last->next = next;
1012 	} else if (obj_dn == NULL)
1013 		obj_dn = (__nis_object_dn_t *)
1014 			s_calloc(1, sizeof (__nis_object_dn_t));
1015 
1016 	return (obj_dn);
1017 }
1018 
1019 /*
1020  * FUNCTION:	get_mapping_rule
1021  *
1022  *	Parse mapping rule attributes
1023  *
1024  * RETURN VALUE:	None. Errors determined by p_error
1025  *
1026  * INPUT:		the attribute value and mapping rule type
1027  */
1028 
1029 static void
1030 get_mapping_rule(
1031 	const char		*s,
1032 	int			len,
1033 	__nis_table_mapping_t	*tbl,
1034 	bool_t			to_ldap)
1035 {
1036 	const char		*end_s			= s + len;
1037 	const char		*begin_token;
1038 	const char		*end_token;
1039 	__nis_mapping_rule_t	**rule			= NULL;
1040 	__nis_mapping_rule_t	*next			= NULL;
1041 	/* __nis_mapping_rule_t	**r; */
1042 	token_type		t;
1043 	int			nRules			= 0;
1044 	const char		*s1;
1045 	int			i;
1046 
1047 	/*
1048 	 * The attribute value is of the form
1049 	 * colattrspec *("," colattrspec)
1050 	 * colattrspec	= lhs "=" rhs
1051 	 * lhs		= lval | namespeclist
1052 	 * rhs		= rval | [namespec]
1053 	 */
1054 
1055 	for (;;) {
1056 		if ((next = (__nis_mapping_rule_t *)
1057 		    s_calloc(1, sizeof (__nis_mapping_rule_t))) == NULL)
1058 			break;
1059 
1060 		s = get_lhs(s, end_s, &next->lhs,
1061 			to_ldap ? mit_ldap : mit_nisplus);
1062 		if (s == NULL)
1063 			break;
1064 
1065 		begin_token = s;
1066 		end_token = end_s;
1067 		s1 = get_next_token(&begin_token, &end_token, &t);
1068 		if (s1 == NULL)
1069 			break;
1070 		if (!(to_ldap && (t == comma_token || t == no_token))) {
1071 			s = get_rhs(s, end_s, &next->rhs,
1072 				to_ldap ? mit_nisplus : mit_ldap);
1073 			if (s == NULL)
1074 				break;
1075 		}
1076 
1077 		if (next->lhs.numElements > 1 &&
1078 		    (next->rhs.numElements != 1 ||
1079 		    next->rhs.element[0].type != me_split)) {
1080 			p_error = parse_lhs_rhs_type_mismatch;
1081 			break;
1082 		}
1083 		if (rule == NULL) {
1084 			rule = (__nis_mapping_rule_t **)
1085 				malloc(sizeof (__nis_mapping_rule_t *));
1086 			if (rule == NULL)
1087 				break;
1088 		} else {
1089 			rule = (__nis_mapping_rule_t **)s_realloc(rule,
1090 				(nRules + 1) *
1091 				sizeof (__nis_mapping_rule_t *));
1092 			if (rule == NULL)
1093 				break;
1094 		}
1095 
1096 		rule[nRules++] = next;
1097 		next = NULL;
1098 
1099 		begin_token = s;
1100 		end_token = end_s;
1101 		s = get_next_token(&begin_token, &end_token, &t);
1102 		if (s == NULL)
1103 			break;
1104 		if (t == comma_token)
1105 			continue;
1106 		if (t != no_token) {
1107 			p_error = parse_unexpected_data_end_rule;
1108 			break;
1109 		}
1110 		if (to_ldap) {
1111 			tbl->numRulesToLDAP = nRules;
1112 			tbl->ruleToLDAP = rule;
1113 		} else {
1114 			tbl->numRulesFromLDAP = nRules;
1115 			tbl->ruleFromLDAP = rule;
1116 		}
1117 		return;
1118 	}
1119 
1120 	if (rule) {
1121 		for (i = 0; i < nRules; i++)
1122 			free_mapping_rule(rule[i]);
1123 		free(rule);
1124 	}
1125 	if (next)
1126 		free_mapping_rule(next);
1127 }
1128 
1129 /*
1130  * FUNCTION:	get_lhs
1131  *
1132  *	Parse left hand side of mapping rule attribute
1133  *
1134  * RETURN VALUE:	NULL if error
1135  *			position of beginning rhs
1136  *
1137  * INPUT:		the attribute value and mapping rule type
1138  */
1139 
1140 static const char *
1141 get_lhs(const char			*s,
1142 	const char			*end_s,
1143 	__nis_mapping_rlhs_t		*lhs,
1144 	__nis_mapping_item_type_t	item_type)
1145 {
1146 	token_type		t;
1147 	const char		*begin_token;
1148 	const char		*end_token;
1149 	const char		*sav_s;
1150 	__nis_mapping_element_t	*e		= NULL;
1151 
1152 	/*
1153 	 *	lhs can be expressed as:
1154 	 *		item
1155 	 *		(item)
1156 	 *		(item list)
1157 	 *		(fmt, item list)
1158 	 *
1159 	 * lhs = lval | namespeclist
1160 	 * lval = "(" formatspec "," namespec *("," namespec) ")"
1161 	 * namespeclist = namespec | "(" namespec *("," namespec) ")"
1162 	 */
1163 
1164 	for (; p_error == no_parse_error; ) {
1165 		begin_token = s;
1166 		end_token = end_s;
1167 		s = get_next_token(&begin_token, &end_token, &t);
1168 		if (s == NULL)
1169 			break;
1170 		if (t == no_token) {
1171 			p_error = parse_unexpected_data_end_rule;
1172 			break;
1173 		}
1174 
1175 		e = (__nis_mapping_element_t *)
1176 			s_calloc(1, sizeof (__nis_mapping_element_t));
1177 		if (e == NULL)
1178 			break;
1179 
1180 		if (t == open_paren_token) {
1181 			free(e);
1182 			e = NULL;
1183 
1184 			begin_token = s;
1185 			end_token = end_s;
1186 			sav_s = s;
1187 			s = get_next_token(&begin_token, &end_token, &t);
1188 			if (s == NULL)
1189 				break;
1190 
1191 			if (t == quoted_string_token) {
1192 				s = get_lhs_match(sav_s, end_s, lhs, item_type);
1193 				if (s == NULL)
1194 					break;
1195 			} else if (t == string_token) {
1196 				s = get_lhs_paren_item(sav_s, end_s, lhs,
1197 					item_type);
1198 				if (s == NULL)
1199 					break;
1200 			} else {
1201 				p_error = parse_bad_lhs_format_error;
1202 				break;
1203 			}
1204 		} else if (t == string_token) {
1205 			s = get_mapping_item(begin_token, end_s,
1206 				&e->element.item, item_type);
1207 			if (s == NULL)
1208 				break;
1209 			e->type = me_item;
1210 			if (!add_element(e, lhs))
1211 				break;
1212 			e = NULL;
1213 		} else {
1214 			p_error = parse_bad_lhs_format_error;
1215 			break;
1216 		}
1217 
1218 		s = skip_token(s, end_s, equal_token);
1219 		if (s == NULL)
1220 			break;
1221 		if (p_error == no_parse_error)
1222 			return (s);
1223 	}
1224 	if (e != NULL)
1225 		free_mapping_element(e);
1226 
1227 	return (NULL);
1228 }
1229 
1230 /*
1231  * FUNCTION:	get_lhs_match
1232  *
1233  *	Parse left hand side of mapping rule attribute in case of
1234  *	matching rule
1235  *
1236  * RETURN VALUE:	NULL if error
1237  *			position of beginning rhs
1238  *
1239  * INPUT:		the attribute value and mapping rule type
1240  */
1241 
1242 static const char *
1243 get_lhs_match(
1244 	const char			*s,
1245 	const char			*end_s,
1246 	__nis_mapping_rlhs_t		*lhs,
1247 	__nis_mapping_item_type_t	item_type)
1248 {
1249 	token_type			t;
1250 	const char			*begin_token;
1251 	const char			*end_token;
1252 	int				n		= 0;
1253 	int				nElements	= 0;
1254 	char				*fmt_string	= NULL;
1255 	__nis_mapping_format_t		*base		= NULL;
1256 	__nis_mapping_item_t		*item		= NULL;
1257 	__nis_mapping_item_t		*itm;
1258 	__nis_mapping_element_t		*e;
1259 
1260 	/*
1261 	 *  lval = "(" formatspec "," namespec *("," namespec) ")"
1262 	 */
1263 
1264 	for (; p_error == no_parse_error; ) {
1265 		begin_token = s;
1266 		end_token = end_s;
1267 		s = get_next_token(&begin_token, &end_token, &t);
1268 		if (s == NULL || t != quoted_string_token) {
1269 			p_error = parse_internal_error;
1270 			break;
1271 		}
1272 
1273 
1274 		fmt_string = s_strndup(begin_token, end_token - begin_token);
1275 		if (fmt_string == NULL)
1276 			break;
1277 
1278 		if (!get_mapping_format(fmt_string, &base, &n, NULL, FALSE))
1279 			break;
1280 
1281 		for (n = 0; base[n].type != mmt_end; n++) {
1282 			if (base[n].type != mmt_item &&
1283 			    base[n].type != mmt_berstring) {
1284 				if (base[n].type == mmt_berstring_null)
1285 					base[n].type = mmt_berstring;
1286 				continue;
1287 			}
1288 			s = skip_token(s, end_s, comma_token);
1289 			if (s == NULL) {
1290 				p_error = parse_not_enough_extract_items;
1291 				break;
1292 			}
1293 			begin_token = s;
1294 			end_token = end_s;
1295 			s = get_next_token(&begin_token, &end_token, &t);
1296 			if (s == NULL)
1297 				break;
1298 			if (t != string_token) {
1299 				p_error = parse_item_expected_error;
1300 				break;
1301 			}
1302 			itm = (__nis_mapping_item_t *)
1303 				s_realloc(item, (nElements + 1) *
1304 				sizeof (__nis_mapping_item_t));
1305 			if (itm == NULL)
1306 				break;
1307 			item = itm;
1308 
1309 			s = get_mapping_item(begin_token, end_s,
1310 				&item[nElements], item_type);
1311 			if (s == NULL)
1312 				break;
1313 			nElements++;
1314 		}
1315 		if (p_error != no_parse_error)
1316 			break;
1317 
1318 		s = skip_token(s, end_s, close_paren_token);
1319 		if (s == NULL)
1320 			break;
1321 		free(fmt_string);
1322 		fmt_string = NULL;
1323 
1324 		if (nElements == 0) {
1325 			p_error = parse_no_match_item;
1326 			break;
1327 		}
1328 		e = (__nis_mapping_element_t *)s_calloc(1,
1329 			sizeof (__nis_mapping_element_t));
1330 		if (e == NULL)
1331 			break;
1332 		e->type = me_match;
1333 		e->element.match.numItems = nElements;
1334 		e->element.match.item = item;
1335 		e->element.match.fmt = base;
1336 		lhs->numElements = 1;
1337 		lhs->element = e;
1338 
1339 		if (p_error == no_parse_error)
1340 			return (s);
1341 	}
1342 	if (item == NULL) {
1343 		for (n = 0; n < nElements; n++)
1344 			free_mapping_item(&item[n]);
1345 		free(item);
1346 	}
1347 	if (fmt_string != NULL)
1348 		free(fmt_string);
1349 	if (base != NULL)
1350 		free_mapping_format(base);
1351 
1352 	return (NULL);
1353 }
1354 
1355 /*
1356  * FUNCTION:	get_lhs_paren_item
1357  *
1358  *	Parse left hand side of mapping rule attribute in case of
1359  *	(item1, ..., item-n)
1360  *
1361  * RETURN VALUE:	NULL if error
1362  *			position of beginning rhs
1363  *
1364  * INPUT:		the attribute value and mapping rule type
1365  */
1366 
1367 static const char *
1368 get_lhs_paren_item(
1369 	const char			*s,
1370 	const char			*end_s,
1371 	__nis_mapping_rlhs_t		*lhs,
1372 	__nis_mapping_item_type_t	item_type)
1373 {
1374 	token_type		t;
1375 	const char		*begin_token;
1376 	const char		*end_token;
1377 	__nis_mapping_element_t	*e		= NULL;
1378 	int			n		= 0;
1379 	int			i;
1380 
1381 	/*
1382 	 * "(" namespec *("," namespec) ")"
1383 	 */
1384 
1385 	for (;;) {
1386 		e = (__nis_mapping_element_t *)s_realloc(e, (n + 1) *
1387 			sizeof (__nis_mapping_element_t));
1388 		if (e == NULL)
1389 			break;
1390 
1391 		s = get_mapping_item(s, end_s, &e[n].element.item,
1392 			item_type);
1393 		if (s == NULL)
1394 			break;
1395 		e[n].type = me_item;
1396 		n++;
1397 
1398 		begin_token = s;
1399 		end_token = end_s;
1400 		s = get_next_token(&begin_token, &end_token, &t);
1401 		if (s != NULL && t == close_paren_token) {
1402 			lhs->numElements = n;
1403 			if (n == 1)
1404 				e[0].element.item.repeat = TRUE;
1405 			lhs->element = e;
1406 			return (s);
1407 		}
1408 		if (s == NULL || t != comma_token) {
1409 			p_error = parse_comma_expected_error;
1410 			break;
1411 		}
1412 	}
1413 	for (i = 0; i < n; i++)
1414 		free_mapping_element(&e[i]);
1415 	if (e != NULL)
1416 		free(e);
1417 	return (NULL);
1418 }
1419 
1420 /*
1421  * FUNCTION:	get_rhs
1422  *
1423  *	Parse right hand side of mapping rule attribute
1424  *
1425  * RETURN VALUE:	NULL if error
1426  *			position of beginning next mapping rule
1427  *
1428  * INPUT:		the attribute value and mapping rule type
1429  */
1430 
1431 static const char *
1432 get_rhs(
1433 	const char			*s,
1434 	const char			*end_s,
1435 	__nis_mapping_rlhs_t		*rhs,
1436 	__nis_mapping_item_type_t	item_type)
1437 {
1438 	/*
1439 	 * This handles the following cases:
1440 	 *	name				me_item
1441 	 *	(name)				me_item
1442 	 *	(fmt, name-list)		me_print
1443 	 *	(item, fmt)			me_extract
1444 	 */
1445 
1446 	token_type		t;
1447 	const char		*begin_token;
1448 	const char		*end_token;
1449 	char			*str		= NULL;
1450 	__nis_mapping_format_t	*fmt		= NULL;
1451 	__nis_mapping_element_t	*e		= NULL;
1452 	__nis_mapping_item_t	item;
1453 	int			n;
1454 
1455 	(void) memset(&item, 0, sizeof (item));
1456 
1457 	for (; p_error == no_parse_error; ) {
1458 		begin_token = s;
1459 		end_token = end_s;
1460 		s = get_next_token(&begin_token, &end_token, &t);
1461 		if (s == NULL)
1462 			break;
1463 
1464 		e = (__nis_mapping_element_t *)
1465 			s_calloc(1, sizeof (__nis_mapping_element_t));
1466 		if (e == NULL)
1467 			break;
1468 
1469 		if (t == string_token) {
1470 			s = get_mapping_item(begin_token, end_s,
1471 				&e->element.item, item_type);
1472 		} else if (t == open_paren_token) {
1473 			begin_token = s;
1474 			end_token = end_s;
1475 			s = get_next_token(&begin_token, &end_token, &t);
1476 			if (s == NULL)
1477 				break;
1478 			if (t == string_token) {
1479 				/* (item, fmt) - me_extract */
1480 				/* (item, "c") - me_split */
1481 				s = get_mapping_item(begin_token, end_s,
1482 					&item, item_type);
1483 				if (s == NULL)
1484 					break;
1485 				begin_token = s;
1486 				end_token = end_s;
1487 				s = get_next_token(&begin_token, &end_token,
1488 					&t);
1489 				if (s == NULL)
1490 					break;
1491 				else if (t == close_paren_token) {
1492 					item.repeat = TRUE;
1493 					e->element.item = item;
1494 					e->type = me_item;
1495 					rhs->numElements = 1;
1496 					rhs->element = e;
1497 					return (s);
1498 				} else if (t != comma_token) {
1499 					p_error = parse_comma_expected_error;
1500 					break;
1501 				}
1502 
1503 				begin_token = s;
1504 				end_token = end_s;
1505 				s = get_next_token(&begin_token, &end_token,
1506 					&t);
1507 				if (s == NULL || t != quoted_string_token) {
1508 				    p_error =
1509 					parse_format_string_expected_error;
1510 				    break;
1511 				}
1512 
1513 				if (end_token == begin_token + 1 ||
1514 				    *begin_token == ESCAPE_CHAR &&
1515 				    end_token == begin_token + 2) {
1516 					e->type = me_split;
1517 					e->element.split.item = item;
1518 					e->element.split.delim = *begin_token;
1519 				} else {
1520 					str = s_strndup(begin_token,
1521 						end_token - begin_token);
1522 					if (str == NULL)
1523 						break;
1524 					if (!get_mapping_format(str, &fmt,
1525 					    NULL, &n, FALSE))
1526 						break;
1527 					free(str);
1528 					str = NULL;
1529 					if (n != 1) {
1530 					    p_error =
1531 						parse_bad_extract_format_spec;
1532 					    break;
1533 					}
1534 					e->type = me_extract;
1535 					e->element.extract.item = item;
1536 					e->element.extract.fmt = fmt;
1537 				}
1538 				s = skip_token(s, end_s, close_paren_token);
1539 			} else if (t == quoted_string_token) {
1540 				/* (fmt, name-list) - me_print */
1541 				str = s_strndup(begin_token,
1542 					end_token - begin_token);
1543 				if (str == NULL)
1544 					break;
1545 
1546 				s = get_print_mapping_element(s, end_s,
1547 					str, e, item_type);
1548 				free(str);
1549 				str = NULL;
1550 			} else {
1551 				p_error = parse_start_rhs_unrecognized;
1552 				break;
1553 			}
1554 		} else {
1555 			p_error = parse_start_rhs_unrecognized;
1556 			break;
1557 		}
1558 		if (s == NULL)
1559 			break;
1560 		rhs->numElements = 1;
1561 		rhs->element = e;
1562 		if (p_error == no_parse_error)
1563 			return (s);
1564 	}
1565 	if (str)
1566 		free(str);
1567 	if (fmt != NULL)
1568 		free_mapping_format(fmt);
1569 	if (e != NULL)
1570 		free_mapping_element(e);
1571 	free_mapping_item(&item);
1572 
1573 	return (NULL);
1574 }
1575 
1576 /*
1577  * FUNCTION:	get_print_mapping_element
1578  *
1579  *	Parse a print mapping rule attribute in case of the form
1580  *	(fmt, name-list)
1581  *
1582  * RETURN VALUE:	NULL if error
1583  *			position of beginning next mapping rule
1584  *
1585  * INPUT:		the attribute value and mapping rule type
1586  */
1587 
1588 static const char *
1589 get_print_mapping_element(
1590 	const char			*s,
1591 	const char			*end_s,
1592 	char				*fmt_string,
1593 	__nis_mapping_element_t		*e,
1594 	__nis_mapping_item_type_t	item_type)
1595 {
1596 	token_type			t;
1597 	const char			*begin_token;
1598 	const char			*end_token;
1599 	char				elide;
1600 	bool_t				doElide;
1601 	__nis_mapping_format_t		*base		= NULL;
1602 	__nis_mapping_sub_element_t	*subElement	= NULL;
1603 	int				n		= 0;
1604 	int				nSub		= 0;
1605 	int				numSubElements;
1606 
1607 	for (; p_error == no_parse_error; ) {
1608 		if (!get_mapping_format(fmt_string, &base, &n,
1609 		    &numSubElements, TRUE))
1610 			break;
1611 		subElement = (__nis_mapping_sub_element_t *)
1612 			s_calloc(numSubElements,
1613 			sizeof (__nis_mapping_sub_element_t));
1614 		if (subElement == NULL)
1615 			break;
1616 		for (n = 0; base[n].type != mmt_end; n++) {
1617 			if (base[n].type != mmt_item &&
1618 				base[n].type != mmt_berstring) {
1619 			    if (base[n].type == mmt_berstring_null)
1620 				base[n].type = mmt_berstring;
1621 			    continue;
1622 			}
1623 			if (nSub < numSubElements) {
1624 				s = skip_token(s, end_s, comma_token);
1625 				if (s == NULL) {
1626 					p_error = parse_bad_print_format;
1627 					break;
1628 				}
1629 			}
1630 
1631 			/* namelist may have parens around it */
1632 			s = get_subElement(s, end_s, &subElement[nSub],
1633 				item_type);
1634 			if (s == NULL)
1635 				break;
1636 			nSub++;
1637 		}
1638 		if (p_error != no_parse_error)
1639 			break;
1640 
1641 		begin_token = s;
1642 		end_token = end_s;
1643 		s = get_next_token(&begin_token, &end_token, &t);
1644 		if (s == NULL || t == no_token) {
1645 			p_error = parse_unexpected_data_end_rule;
1646 			break;
1647 		} else if (t == close_paren_token) {
1648 			doElide = FALSE;
1649 			elide = '\0';
1650 		} else if (t == comma_token) {
1651 			begin_token = s;
1652 			end_token = end_s;
1653 			s = get_next_token(&begin_token, &end_token, &t);
1654 			if (s != NULL && t == quoted_string_token &&
1655 				(end_token == begin_token + 1 ||
1656 				    *begin_token == ESCAPE_CHAR &&
1657 				    end_token == begin_token + 2)) {
1658 				if (numSubElements != 1 ||
1659 				    subElement->type == me_extract ||
1660 				    subElement->type == me_split) {
1661 					p_error = parse_cannot_elide;
1662 					break;
1663 				}
1664 				if (subElement->type == me_item &&
1665 				    !subElement->element.item.repeat) {
1666 					p_error = parse_cannot_elide;
1667 					break;
1668 				}
1669 				elide = *begin_token;
1670 				doElide = TRUE;
1671 
1672 			} else {
1673 				p_error = parse_bad_elide_char;
1674 				break;
1675 			}
1676 			s = skip_token(s, end_s, close_paren_token);
1677 			if (s == NULL)
1678 				break;
1679 		}
1680 
1681 		e->type = me_print;
1682 		e->element.print.fmt = base;
1683 		e->element.print.numSubElements = numSubElements;
1684 		e->element.print.subElement = subElement;
1685 		e->element.print.elide = elide;
1686 		e->element.print.doElide = doElide;
1687 
1688 		if (p_error == no_parse_error)
1689 			return (s);
1690 	}
1691 	if (base)
1692 		free_mapping_format(base);
1693 	if (subElement != NULL) {
1694 		for (n = 0; n < numSubElements; n++)
1695 			free_mapping_sub_element(&subElement[n]);
1696 		free(subElement);
1697 	}
1698 
1699 	return (NULL);
1700 }
1701 
1702 /*
1703  * FUNCTION:	get_mapping_item
1704  *
1705  *	Parse attribute string to get mapping item
1706  *
1707  * RETURN VALUE:	NULL if error
1708  *			position of beginning next token after item
1709  *
1710  * INPUT:		the attribute value and mapping rule type
1711  */
1712 
1713 static const char *
1714 get_mapping_item(
1715 	const char			*s,
1716 	const char			*end_s,
1717 	__nis_mapping_item_t		*item,
1718 	__nis_mapping_item_type_t	type)
1719 {
1720 	token_type			t;
1721 	const char			*begin_token;
1722 	const char			*end_token;
1723 	char				*name		= NULL;
1724 	char				*index_string;
1725 	const char			*s_sav;
1726 	int				len;
1727 
1728 	(void) memset(item, 0, sizeof (*item));
1729 
1730 	/*
1731 	 * A namepec is defined as follows:
1732 	 * namespec	= ["ldap:"] attrspec [searchTriple] |
1733 	 *		  ["nis+:"] colspec  [objectspec]
1734 	 *
1735 	 * The form of the item is assumed to be as follows:
1736 	 * ["ldap:"] attrspec [searchTriple]
1737 	 * attrspec = attribute | "(" attribute ")"
1738 	 * searchTriple	= ":" [baseDN] ["?" [scope] ["?" [filter]]]
1739 	 * baseDN = Base DN for search
1740 	 * scope = "base" | "one" | "sub"
1741 	 * filter = LDAP search filter
1742 	 *
1743 	 * The form of the objectspec is as follows:
1744 	 * ["nis+:"] colspec  [objectspec]
1745 	 * objectspec	= objectname | "[" indexlist "]" tablename
1746 	 * objectname	= The name of a NIS+ object
1747 	 * tablename	= The name of a NIS+ table
1748 	 * indexlist	= colspec ["," colspec]
1749 	 * colspec	= colname "=" colvalue
1750 	 * colname	= The name of a column in the table
1751 	 * colvalue	= colvaluestring | \" colvaluestring \"
1752 	 */
1753 
1754 	for (; p_error == no_parse_error; ) {
1755 		while (s < end_s && is_whitespace(*s))
1756 			s++;
1757 		len = end_s - s;
1758 		if (yp2ldap) {
1759 			if ((begin_token = skip_string("ldap:", s,
1760 				len)) != NULL) {
1761 				item->type = mit_ldap;
1762 			} else if ((begin_token = skip_string("yp:", s,
1763 				len)) != NULL) {
1764 				item->type = mit_nisplus;
1765 			} else {
1766 				item->type = type;
1767 				begin_token = s;
1768 			}
1769 		} else {
1770 			if ((begin_token = skip_string("ldap:", s,
1771 				len)) != NULL) {
1772 			item->type = mit_ldap;
1773 			} else if ((begin_token = skip_string("nis+:", s,
1774 				len)) != NULL) {
1775 				item->type = mit_nisplus;
1776 			} else if ((begin_token = skip_string("nisplus:", s,
1777 				len)) != NULL) {
1778 				item->type = mit_nisplus;
1779 			} else {
1780 				item->type = type;
1781 				begin_token = s;
1782 			}
1783 		}
1784 
1785 		end_token = end_s;
1786 		s = get_next_token(&begin_token, &end_token, &t);
1787 		if (s == NULL || t != string_token) {
1788 			p_error = parse_bad_item_format;
1789 			break;
1790 		}
1791 
1792 		item->name = s_strndup_esc(begin_token,
1793 			end_token - begin_token);
1794 		if (item->name == NULL)
1795 			break;
1796 		if (item->type == mit_ldap) {
1797 			item->searchSpec.triple.scope = LDAP_SCOPE_UNKNOWN;
1798 			begin_token = s;
1799 			end_token = end_s;
1800 			s_sav = s;
1801 			s = get_next_token(&begin_token, &end_token, &t);
1802 			if (s != NULL && t == colon_token) {
1803 				s = get_search_triple(s, end_s,
1804 					&item->searchSpec.triple);
1805 				if (s == NULL)
1806 					break;
1807 			} else
1808 				s = s_sav;
1809 		} else if (item->type == mit_nisplus) {
1810 			while (s < end_s && is_whitespace(*s))
1811 				s++;
1812 
1813 			if (s < end_s && *s == OPEN_BRACKET) {
1814 				index_string = getIndex(&s, end_s);
1815 				if (index_string == NULL)
1816 					break;
1817 				(void) parse_index(index_string,
1818 					index_string + strlen(index_string),
1819 					&item->searchSpec.obj.index);
1820 				free(index_string);
1821 				if (p_error != no_parse_error)
1822 					break;
1823 			}
1824 			s_sav = s;
1825 			begin_token = s;
1826 			end_token = end_s;
1827 			s = get_next_token(&begin_token, &end_token, &t);
1828 			if (s != NULL && t == string_token) {
1829 				name = s_strndup_esc(begin_token,
1830 					end_token - begin_token);
1831 				if (name == NULL)
1832 					break;
1833 				item->searchSpec.obj.name = name;
1834 			} else
1835 				s = s_sav;
1836 		}
1837 		if (p_error == no_parse_error)
1838 			return (s);
1839 	}
1840 	free_mapping_item(item);
1841 	(void) memset(item, 0, sizeof (*item));
1842 	if (name == NULL)
1843 		free(name);
1844 	return (NULL);
1845 }
1846 
1847 static const char *
1848 get_print_sub_element(const char		*s,
1849 		const char			*end_s,
1850 		__nis_mapping_item_type_t	type,
1851 		__nis_mapping_sub_element_t	*sub)
1852 {
1853 
1854 	int			k;
1855 	int			n;
1856 	const char		*begin_token;
1857 	const char		*end_token;
1858 	token_type		t;
1859 	__nis_mapping_format_t	*base;
1860 	__nis_mapping_item_t	*print_item;
1861 
1862 	k = 0;
1863 	base = sub->element.print.fmt;
1864 	print_item = sub->element.print.item;
1865 	sub->element.print.doElide = FALSE;
1866 	sub->element.print.elide = '\0';
1867 
1868 	for (n = 0; base[n].type != mmt_end; n++) {
1869 		if (base[n].type != mmt_item && base[n].type != mmt_berstring) {
1870 			if (base[n].type == mmt_berstring_null)
1871 					base[n].type = mmt_berstring;
1872 			continue;
1873 		}
1874 		s = skip_token(s, end_s, comma_token);
1875 		if (s == NULL) {
1876 			p_error = parse_bad_print_format;
1877 			break;
1878 		}
1879 
1880 		begin_token = s;
1881 		end_token = end_s;
1882 		s = get_next_token(&begin_token, &end_token, &t);
1883 		if (s == NULL)
1884 			break;
1885 		/*
1886 		 * Determine if of the form
1887 		 * ("fmt", (item), "delim") or
1888 		 * ("fmt", item1, item2, ..., item n)
1889 		 */
1890 		if (t == open_paren_token) {
1891 			if (sub->element.print.numItems != 1) {
1892 				p_error = parse_invalid_print_arg;
1893 				break;
1894 			}
1895 			s = get_mapping_item(s, end_s, &print_item[k++], type);
1896 			s = skip_token(s, end_s, close_paren_token);
1897 			s = skip_token(s, end_s, comma_token);
1898 			if (s == NULL) {
1899 				p_error = parse_bad_print_format;
1900 				break;
1901 			}
1902 			begin_token = s;
1903 			end_token = end_s;
1904 			s = get_next_token(&begin_token, &end_token, &t);
1905 			if (s == NULL)
1906 				break;
1907 			if (t != quoted_string_token ||
1908 				    begin_token + 1 != end_token) {
1909 				p_error = parse_bad_elide_char;
1910 				break;
1911 			}
1912 			sub->element.print.elide = *begin_token;
1913 			sub->element.print.doElide = TRUE;
1914 			print_item[0].repeat = TRUE;
1915 			break;
1916 		}
1917 		s = get_mapping_item(begin_token, end_s,
1918 			&print_item[k++], type);
1919 		if (s == NULL)
1920 			break;
1921 
1922 		if (p_error != no_parse_error)
1923 			break;
1924 	}
1925 
1926 	return (p_error == no_parse_error ? s : NULL);
1927 }
1928 
1929 /*
1930  * FUNCTION:	get_subElement
1931  *
1932  *	Parse attribute string to get sub element item
1933  *
1934  * RETURN VALUE:	NULL if error
1935  *			position of beginning next token after item
1936  *
1937  * INPUT:		the attribute value and mapping rule type
1938  */
1939 
1940 static const char *
1941 get_subElement(
1942 	const char			*s,
1943 	const char			*end_s,
1944 	__nis_mapping_sub_element_t	*subelement,
1945 	__nis_mapping_item_type_t	type)
1946 {
1947 	token_type			t;
1948 	const char			*begin_token;
1949 	const char			*end_token;
1950 	char				*fmt_string;
1951 	__nis_mapping_item_t		item;
1952 	__nis_mapping_element_type_t	e_type;
1953 	__nis_mapping_item_t		*print_item	= NULL;
1954 	__nis_mapping_format_t		*base		= NULL;
1955 	int				n		= 0;
1956 	int				numItems	= 0;
1957 	unsigned char			delim;
1958 	__nis_mapping_sub_element_t	sub;
1959 
1960 /*
1961  *	What is the form of we are expecting here
1962  *	item					me_item
1963  *	(item)					me_item
1964  *	("fmt", item1, item2, ..., item n)	me_print
1965  *	("fmt", (item), "elide")		me_print
1966  *	(name, "delim")				me_split
1967  *	(item, "fmt")				me_extract
1968  */
1969 	(void) memset(&item, 0, sizeof (item));
1970 
1971 	for (; p_error == no_parse_error; ) {
1972 		begin_token = s;
1973 		end_token = end_s;
1974 		s = get_next_token(&begin_token, &end_token, &t);
1975 		if (s == NULL)
1976 			break;
1977 		if (t == string_token) {	/* me_item */
1978 			s = get_mapping_item(begin_token, end_s,
1979 				&subelement->element.item, type);
1980 			if (s == NULL)
1981 				break;
1982 			subelement->type = me_item;
1983 			return (s);
1984 		} else if (t != open_paren_token) {
1985 			p_error = parse_item_expected_error;
1986 			break;
1987 		}
1988 
1989 		begin_token = s;
1990 		end_token = end_s;
1991 		s = get_next_token(&begin_token, &end_token, &t);
1992 		if (s == NULL)
1993 			break;
1994 
1995 		if (t != string_token && t != quoted_string_token) {
1996 			p_error = parse_item_expected_error;
1997 			break;
1998 		}
1999 		e_type = me_print;
2000 		if (t == string_token) {
2001 			/* me_item, me_extract or me_split */
2002 			s = get_mapping_item(begin_token, end_s, &item, type);
2003 			if (s == NULL)
2004 				break;
2005 
2006 			begin_token = s;
2007 			end_token = end_s;
2008 			s = get_next_token(&begin_token, &end_token, &t);
2009 			if (s == NULL) {
2010 				p_error = parse_unexpected_data_end_rule;
2011 				break;
2012 			} else if (t == close_paren_token) {
2013 				subelement->type = me_item;
2014 				item.repeat = TRUE;
2015 				subelement->element.item = item;
2016 				if (yp2ldap) {
2017 					while (s < end_s && is_whitespace(*s))
2018 						s++;
2019 					if (s == end_s) {
2020 						p_error =
2021 						parse_unexpected_data_end_rule;
2022 						break;
2023 					}
2024 					if (*s == DASH_CHAR && s < end_s) {
2025 						s++;
2026 						while (s < end_s &&
2027 							is_whitespace(*s))
2028 							s++;
2029 						begin_token = s;
2030 						end_token = end_s;
2031 
2032 						subelement->element.item.exItem
2033 							=
2034 							(__nis_mapping_item_t *)
2035 					s_malloc(sizeof (__nis_mapping_item_t));
2036 						if (!subelement->
2037 						element.item.exItem)
2038 							break;
2039 						s = get_mapping_item(s, end_s,
2040 							subelement->
2041 							element.item.exItem,
2042 							type);
2043 						if (s == NULL) {
2044 							p_error =
2045 							parse_internal_error;
2046 							free_mapping_item(
2047 							subelement->
2048 							element.item.exItem);
2049 							subelement->
2050 							element.item.exItem =
2051 								NULL;
2052 							break;
2053 						}
2054 					}
2055 				}
2056 				return (s);
2057 			} else if (t != comma_token) {
2058 				p_error = parse_comma_expected_error;
2059 				break;
2060 			}
2061 
2062 			begin_token = s;
2063 			end_token = end_s;
2064 			s = get_next_token(&begin_token, &end_token, &t);
2065 			if (s == NULL || t != quoted_string_token) {
2066 				p_error = parse_format_string_expected_error;
2067 				break;
2068 			}
2069 			if (end_token == begin_token + 1 ||
2070 			    *begin_token == ESCAPE_CHAR &&
2071 			    end_token == begin_token + 2) {
2072 					/* me_split */
2073 				delim = (unsigned char)end_token[-1];
2074 				s = skip_token(s, end_s, close_paren_token);
2075 				if (s == NULL)
2076 					break;
2077 				subelement->element.split.item = item;
2078 				subelement->element.split.delim = delim;
2079 				subelement->type = me_split;
2080 				return (s);
2081 			}
2082 			e_type = me_extract;
2083 		}
2084 		fmt_string = s_strndup(begin_token, end_token - begin_token);
2085 		if (fmt_string == NULL)
2086 			break;
2087 		if (!get_mapping_format(fmt_string, &base, &n, &numItems,
2088 		    e_type == me_print)) {
2089 			free(fmt_string);
2090 			break;
2091 		}
2092 		free(fmt_string);
2093 
2094 		if (numItems != 1 && e_type == me_extract) {
2095 			p_error = numItems == 0 ?
2096 				parse_not_enough_extract_items :
2097 				parse_too_many_extract_items;
2098 			break;
2099 		} else if (numItems > 0 && e_type == me_print) {
2100 			print_item = (__nis_mapping_item_t *)s_calloc(numItems,
2101 				sizeof (__nis_mapping_item_t));
2102 			if (print_item == NULL)
2103 				break;
2104 		}
2105 
2106 		if (e_type == me_print) {
2107 			sub.element.print.numItems = numItems;
2108 			sub.element.print.fmt = base;
2109 			sub.element.print.item = print_item;
2110 			s = get_print_sub_element(s, end_s, type, &sub);
2111 			if (s == NULL)
2112 				break;
2113 		}
2114 		s = skip_token(s, end_s, close_paren_token);
2115 		if (s == NULL)
2116 			break;
2117 
2118 		subelement->type = e_type;
2119 		if (e_type == me_extract) {
2120 			subelement->element.extract.fmt = base;
2121 			subelement->element.extract.item = item;
2122 		} else {
2123 			subelement->type = me_print;
2124 			subelement->element.print.fmt = base;
2125 			subelement->element.print.numItems = numItems;
2126 			subelement->element.print.item = print_item;
2127 			subelement->element.print.doElide =
2128 				sub.element.print.doElide;
2129 			subelement->element.print.elide =
2130 				sub.element.print.elide;
2131 		}
2132 		if (p_error == no_parse_error)
2133 			return (s);
2134 	}
2135 	free_mapping_item(&item);
2136 	if (base != NULL)
2137 		free_mapping_format(base);
2138 	if (print_item) {
2139 		for (n = 0; n < numItems; n++)
2140 			free_mapping_item(&print_item[n]);
2141 		free(print_item);
2142 	}
2143 
2144 	return (NULL);
2145 }
2146 
2147 /*
2148  * FUNCTION:	skip_get_dn
2149  *
2150  *	Get first token after dn
2151  *
2152  * RETURN VALUE:	NULL if error (not valid dn)
2153  *			position of beginning next token after dn
2154  *
2155  * INPUT:		the attribute value
2156  */
2157 
2158 const char *
2159 skip_get_dn(const char *dn, const char *end)
2160 {
2161 	size_t		len		= 0;
2162 	bool_t		in_quote	= FALSE;
2163 	bool_t		goteq		= FALSE;
2164 	bool_t		gotch		= FALSE;
2165 	bool_t		done		= FALSE;
2166 	bool_t		last_comma	= FALSE;
2167 	const char	*last_dn	= dn;
2168 
2169 	while (!done) {
2170 		dn += len;
2171 		if (last_comma) {
2172 			last_dn = dn;
2173 			last_comma = FALSE;
2174 		}
2175 		if (dn >= end)
2176 			break;
2177 		len = 1;
2178 		switch (*dn) {
2179 			case ESCAPE_CHAR:
2180 				len = 2;
2181 				gotch = TRUE;
2182 				break;
2183 			case DOUBLE_QUOTE_CHAR:
2184 				in_quote = !in_quote;
2185 				break;
2186 			case QUESTION_MARK:
2187 			case CLOSE_PAREN_CHAR:
2188 			case COLON_CHAR:
2189 				done = !in_quote;
2190 				/* FALLTHRU */
2191 			case SEMI_COLON_CHAR:
2192 			case PLUS_SIGN:
2193 			case COMMA_CHAR:
2194 				if (!in_quote) {
2195 					if (!goteq || !gotch)
2196 						return (last_dn);
2197 					goteq = FALSE;
2198 					gotch = FALSE;
2199 					if (*dn != PLUS_SIGN)
2200 						last_dn = dn;
2201 					last_comma = *dn == COMMA_CHAR;
2202 				} else {
2203 					gotch = TRUE;
2204 				}
2205 				break;
2206 			case EQUAL_CHAR:
2207 				if (!in_quote) {
2208 					if (!gotch || goteq)
2209 						return (NULL);
2210 					goteq = TRUE;
2211 					gotch = FALSE;
2212 				} else {
2213 					gotch = TRUE;
2214 				}
2215 				break;
2216 			default:
2217 				if (!is_whitespace(*dn))
2218 					gotch = TRUE;
2219 				break;
2220 		}
2221 	}
2222 
2223 	if (dn == end) {
2224 		if (!in_quote && goteq && gotch)
2225 			last_dn = dn;
2226 	}
2227 
2228 	return (last_dn);
2229 }
2230 
2231 /*
2232  * FUNCTION:	get_ldap_filter_element
2233  *
2234  *	Get an ldap filter element for a given string
2235  *
2236  * RETURN VALUE:	NULL if error
2237  *			__nis_mapping_element_t if success
2238  *
2239  * INPUT:		the string to parse
2240  */
2241 
2242 static __nis_mapping_element_t *
2243 get_ldap_filter_element(
2244 	const char			*s,
2245 	const char			*end_s
2246 )
2247 {
2248 	token_type			t;
2249 	const char			*begin_token;
2250 	const char			*end_token;
2251 	char				*format_str;
2252 	__nis_mapping_element_t		*e		= NULL;
2253 
2254 	begin_token = s;
2255 	end_token = end_s;
2256 	s = get_next_token(&begin_token, &end_token, &t);
2257 	if (s == NULL || t != open_paren_token)
2258 		return (NULL);
2259 
2260 	begin_token = s;
2261 	end_token = end_s;
2262 	s = get_next_token(&begin_token, &end_token, &t);
2263 	if (s == NULL || t != quoted_string_token)
2264 		return (NULL);
2265 
2266 	format_str = s_strndup(begin_token, end_token - begin_token);
2267 	if (format_str == NULL)
2268 		return (NULL);
2269 	e = (__nis_mapping_element_t *)
2270 		s_calloc(1, sizeof (__nis_mapping_element_t));
2271 	if (e != NULL) {
2272 		(void) get_print_mapping_element(s, end_s,
2273 				format_str, e, mit_nisplus);
2274 		if (p_error != no_parse_error) {
2275 			free_mapping_element(e);
2276 			e = NULL;
2277 		}
2278 	}
2279 	free(format_str);
2280 	return (e);
2281 }
2282 
2283 /*
2284  * FUNCTION:	get_search_triple
2285  *
2286  *	Get the search triple or if NULL determine if valid
2287  *
2288  * RETURN VALUE:	NULL if error
2289  *			position of beginning next token after
2290  *			search triple
2291  *
2292  * INPUT:		the attribute value
2293  */
2294 
2295 const char *
2296 get_search_triple(
2297 	const char			*s,
2298 	const char			*end_s,
2299 	__nis_search_triple_t		*triple
2300 )
2301 {
2302 	const char	*begin_token;
2303 	const char	*end_token;
2304 	char		*search_base	= NULL;
2305 	int		scope		= LDAP_SCOPE_ONELEVEL;
2306 	char		*filter		= NULL;
2307 	const char	*s1;
2308 	__nis_mapping_element_t
2309 			*element	= NULL;
2310 
2311 	/*
2312 	 * The form of the searchTriple is assumed to be as follows:
2313 	 * searchTriple	= [baseDN] ["?" [scope] ["?" [filter]]]
2314 	 * baseDN = Base DN for search
2315 	 * scope = "base" | "one" | "sub"
2316 	 * filter = LDAP search filter
2317 	 */
2318 	for (; p_error == no_parse_error; ) {
2319 		while (s < end_s && is_whitespace(*s))
2320 			s++;
2321 		if (s == end_s)
2322 			break;
2323 
2324 		if (!IS_TERMINAL_CHAR(*s)) {
2325 			begin_token = s;
2326 			s = skip_get_dn(begin_token, end_s);
2327 			if (s == NULL) {
2328 				p_error = parse_invalid_dn;
2329 				break;
2330 			}
2331 			if (triple != NULL) {
2332 				search_base = s_strndup(begin_token,
2333 					s - begin_token);
2334 				if (search_base == NULL)
2335 					break;
2336 			}
2337 			while (s < end_s && is_whitespace(*s))
2338 				s++;
2339 			if (s == end_s)
2340 				break;
2341 		}
2342 
2343 		if (!IS_TERMINAL_CHAR(*s)) {
2344 			p_error = parse_bad_ldap_item_format;
2345 			break;
2346 		}
2347 		if (*s != QUESTION_MARK)
2348 			break;
2349 
2350 		s++;
2351 		while (s < end_s && is_whitespace(*s))
2352 			s++;
2353 		if (s == end_s)
2354 			break;
2355 
2356 		/* base, one, or sub, or empty value */
2357 		if (!IS_TERMINAL_CHAR(*s)) {
2358 			if ((s1 = skip_string("base", s, end_s - s)) != NULL) {
2359 				scope = LDAP_SCOPE_BASE;
2360 			} else if ((s1 = skip_string("one", s, end_s - s)) !=
2361 					NULL) {
2362 				scope = LDAP_SCOPE_ONELEVEL;
2363 			} else if ((s1 = skip_string("sub", s, end_s - s)) !=
2364 					NULL) {
2365 				scope = LDAP_SCOPE_SUBTREE;
2366 			} else if (s + 1 < end_s && *s != QUESTION_MARK) {
2367 				p_error = parse_invalid_scope;
2368 				break;
2369 			}
2370 			if (s1 != NULL)
2371 				s = s1;
2372 			while (s < end_s && is_whitespace(*s))
2373 				s++;
2374 		}
2375 
2376 		if (s == end_s)
2377 			break;
2378 		if (*s != QUESTION_MARK)
2379 			break;
2380 		s++;
2381 		while (s < end_s && is_whitespace(*s))
2382 			s++;
2383 		if (s == end_s || IS_TERMINAL_CHAR(*s))
2384 			break;
2385 
2386 		/* LDAP search filter */
2387 		if (*s == OPEN_PAREN_CHAR) {
2388 		    begin_token = s;
2389 		    end_token = end_s;
2390 		    s = get_ldap_filter(&begin_token, &end_token);
2391 		    if (s == NULL)
2392 			break;
2393 		    s = end_token;
2394 		    element = get_ldap_filter_element(begin_token, end_token);
2395 		    if (element != NULL)
2396 			break;
2397 		} else {
2398 		    begin_token = s;
2399 		    end_token = end_s;
2400 		    s = get_ava_list(&begin_token, &end_token, TRUE);
2401 		    if (s == NULL)
2402 			break;
2403 		    s = end_token;
2404 		}
2405 		if (triple != NULL)
2406 			filter = s_strndup(begin_token, s - begin_token);
2407 		if (p_error == no_parse_error)
2408 			break;
2409 	}
2410 	if (p_error == no_parse_error && triple != NULL) {
2411 		triple->base = search_base;
2412 		triple->scope = scope;
2413 		triple->attrs = filter;
2414 		triple->element = element;
2415 		element = NULL;
2416 		filter = NULL;
2417 		search_base = NULL;
2418 	}
2419 
2420 	if (search_base != NULL)
2421 		free(search_base);
2422 	if (filter != NULL)
2423 		free(filter);
2424 	if (element != NULL) {
2425 		free_mapping_element(element);
2426 		free(element);
2427 	}
2428 	return (p_error == no_parse_error ? s : NULL);
2429 }
2430 
2431 /*
2432  * FUNCTION:	get_mapping_format
2433  *
2434  *	Get the __nis_mapping_format_t from the string
2435  *
2436  * RETURN VALUE:	FALSE if error
2437  *			TRUE if __nis_mapping_format_t returned
2438  *
2439  * INPUT:		the format string
2440  */
2441 
2442 static bool_t
2443 get_mapping_format(
2444 	const char		*fmt_string,
2445 	__nis_mapping_format_t	**fmt,
2446 	int			*nfmt,
2447 	int			*numItems,
2448 	bool_t			print_mapping)
2449 {
2450 	const char		*f	= fmt_string;
2451 	const char		*ef;
2452 	__nis_mapping_format_t	*b;
2453 	__nis_mapping_format_t	*base	= NULL;
2454 	int			n	= 0;
2455 	int			nItems	= 0;
2456 
2457 	f = fmt_string;
2458 	ef = f + strlen(f);
2459 	base = (__nis_mapping_format_t *)
2460 	    s_calloc(1, sizeof (__nis_mapping_format_t));
2461 
2462 	if (base == NULL)
2463 		return (FALSE);
2464 	base->type = mmt_begin;
2465 	n++;
2466 
2467 	for (;;) {
2468 		b = (__nis_mapping_format_t *)s_realloc(
2469 		    base, (n + 1) * sizeof (__nis_mapping_format_t));
2470 
2471 		if (b == NULL)
2472 			break;
2473 		base = b;
2474 		base[n].type = mmt_end;
2475 		if (f == ef) {
2476 			if (nfmt)
2477 				*nfmt = n + 1;
2478 			*fmt = base;
2479 			if (numItems)
2480 				*numItems = nItems;
2481 			return (TRUE);
2482 		}
2483 		if (print_mapping)
2484 		    f = get_next_print_format_item(f, ef, &base[n]);
2485 		else
2486 		    f = get_next_extract_format_item(f, ef, &base[n]);
2487 
2488 
2489 		if (f == NULL)
2490 			break;
2491 		if (base[n].type == mmt_item ||
2492 			base[n].type == mmt_berstring)
2493 			nItems++;
2494 		n++;
2495 	}
2496 	if (base != NULL)
2497 		free_mapping_format(base);
2498 	return (FALSE);
2499 }
2500 
2501 /*
2502  * FUNCTION:	getIndex
2503  *
2504  *	Returns a string containing the index
2505  *
2506  * RETURN VALUE:	NULL if error
2507  *			a string containing the index
2508  *
2509  * INPUT:		attribute containing the index
2510  */
2511 
2512 static char *
2513 getIndex(const char **s_cur, const char *s_end)
2514 {
2515 	const char	*s		= *s_cur + 1;
2516 	const char	*s1;
2517 	char		*s_index;
2518 	char		*s_index1;
2519 	char		*s_index_end;
2520 	int		n_brackets	= 1;
2521 	bool_t		in_quotes	= FALSE;
2522 	char		*index		= NULL;
2523 
2524 	while (s < s_end && is_whitespace(*s))
2525 		s++;
2526 	for (s1 = s; s1 < s_end; s1++) {
2527 		if (*s1 == ESCAPE_CHAR)
2528 			s1++;
2529 		else if (*s1 == DOUBLE_QUOTE_CHAR) {
2530 			in_quotes = !in_quotes;
2531 		} else if (in_quotes)
2532 			;
2533 		else if (*s1 == CLOSE_BRACKET) {
2534 			if (--n_brackets == 0)
2535 				break;
2536 		} else if (*s1 == OPEN_BRACKET)
2537 			n_brackets++;
2538 	}
2539 
2540 	if (n_brackets == 0) {
2541 		index = s_strndup(s, s1 - s);
2542 		if (index != NULL) {
2543 			s_index_end = index + (s1 - s);
2544 			s_index1 = index;
2545 			for (s_index = index; s_index < s_index_end;
2546 			    s_index++) {
2547 				if (*s_index == ESCAPE_CHAR) {
2548 					*s_index1++ = *s_index++;
2549 				} else if (*s_index == DOUBLE_QUOTE_CHAR) {
2550 					in_quotes = !in_quotes;
2551 				} else if (!in_quotes &&
2552 				    is_whitespace(*s_index)) {
2553 					continue;
2554 				}
2555 				*s_index1++ = *s_index;
2556 			}
2557 			*s_index1 = *s_index;
2558 
2559 			s = s1 + 1;
2560 
2561 			while (s < s_end && is_whitespace(*s))
2562 				s++;
2563 			*s_cur = s;
2564 		}
2565 	} else
2566 		p_error = parse_mismatched_brackets;
2567 
2568 	return (index);
2569 }
2570 
2571 /*
2572  * FUNCTION:	parse_index
2573  *
2574  *	Parse attribute string to get __nis_index_t
2575  *
2576  * RETURN VALUE:	FALSE if error
2577  *			TRUE if __nis_index_t returned
2578  *
2579  * INPUT:		the attribute value to parse
2580  */
2581 
2582 bool_t
2583 parse_index(const char *s, const char *end_s, __nis_index_t *index)
2584 {
2585 	const char		*begin_token;
2586 	const char		*end_token;
2587 	char			*name_str	= NULL;
2588 	char			**name;
2589 	char			*fmt_string	= NULL;
2590 	__nis_mapping_format_t	*v		= NULL;
2591 	__nis_mapping_format_t	**value;
2592 	token_type		t;
2593 	int			n		 = 0;
2594 
2595 	if (index != NULL)
2596 		(void) memset(index, 0, sizeof (*index));
2597 
2598 	while (s < end_s) {
2599 		if (n > 0) {
2600 			s = skip_token(s, end_s, comma_token);
2601 			if (s == NULL) {
2602 				p_error = parse_bad_index_format;
2603 				break;
2604 			}
2605 		}
2606 		begin_token = s;
2607 		end_token = end_s;
2608 		s = get_next_token(&begin_token, &end_token, &t);
2609 		if (s == NULL)
2610 			break;
2611 		if (t != string_token) {
2612 			p_error = parse_bad_index_format;
2613 			break;
2614 		}
2615 		s = skip_token(s, end_s, equal_token);
2616 		if (s == NULL) {
2617 			p_error = parse_bad_index_format;
2618 			break;
2619 		}
2620 		if (index != NULL) {
2621 			name_str = s_strndup_esc(begin_token,
2622 				end_token - begin_token);
2623 			if (name_str == NULL)
2624 				break;
2625 		}
2626 		begin_token = s;
2627 		end_token = end_s;
2628 		s = get_next_token(&begin_token, &end_token, &t);
2629 		if (s == NULL)
2630 			break;
2631 		if (t != string_token && t != quoted_string_token) {
2632 			p_error = parse_bad_index_format;
2633 			break;
2634 		}
2635 		fmt_string = s_strndup(begin_token, end_token - begin_token);
2636 		if (fmt_string == NULL)
2637 			break;
2638 		if (!get_mapping_format(fmt_string, &v, NULL, NULL, FALSE))
2639 			break;
2640 		free(fmt_string);
2641 		fmt_string = NULL;
2642 		if (index != NULL) {
2643 			name = s_realloc(index->name,
2644 				(n + 1) * sizeof (char *));
2645 			if (name == NULL)
2646 				break;
2647 			value = s_realloc(index->value,
2648 				(n + 1) * sizeof (__nis_mapping_format_t *));
2649 			if (value == NULL)
2650 				break;
2651 			name[n] = name_str;
2652 			name_str = NULL;
2653 			value[n] = v;
2654 			v = NULL;
2655 			index->numIndexes = ++n;
2656 			index->name = name;
2657 			index->value = value;
2658 		} else if (v != NULL) {
2659 			free_mapping_format(v);
2660 			v = NULL;
2661 		}
2662 	}
2663 	if (p_error != no_parse_error) {
2664 		if (name_str != NULL)
2665 			free(name_str);
2666 		if (v != NULL)
2667 			free_mapping_format(v);
2668 		if (fmt_string != NULL)
2669 			free(fmt_string);
2670 		if (index != NULL)
2671 			free_index(index);
2672 	}
2673 	return (p_error != no_parse_error);
2674 }
2675 
2676 /*
2677  * FUNCTION:	get_deleteDisp
2678  *
2679  *	Parse deleteDisp. Sets p_error if an error occurred.
2680  *
2681  * RETURN VALUE:	TRUE on success
2682  *			FAILURE on failure
2683  *
2684  * INPUT:		begin and end of string and __nis_object_dn_t
2685  */
2686 
2687 static bool_t
2688 get_deleteDisp(const char *s_begin, const char *s_end,
2689 		__nis_object_dn_t *obj_dn)
2690 {
2691 	/*
2692 	 * deleteDisp: "always" | perDbId | "never"
2693 	 * perDbId: "dbid" "=" delDatabaseId
2694 	 */
2695 
2696 	if (same_string("always", s_begin, s_end - s_begin)) {
2697 		obj_dn->delDisp = dd_always;
2698 	} else if (same_string("never", s_begin, s_end - s_begin)) {
2699 		obj_dn->delDisp = dd_never;
2700 	} else if ((s_begin = skip_string("dbid", s_begin, s_end - s_begin))
2701 			!= NULL) {
2702 		obj_dn->delDisp = dd_perDbId;
2703 		while (s_begin < s_end && is_whitespace(*s_begin))
2704 			s_begin++;
2705 		if (s_begin == s_end || *s_begin != EQUAL_CHAR) {
2706 			p_error = parse_object_dn_syntax_error;
2707 		} else {
2708 			s_begin++;
2709 			while (s_begin < s_end && is_whitespace(*s_begin))
2710 				s_begin++;
2711 			while (s_begin < s_end && is_whitespace(s_end[-1]))
2712 				s_end--;
2713 			if (s_begin == s_end) {
2714 				p_error = parse_object_dn_syntax_error;
2715 			} else {
2716 				obj_dn->dbIdName =
2717 					s_strndup(s_begin, s_end - s_begin);
2718 			}
2719 		}
2720 	} else {
2721 		p_error = parse_object_dn_syntax_error;
2722 	}
2723 	return (p_error == no_parse_error);
2724 }
2725