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