Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*
      7  * The contents of this file are subject to the Netscape Public
      8  * License Version 1.1 (the "License"); you may not use this file
      9  * except in compliance with the License. You may obtain a copy of
     10  * the License at http://www.mozilla.org/NPL/
     11  *
     12  * Software distributed under the License is distributed on an "AS
     13  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
     14  * implied. See the License for the specific language governing
     15  * rights and limitations under the License.
     16  *
     17  * The Original Code is Mozilla Communicator client code, released
     18  * March 31, 1998.
     19  *
     20  * The Initial Developer of the Original Code is Netscape
     21  * Communications Corporation. Portions created by Netscape are
     22  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
     23  * Rights Reserved.
     24  *
     25  * Contributor(s):
     26  */
     27 
     28 /* ldapmodify.c - generic program to modify or add entries using LDAP */
     29 
     30 #include "ldaptool.h"
     31 #include "fileurl.h"
     32 
     33 #ifdef SOLARIS_LDAP_CMD
     34 #include <locale.h>
     35 #include "ldif.h"
     36 #endif	/* SOLARIS_LDAP_CMD */
     37 
     38 #ifndef SOLARIS_LDAP_CMD
     39 #define gettext(s) s
     40 #endif
     41 
     42 static int		newval, contoper, force, valsfromfiles, display_binary_values;
     43 static int		ldif_version = -1;	/* -1 => unknown version */
     44 static char		*rejfile = NULL;
     45 static char		*bulkimport_suffix = NULL;
     46 static int		ldapmodify_quiet = 0;
     47 
     48 #ifdef SOLARIS_LDAP_CMD
     49 static int		error = 0, replace, nbthreads = 1;
     50 static int		thr_create_errors = 0;
     51 static pthread_mutex_t	read_mutex = {0};
     52 static pthread_mutex_t	wait_mutex = {0};
     53 static pthread_cond_t	wait_cond  = {0};
     54 #else
     55 /*
     56  * For Solaris, ld is defined local to process() because
     57  * multiple threads restricts Solaris from using a global
     58  * ld variable.
     59  * Solaris uses multiple threads to create multiple
     60  * ldap connections if nbthreads > 1 (i.e -l option).
     61  */
     62 static LDAP		*ld;
     63 #endif	/* SOLARIS_LDAP_CMD */
     64 
     65 #define LDAPMOD_MAXLINE		4096
     66 
     67 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
     68 #define T_REPLICA_STR		"replica"
     69 #define T_DN_STR		"dn"
     70 #define T_VERSION_STR		"version"
     71 #define T_CHANGETYPESTR         "changetype"
     72 #define T_ADDCTSTR		"add"
     73 #define T_MODIFYCTSTR		"modify"
     74 #define T_DELETECTSTR		"delete"
     75 #define T_RENAMECTSTR		"rename"	/* non-standard */
     76 #define T_MODDNCTSTR		"moddn"
     77 #define T_MODRDNCTSTR		"modrdn"
     78 #define T_MODOPADDSTR		"add"
     79 #define T_MODOPREPLACESTR	"replace"
     80 #define T_MODOPDELETESTR	"delete"
     81 #define T_MODSEPSTR		"-"
     82 #define T_NEWRDNSTR		"newrdn"
     83 #define	T_NEWSUPERIORSTR	"newsuperior"
     84 #define	T_NEWPARENTSTR		"newparent"
     85 #define T_DELETEOLDRDNSTR	"deleteoldrdn"
     86 #define T_NEWSUPERIORSTR        "newsuperior"
     87 #define T_NEWPARENTSTR          "newparent"	/* non-standard */
     88 
     89 /* bulk import */
     90 #define	BULKIMPORT_START_OID	"2.16.840.1.113730.3.5.7"
     91 #define	BULKIMPORT_STOP_OID	"2.16.840.1.113730.3.5.8"
     92 
     93 static int process( void *arg );
     94 static void options_callback( int option, char *optarg );
     95 static void addmodifyop( LDAPMod ***pmodsp, int modop, char *attr,
     96 	char *value, int vlen );
     97 static void freepmods( LDAPMod **pmods );
     98 static char *read_one_record( FILE *fp );
     99 static char *strdup_and_trim( char *s );
    100 
    101 #ifdef SOLARIS_LDAP_CMD
    102 static int process_ldapmod_rec( LDAP *ld, char *rbuf );
    103 static int process_ldif_rec( LDAP *ld, char *rbuf );
    104 static int domodify( LDAP *ld, char *dn, LDAPMod **pmods, int newentry );
    105 static int dodelete( LDAP *ld, char *dn );
    106 static int dorename( LDAP *ld, char *dn, char *newrdn, char *newparent,
    107 	int deleteoldrdn );
    108 #else
    109 static int process_ldapmod_rec( char *rbuf );
    110 static int process_ldif_rec( char *rbuf );
    111 static int domodify( char *dn, LDAPMod **pmods, int newentry );
    112 static int dodelete( char *dn );
    113 static int dorename( char *dn, char *newrdn, char *newparent,
    114 	int deleteoldrdn );
    115 #endif	/* SOLARIS_LDAP_CMD */
    116 
    117 static void
    118 usage( void )
    119 {
    120     fprintf( stderr, gettext("usage: %s [options]\n"), ldaptool_progname );
    121     fprintf( stderr, gettext("options:\n") );
    122     ldaptool_common_usage( 0 );
    123     fprintf( stderr, gettext("    -c\t\tcontinuous mode (do not stop on errors)\n") );
    124     fprintf( stderr, gettext("    -A\t\tdisplay non-ASCII values in conjunction with -v\n") );
    125     fprintf( stderr, gettext("    -f file\tread modifications from file (default: standard input)\n") );
    126     if ( strcmp( ldaptool_progname, "ldapmodify" ) == 0 ){
    127 	fprintf( stderr, gettext("    -a\t\tadd entries\n") );
    128     }
    129     fprintf( stderr, gettext("    -b\t\tread values that start with / from files (for bin attrs)\n") );
    130     fprintf( stderr, gettext("    -F\t\tforce application of all changes, regardless of\n") );
    131     fprintf( stderr, gettext("      \t\treplica lines\n") );
    132     fprintf( stderr, gettext("    -e rejfile\tsave rejected entries in \"rejfile\"\n") );
    133     fprintf( stderr, gettext("    -B suffix\tbulk import to \"suffix\"\n"));
    134     fprintf( stderr, gettext("    -q\t\tbe quiet when adding/modifying entries\n") );
    135 #ifdef SOLARIS_LDAP_CMD
    136     fprintf( stderr, gettext("    -r\t\treplace values\n"));
    137     fprintf( stderr, gettext("    -l nb-connections\tnumber of LDAP connections\n"));
    138 #endif	/* SOLARIS_LDAP_CMD */
    139     exit( LDAP_PARAM_ERROR );
    140 }
    141 
    142 
    143 int
    144 main( int argc, char **argv )
    145 {
    146     int		optind, i;
    147     int		rc;
    148 
    149 #ifdef SOLARIS_LDAP_CMD
    150     char *locale = setlocale(LC_ALL, "");
    151     textdomain(TEXT_DOMAIN);
    152 #endif
    153 
    154 #ifdef notdef
    155 #ifdef HPUX11
    156 #ifndef __LP64__
    157 	_main( argc, argv);
    158 #endif /* __LP64_ */
    159 #endif /* HPUX11 */
    160 #endif
    161 
    162     valsfromfiles = display_binary_values = 0;
    163 
    164 #ifdef SOLARIS_LDAP_CMD
    165     optind = ldaptool_process_args( argc, argv, "aAbcFe:B:qrl:", 0,
    166 	    options_callback );
    167 #else
    168     optind = ldaptool_process_args( argc, argv, "aAbcFe:B:q", 0,
    169 	    options_callback );
    170 #endif	/* SOLARIS_LDAP_CMD */
    171 
    172 
    173     if ( optind == -1 ) {
    174 	usage();
    175     }
    176 
    177     if ( !newval && strcmp( ldaptool_progname, "ldapadd" ) == 0 ) {
    178 	newval = 1;
    179     }
    180 
    181     if ( ldaptool_fp == NULL ) {
    182 	ldaptool_fp = stdin;
    183     }
    184 
    185     if ( argc - optind != 0 ) {
    186 	usage();
    187     }
    188 
    189 #ifdef SOLARIS_LDAP_CMD
    190     /* trivial case */
    191     if ( nbthreads == 1 ) {
    192 	rc = process(NULL);
    193 	/* check for and report output error */
    194 	fflush( stdout );
    195 	rc = ldaptool_check_ferror( stdout, rc,
    196 		gettext("output error (output might be incomplete)") );
    197 	return( rc );
    198     }
    199 
    200     for ( i=0; i<nbthreads; ++i ) {
    201  	if ( thr_create(NULL, 0, process, NULL, NULL, NULL) != 0 )
    202 		++thr_create_errors;
    203     }
    204 
    205     if ( thr_create_errors < nbthreads )
    206     	while ( thr_join(0, NULL, NULL) == 0 );
    207     else
    208 	error = -1;
    209     rc = error;
    210     /* check for and report output error */
    211     fflush( stdout );
    212     rc = ldaptool_check_ferror( stdout, rc,
    213 		gettext("output error (output might be incomplete)") );
    214     return( rc );
    215 #else
    216     rc = process(NULL);
    217     /* check for and report output error */
    218     fflush( stdout );
    219     rc = ldaptool_check_ferror( stdout, rc,
    220 		gettext("output error (output might be incomplete)") );
    221     return( rc );
    222 #endif  /* SOLARIS_LDAP_CMD */
    223 }
    224 
    225 #ifdef SOLARIS_LDAP_CMD
    226 #define	exit(a)	\
    227 	if (nbthreads > 1) { \
    228     		mutex_lock(&read_mutex); \
    229 		error |= a; \
    230     		mutex_unlock(&read_mutex); \
    231 		thr_exit(&error); \
    232 	} else { \
    233 		exit(a); \
    234 	}
    235 #endif  /* SOLARIS_LDAP_CMD */
    236 
    237 static int
    238 process( void *arg )
    239 {
    240     char	*rbuf, *saved_rbuf, *start, *p, *q;
    241     FILE	*rfp = NULL;
    242     int		rc, use_ldif, deref;
    243     LDAPControl	*ldctrl;
    244 
    245 #ifdef SOLARIS_LDAP_CMD
    246     LDAP	*ld;
    247 #endif  /* SOLARIS_LDAP_CMD */
    248 
    249     ld = ldaptool_ldap_init( 0 );
    250 
    251     if ( !ldaptool_not ) {
    252 	deref = LDAP_DEREF_NEVER;	/* this seems prudent */
    253 	ldap_set_option( ld, LDAP_OPT_DEREF, &deref );
    254     }
    255 
    256     ldaptool_bind( ld );
    257 
    258     if (( ldctrl = ldaptool_create_manage_dsait_control()) != NULL ) {
    259 	ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls);
    260     }
    261 
    262     if ((ldctrl = ldaptool_create_proxyauth_control(ld)) !=NULL) {
    263 	ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls);
    264     }
    265 
    266     rc = 0;
    267 
    268     /* turn on bulk import?*/
    269     if (bulkimport_suffix) {
    270 	struct berval	bv, *retdata;
    271 	char		*retoid;
    272 
    273 	bv.bv_val = bulkimport_suffix;
    274 	bv.bv_len = strlen(bulkimport_suffix);
    275 	if ((rc = ldap_extended_operation_s(ld,
    276 	    BULKIMPORT_START_OID, &bv, NULL,
    277 	    NULL, &retoid, &retdata)) != 0) {
    278 		fprintf(stderr, gettext("Error: unable to service "
    279 		    "extended operation request\n\t'%s' for "
    280 		    "bulk import\n\t(error:%d:'%s')\n"),
    281 		    BULKIMPORT_START_OID, rc, ldap_err2string(rc));
    282 		return (rc);
    283 	}
    284 	if (retoid)
    285 		ldap_memfree(retoid);
    286 	if (retdata)
    287 		ber_bvfree(retdata);
    288     }
    289 
    290     while (( rc == 0 || contoper ) &&
    291 		( rbuf = read_one_record( ldaptool_fp )) != NULL ) {
    292 	/*
    293 	 * we assume record is ldif/slapd.replog if the first line
    294 	 * has a colon that appears to the left of any equal signs, OR
    295 	 * if the first line consists entirely of digits (an entry id)
    296 	 */
    297 	use_ldif = ( p = strchr( rbuf, ':' )) != NULL &&
    298 		( q = strchr( rbuf, '\n' )) != NULL && p < q &&
    299 		(( q = strchr( rbuf, '=' )) == NULL || p < q );
    300 
    301 	start = rbuf;
    302 	saved_rbuf = strdup( rbuf );
    303 
    304 	if ( !use_ldif && ( q = strchr( rbuf, '\n' )) != NULL ) {
    305 	    for ( p = rbuf; p < q; ++p ) {
    306 		if ( !isdigit( *p )) {
    307 		    break;
    308 		}
    309 	    }
    310 	    if ( p >= q ) {
    311 		use_ldif = 1;
    312 		start = q + 1;
    313 	    }
    314 	}
    315 
    316 #ifdef SOLARIS_LDAP_CMD
    317 	if ( use_ldif ) {
    318 	    rc = process_ldif_rec( ld, start );
    319 	} else {
    320 	    rc = process_ldapmod_rec( ld, start );
    321 	}
    322 #else
    323 	if ( use_ldif ) {
    324 	    rc = process_ldif_rec( start );
    325 	} else {
    326 	    rc = process_ldapmod_rec( start );
    327 	}
    328 #endif	/* SOLARIS_LDAP_CMD */
    329 
    330 	if ( rc != LDAP_SUCCESS && rejfile != NULL ) {
    331 	    /* Write this record to the reject file */
    332 	    int newfile = 0;
    333 	    struct stat stbuf;
    334 	    if ( stat( rejfile, &stbuf ) < 0 ) {
    335 		if ( errno == ENOENT ) {
    336 		    newfile = 1;
    337 		}
    338 	    }
    339 	    if (( rfp = ldaptool_open_file( rejfile, "a" )) == NULL ) {
    340 		fprintf( stderr, gettext("Cannot open error file \"%s\" - "
    341 			"erroneous entries will not be saved\n"), rejfile );
    342 		rejfile = NULL;
    343 	    } else {
    344 		if ( newfile == 0 ) {
    345 		    fputs( "\n", rfp );
    346 		}
    347 		fprintf( rfp, gettext("# Error: %s\n"), ldap_err2string( rc ));
    348 		fputs( saved_rbuf, rfp );
    349 		fclose( rfp );
    350 		rfp = NULL;
    351 	    }
    352 	}
    353 
    354 	free( rbuf );
    355 	free( saved_rbuf );
    356     }
    357     ldaptool_reset_control_array( ldaptool_request_ctrls );
    358 
    359     /* turn off bulk import?*/
    360     if (bulkimport_suffix) {
    361 	struct berval	bv, *retdata;
    362 	char		*retoid;
    363 
    364 	bv.bv_val = "";
    365 	bv.bv_len = 0;
    366 	if ((rc = ldap_extended_operation_s(ld,
    367 	    BULKIMPORT_STOP_OID, &bv, NULL,
    368 	    NULL, &retoid, &retdata)) != 0) {
    369 
    370 		fprintf(stderr, gettext("Error: unable to service "
    371 		    "extended operation request\n\t '%s' for "
    372 		    "bulk import\n\t(rc:%d:'%s')\n"),
    373 		    BULKIMPORT_STOP_OID, rc, ldap_err2string(rc));
    374 		return (rc);
    375 	}
    376 	if (retoid)
    377 		ldap_memfree(retoid);
    378 	if (retdata)
    379 		ber_bvfree(retdata);
    380     }
    381 
    382 #ifdef SOLARIS_LDAP_CMD
    383     mutex_lock(&read_mutex);
    384 #endif
    385     ldaptool_cleanup( ld );
    386 #ifdef SOLARIS_LDAP_CMD
    387     mutex_unlock(&read_mutex);
    388 #endif
    389     return( rc );
    390 }
    391 
    392 
    393 static void
    394 options_callback( int option, char *optarg )
    395 {
    396     switch( option ) {
    397     case 'a':	/* add */
    398 	newval = 1;
    399 	break;
    400     case 'b':	/* read values from files (for binary attributes) */
    401 	valsfromfiles = 1;
    402 	break;
    403     case 'A':	/* display non-ASCII values when -v is used */
    404 	display_binary_values = 1;
    405 	break;
    406     case 'c':	/* continuous operation */
    407 	contoper = 1;
    408 	break;
    409     case 'F':	/* force all changes records to be used */
    410 	force = 1;
    411 	break;
    412     case 'e':
    413 	rejfile = strdup( optarg );
    414 	break;
    415     case 'B':	/* bulk import option */
    416 	bulkimport_suffix = strdup( optarg );
    417 	break;
    418     case 'q':	/* quiet mode on add/modify operations */
    419 	ldapmodify_quiet = 1;
    420 	break;
    421 #ifdef SOLARIS_LDAP_CMD
    422     case 'r':	/* default is to replace rather than add values */
    423 	replace = 1;
    424 	break;
    425     case 'l':
    426 	nbthreads = atoi(optarg);
    427 	break;
    428 #endif	/* SOLARIS_LDAP_CMD */
    429     default:
    430 	usage();
    431     }
    432 }
    433 
    434 
    435 
    436 static int
    437 #ifdef SOLARIS_LDAP_CMD
    438 process_ldif_rec( LDAP *ld, char *rbuf )
    439 #else
    440 process_ldif_rec( char *rbuf )
    441 #endif
    442 {
    443     char	*line, *dn, *type, *value, *newrdn, *newparent, *p;
    444     char	*ctrl_oid=NULL, *ctrl_value=NULL;
    445     int 	ctrl_criticality=1;
    446     LDAPControl *ldctrl;
    447     int		rc, linenum, vlen, modop, replicaport;
    448     int		expect_modop, expect_sep, expect_chgtype_or_control, expect_newrdn;
    449     int		expect_deleteoldrdn, expect_newparent, rename, moddn;
    450     int		deleteoldrdn, saw_replica, use_record, new_entry, delete_entry;
    451     int         got_all, got_value;
    452     LDAPMod	**pmods;
    453 
    454     new_entry = newval;
    455 
    456     rc = got_all = saw_replica = delete_entry = expect_modop = 0;
    457     expect_deleteoldrdn = expect_newrdn = expect_newparent = expect_sep = 0;
    458     expect_chgtype_or_control = linenum = got_value = rename = moddn = 0;
    459     deleteoldrdn = 1;
    460     use_record = force;
    461     pmods = NULL;
    462     dn = newrdn = newparent = NULL;
    463 
    464 #ifdef SOLARIS_LDAP_CMD
    465     while ( rc == 0 && ( line = str_getline( &rbuf )) != NULL ) {
    466 #else
    467     while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
    468 #endif	/* SOLARIS_LDAP_CMD */
    469 	++linenum;
    470 	if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
    471 	    expect_sep = 0;
    472 	    expect_modop = 1;
    473 
    474 	    /*If we see a separator in the input stream,
    475 	     but we didn't get a value from the last modify
    476 	     then we have to fill pmods with an empty value*/
    477 	    if (modop == LDAP_MOD_REPLACE && !got_value){
    478 	      addmodifyop( &pmods, modop, value, NULL, 0);
    479 	    }
    480 
    481 	    got_value = 0;
    482 	    continue;
    483 	}
    484 
    485 #ifdef SOLARIS_LDAP_CMD
    486 	if ( str_parse_line( line, &type, &value, &vlen ) < 0 ) {
    487 #else
    488 	if ( ldif_parse_line( line, &type, &value, &vlen ) < 0 ) {
    489 #endif	/* SOLARIS_LDAP_CMD */
    490 	    fprintf( stderr, gettext("%s: invalid format (line %d of entry: %s)\n"),
    491 		    ldaptool_progname, linenum, dn == NULL ? "" : dn );
    492 	    fprintf( stderr, gettext("%s: line contents: (%s)\n"),
    493 		    ldaptool_progname, line );
    494 	    rc = LDAP_PARAM_ERROR;
    495 	    break;
    496 	}
    497 
    498 evaluate_line:
    499 	if ( dn == NULL ) {
    500 	    if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
    501 		++saw_replica;
    502 		if (( p = strchr( value, ':' )) == NULL ) {
    503 		    replicaport = LDAP_PORT;
    504 		} else {
    505 		    *p++ = '\0';
    506 		    replicaport = atoi( p );
    507 		}
    508 		if ( strcasecmp( value, ldaptool_host ) == 0 &&
    509 			replicaport == ldaptool_port ) {
    510 		    use_record = 1;
    511 		}
    512 
    513 	    } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
    514 		if (( dn = strdup( value )) == NULL ) {
    515 		    perror( "strdup" );
    516 		    exit( LDAP_NO_MEMORY );
    517 		}
    518 		expect_chgtype_or_control = 1;
    519 
    520 	    } else if ( strcasecmp( type, T_VERSION_STR ) == 0 ) {
    521 		ldif_version = atoi( value );
    522 		if ( ldif_version != LDIF_VERSION_ONE ) {
    523 		    fprintf( stderr, gettext("%s:  LDIF version %d is not supported;"
    524 			" use version: %d\n"), ldaptool_progname, ldif_version,
    525 			LDIF_VERSION_ONE );
    526 		    exit( LDAP_PARAM_ERROR );
    527 		}
    528 		if ( ldaptool_verbose ) {
    529 		    printf( gettext("Processing a version %d LDIF file...\n"),
    530 			    ldif_version );
    531 		}
    532 
    533 		/* Now check if there's something left to process   */
    534 		/* and if not, go get the new record, else continue */
    535 		if ( *rbuf == '\0' ) {
    536 			return( 0 );
    537 		}
    538 
    539 	    } else if ( !saw_replica ) {
    540 		printf( gettext("%s: skipping change record: no dn: line\n"),
    541 			ldaptool_progname );
    542 		return( 0 );
    543 	    }
    544 
    545 	    continue; /* skip all lines until we see "dn:" */
    546 	}
    547 
    548 	if ( expect_chgtype_or_control ) {
    549 	    expect_chgtype_or_control = 0;
    550 	    if ( !use_record && saw_replica ) {
    551 		printf( gettext("%s: skipping change record for entry: %s\n\t(LDAP host/port does not match replica: lines)\n"),
    552 			ldaptool_progname, dn );
    553 		free( dn );
    554 		return( 0 );
    555 	    }
    556 
    557 #ifndef SOLARIS_LDAP_CMD
    558 	    if ( strcasecmp( type, "control" ) == 0 ) {
    559 		value = strdup_and_trim( value );
    560 		if (ldaptool_parse_ctrl_arg(value, ' ', &ctrl_oid,
    561 			&ctrl_criticality, &ctrl_value, &vlen)) {
    562 			    usage();
    563 		}
    564         	ldctrl = calloc(1,sizeof(LDAPControl));
    565         	if (ctrl_value) {
    566         	    rc = ldaptool_berval_from_ldif_value( ctrl_value, vlen,
    567 			 &(ldctrl->ldctl_value),
    568 			 1 /* recognize file URLs */, 0 /* always try file */,
    569             		 1 /* report errors */ );
    570         	    if ((rc = ldaptool_fileurlerr2ldaperr( rc )) != LDAP_SUCCESS) {
    571             		fprintf( stderr, gettext("Unable to parse %s\n"), ctrl_value);
    572             		usage();
    573         	    }
    574         	}
    575         	ldctrl->ldctl_oid = ctrl_oid;
    576         	ldctrl->ldctl_iscritical = ctrl_criticality;
    577         	ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
    578 		expect_chgtype_or_control = 1;
    579 		continue;
    580 	    }
    581 #endif /* SOLARIS_LDAP_CMD */
    582 
    583 	    if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
    584 		value = strdup_and_trim( value );
    585 		if ( strcasecmp( value, T_MODIFYCTSTR ) == 0 ) {
    586 		    new_entry = 0;
    587 		    expect_modop = 1;
    588 		} else if ( strcasecmp( value, T_ADDCTSTR ) == 0 ) {
    589 		    new_entry = 1;
    590 		    modop = LDAP_MOD_ADD;
    591 		} else if ( strcasecmp( value, T_MODRDNCTSTR ) == 0 ) {
    592 		    expect_newrdn = 1;
    593 		    moddn = 1;
    594 		} else if ( strcasecmp( value, T_MODDNCTSTR ) == 0 ) {
    595 		    expect_newrdn = 1;
    596 		    moddn = 1;
    597 		} else if ( strcasecmp( value, T_RENAMECTSTR ) == 0 ) {
    598 		    expect_newrdn = 1;
    599 		    rename = 1;
    600 		} else if ( strcasecmp( value, T_DELETECTSTR ) == 0 ) {
    601 		    got_all = delete_entry = 1;
    602 		} else {
    603 		    fprintf( stderr,
    604 			    gettext("%s:  unknown %s \"%s\" (line %d of entry: %s)\n"),
    605 			    ldaptool_progname, T_CHANGETYPESTR, value,
    606 			    linenum, dn );
    607 		    rc = LDAP_PARAM_ERROR;
    608 		}
    609 		free( value );
    610 		continue;
    611 	    } else if ( newval ) {		/*  missing changetype => add */
    612 		new_entry = 1;
    613 		modop = LDAP_MOD_ADD;
    614 	    } else {
    615 	      /*The user MUST put in changetype: blah
    616 	       unless adding a new entry with either -a or ldapadd*/
    617 		fprintf(stderr, gettext("%s: Missing changetype operation specification.\n\tThe dn line must be followed by \"changetype: operation\"\n\t(unless ldapmodify is called with -a option)\n\twhere operation is add|delete|modify|modrdn|moddn|rename\n\t\"%s\" is not a valid changetype operation specification\n\t(line %d of entry %s)\n"),
    618 		ldaptool_progname, type, linenum, dn);
    619 		rc = LDAP_PARAM_ERROR;
    620 		/*expect_modop = 1;	 missing changetype => modify */
    621 	    }
    622 	}
    623 
    624 	if ( expect_modop ) {
    625 	    expect_modop = 0;
    626 	    expect_sep = 1;
    627 	    if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
    628 		modop = LDAP_MOD_ADD;
    629 		continue;
    630 	    } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
    631 		modop = LDAP_MOD_REPLACE;
    632 		continue;
    633 	    } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
    634 		modop = LDAP_MOD_DELETE;
    635 		addmodifyop( &pmods, modop, value, NULL, 0 );
    636 		continue;
    637 #ifdef SOLARIS_LDAP_CMD
    638 	    }  else { /* no modify op: use default */
    639 		modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
    640 	    }
    641 #else
    642 	    }  else { /*Bug 27479. Remove default add operation*/
    643 	      fprintf(stderr, gettext("%s: Invalid parameter \"%s\" specified for changetype modify (line %d of entry %s)\n"),
    644 		      ldaptool_progname, type, linenum, dn);
    645 	      rc = LDAP_PARAM_ERROR;
    646 	    }
    647 #endif	/* SOLARIS_LDAP_CMD */
    648 
    649 	  }
    650 
    651 	if ( expect_newrdn ) {
    652 	    if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
    653 		if ( *value == '\0' ) {
    654 		    fprintf( stderr,
    655 			    gettext("%s: newrdn value missing (line %d of entry: %s)\n"),
    656 			    ldaptool_progname, linenum, dn == NULL ? "" : dn );
    657 		    rc = LDAP_PARAM_ERROR;
    658 		} else if (( newrdn = strdup( value )) == NULL ) {
    659 		    perror( "strdup" );
    660 		    exit( LDAP_NO_MEMORY );
    661 		} else {
    662 		    expect_newrdn = 0;
    663 		    if ( rename ) {
    664 			expect_newparent = 1;
    665 		    } else {
    666 			expect_deleteoldrdn = 1;
    667 		    }
    668 		}
    669 	    } else {
    670 		fprintf( stderr, gettext("%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n"),
    671 			ldaptool_progname, T_NEWRDNSTR, type, linenum, dn );
    672 		rc = LDAP_PARAM_ERROR;
    673 	    }
    674 	} else if ( expect_newparent ) {
    675 	    expect_newparent = 0;
    676 	    if ( rename ) {
    677 		expect_deleteoldrdn = 1;
    678 	    }
    679 	    if ( strcasecmp( type, T_NEWPARENTSTR ) == 0
    680 		    || strcasecmp( type, T_NEWSUPERIORSTR ) == 0 ) {
    681 		if (( newparent = strdup( value )) == NULL ) {
    682 		    perror( "strdup" );
    683 		    exit( LDAP_NO_MEMORY );
    684 		}
    685 	    } else {
    686 		/* Since this is an optional argument for rename/moddn, cause
    687 		 * the current line to be re-evaluated if newparent doesn't
    688 		 * follow deleteoldrdn.
    689 		 */
    690 		newparent = NULL;
    691 		goto evaluate_line;
    692 	    }
    693 	} else if ( expect_deleteoldrdn ) {
    694 	    if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
    695 		if ( *value == '\0' ) {
    696 		    fprintf( stderr,
    697 			    gettext("%s: missing 0 or 1 (line %d of entry: %s)\n"),
    698 			    ldaptool_progname, linenum, dn == NULL ? "" : dn );
    699 		    rc = LDAP_PARAM_ERROR;
    700 		} else {
    701 		    deleteoldrdn = ( *value == '0' ) ? 0 : 1;
    702 		    expect_deleteoldrdn = 0;
    703 		    if ( moddn ) {
    704 			expect_newparent = 1;
    705 		    }
    706 		}
    707 	    } else {
    708 		fprintf( stderr, gettext("%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n"),
    709 			ldaptool_progname, T_DELETEOLDRDNSTR, type, linenum,
    710 			dn );
    711 		rc = LDAP_PARAM_ERROR;
    712 	    }
    713 	    got_all = 1;
    714 	} else if ( got_all ) {
    715 	    fprintf( stderr,
    716 		    gettext("%s: extra lines at end (line %d of entry %s)\n"),
    717 		    ldaptool_progname, linenum, dn );
    718 	    rc = LDAP_PARAM_ERROR;
    719 	    got_all = 1;
    720 	} else {
    721 	    addmodifyop( &pmods, modop, type, value, vlen );
    722 	    /*There was a value to replace*/
    723 	    got_value = 1;
    724 
    725 	}
    726     }
    727 
    728     if ( rc == 0 ) {
    729 	if ( delete_entry ) {
    730 #ifdef SOLARIS_LDAP_CMD
    731 	    rc = dodelete( ld, dn );
    732 #else
    733 	    rc = dodelete( dn );
    734 #endif	/* SOLARIS_LDAP_CMD */
    735 	} else if ( newrdn != NULL ) {
    736 #ifdef SOLARIS_LDAP_CMD
    737 	    rc = dorename( ld, dn, newrdn, newparent, deleteoldrdn );
    738 #else
    739 	    rc = dorename( dn, newrdn, newparent, deleteoldrdn );
    740 #endif	/* SOLARIS_LDAP_CMD */
    741 	    rename = 0;
    742 	} else {
    743 
    744 	  /*Patch to fix Bug 22183
    745 	    If pmods is null, then there is no
    746 	    attribute to replace, so we alloc
    747 	    an empty pmods*/
    748 	  if (modop == LDAP_MOD_REPLACE && !got_value && expect_sep){
    749 	    addmodifyop( &pmods, modop, value, NULL, 0);
    750 	  }/*End Patch*/
    751 
    752 
    753 #ifdef SOLARIS_LDAP_CMD
    754 	  rc = domodify( ld, dn, pmods, new_entry );
    755 #else
    756 	  rc = domodify( dn, pmods, new_entry );
    757 #endif	/* SOLARIS_LDAP_CMD */
    758 	}
    759 
    760 	if ( rc == LDAP_SUCCESS ) {
    761 	    rc = 0;
    762 	}
    763     }
    764 
    765     if ( dn != NULL ) {
    766 	free( dn );
    767     }
    768     if ( newrdn != NULL ) {
    769 	free( newrdn );
    770     }
    771     if ( newparent != NULL ) {
    772 	free( newparent );
    773     }
    774     if ( pmods != NULL ) {
    775 	freepmods( pmods );
    776     }
    777 
    778     return( rc );
    779 }
    780 
    781 
    782 static int
    783 #ifdef SOLARIS_LDAP_CMD
    784 process_ldapmod_rec( LDAP *ld, char *rbuf )
    785 #else
    786 process_ldapmod_rec( char *rbuf )
    787 #endif	/* SOLARIS_LDAP_CMD */
    788 {
    789     char	*line, *dn, *p, *q, *attr, *value;
    790     int		rc, linenum, modop;
    791     LDAPMod	**pmods;
    792 
    793     pmods = NULL;
    794     dn = NULL;
    795     linenum = 0;
    796     line = rbuf;
    797     rc = 0;
    798 
    799     while ( rc == 0 && rbuf != NULL && *rbuf != '\0' ) {
    800 	++linenum;
    801 	if (( p = strchr( rbuf, '\n' )) == NULL ) {
    802 	    rbuf = NULL;
    803 	} else {
    804 	    if ( *(p-1) == '\\' ) {	/* lines ending in '\' are continued */
    805 		strcpy( p - 1, p );
    806 		rbuf = p;
    807 		continue;
    808 	    }
    809 	    *p++ = '\0';
    810 	    rbuf = p;
    811 	}
    812 
    813 	if ( dn == NULL ) {	/* first line contains DN */
    814 	    if (( dn = strdup( line )) == NULL ) {
    815 		perror( "strdup" );
    816 		exit( LDAP_NO_MEMORY );
    817 	    }
    818 	} else {
    819 	    if (( p = strchr( line, '=' )) == NULL ) {
    820 		value = NULL;
    821 		p = line + strlen( line );
    822 	    } else {
    823 		*p++ = '\0';
    824 		value = p;
    825 	    }
    826 
    827 	    for ( attr = line; *attr != '\0' && isspace( *attr ); ++attr ) {
    828 		;	/* skip attribute leading white space */
    829 	    }
    830 
    831 	    for ( q = p - 1; q > attr && isspace( *q ); --q ) {
    832 		*q = '\0';	/* remove attribute trailing white space */
    833 	    }
    834 
    835 	    if ( value != NULL ) {
    836 		while ( isspace( *value )) {
    837 		    ++value;		/* skip value leading white space */
    838 		}
    839 		for ( q = value + strlen( value ) - 1; q > value &&
    840 			isspace( *q ); --q ) {
    841 		    *q = '\0';	/* remove value trailing white space */
    842 		}
    843 		if ( *value == '\0' ) {
    844 		    value = NULL;
    845 		}
    846 
    847 	    }
    848 
    849 	    if ( value == NULL && newval ) {
    850 		fprintf( stderr, gettext("%s: missing value on line %d (attr is %s)\n"),
    851 			ldaptool_progname, linenum, attr );
    852 		rc = LDAP_PARAM_ERROR;
    853 	    } else {
    854 		 switch ( *attr ) {
    855 		case '-':
    856 		    modop = LDAP_MOD_DELETE;
    857 		    ++attr;
    858 		    break;
    859 		case '+':
    860 		    modop = LDAP_MOD_ADD;
    861 		    ++attr;
    862 		    break;
    863 		default:
    864 #ifdef SOLARIS_LDAP_CMD
    865 		    modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
    866 #else
    867 		    /*Bug 27479. Remove the add default*/
    868 		      fprintf(stderr, gettext("%s: Invalid parameter specified for changetype modify (line %d of entry %s)\n"),
    869 		      ldaptool_progname, linenum, dn);
    870 		      rc = LDAP_PARAM_ERROR;
    871 #endif	/* SOLARIS_LDAP_CMD */
    872 		}
    873 
    874 		addmodifyop( &pmods, modop, attr, value,
    875 			( value == NULL ) ? 0 : strlen( value ));
    876 	    }
    877 	}
    878 
    879 	line = rbuf;
    880     }
    881 
    882     if ( rc == 0 ) {
    883 	if ( dn == NULL ) {
    884 	    rc = LDAP_PARAM_ERROR;
    885 #ifdef SOLARIS_LDAP_CMD
    886 	} else if (( rc = domodify( ld, dn, pmods, newval )) == LDAP_SUCCESS ){
    887 #else
    888 	} else if (( rc = domodify( dn, pmods, newval )) == LDAP_SUCCESS ){
    889 #endif	/* SOLARIS_LDAP_CMD */
    890 	  rc = 0;
    891 	}
    892       }
    893 
    894     if ( pmods != NULL ) {
    895 	freepmods( pmods );
    896     }
    897     if ( dn != NULL ) {
    898 	free( dn );
    899     }
    900 
    901     return( rc );
    902 }
    903 
    904 
    905 static void
    906 addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
    907 {
    908     LDAPMod		**pmods;
    909     int			i, j, rc;
    910     struct berval	*bvp;
    911 
    912     pmods = *pmodsp;
    913     modop |= LDAP_MOD_BVALUES;
    914 
    915     i = 0;
    916     if ( pmods != NULL ) {
    917 	for ( ; pmods[ i ] != NULL; ++i ) {
    918 	    if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
    919 		    pmods[ i ]->mod_op == modop ) {
    920 		break;
    921 	    }
    922 	}
    923     }
    924 
    925     if ( pmods == NULL || pmods[ i ] == NULL ) {
    926 	if (( pmods = (LDAPMod **)LDAPTOOL_SAFEREALLOC( pmods, (i + 2) *
    927 		sizeof( LDAPMod * ))) == NULL ) {
    928 	    perror( "realloc" );
    929 	    exit( LDAP_NO_MEMORY );
    930 	}
    931 	*pmodsp = pmods;
    932 	pmods[ i + 1 ] = NULL;
    933 	if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
    934 		== NULL ) {
    935 	    perror( "calloc" );
    936 	    exit( LDAP_NO_MEMORY );
    937 	}
    938 	pmods[ i ]->mod_op = modop;
    939 	if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
    940 	    perror( "strdup" );
    941 	    exit( LDAP_NO_MEMORY );
    942 	}
    943     }
    944 
    945     if ( value != NULL ) {
    946 	j = 0;
    947 	if ( pmods[ i ]->mod_bvalues != NULL ) {
    948 	    for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
    949 		;
    950 	    }
    951 	}
    952 	if (( pmods[ i ]->mod_bvalues = (struct berval **)
    953 		LDAPTOOL_SAFEREALLOC( pmods[ i ]->mod_bvalues,
    954 		(j + 2) * sizeof( struct berval * ))) == NULL ) {
    955 	    perror( "realloc" );
    956 	    exit( LDAP_NO_MEMORY );
    957 	}
    958 	pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
    959 	if (( bvp = (struct berval *)malloc( sizeof( struct berval )))
    960 		== NULL ) {
    961 	    perror( "malloc" );
    962 	    exit( LDAP_NO_MEMORY );
    963 	}
    964 	pmods[ i ]->mod_bvalues[ j ] = bvp;
    965 
    966 #ifdef notdef
    967 	if (ldaptool_verbose) {
    968 		printf(gettext("%s: value: %s vlen: %d\n"), "ldapmodify", value, vlen);
    969 	}
    970 #endif
    971 	rc = ldaptool_berval_from_ldif_value( value, vlen, bvp,
    972 		    ( ldif_version >= LDIF_VERSION_ONE ), valsfromfiles,
    973 			1 /* report errors */ );
    974 	if ( rc != LDAPTOOL_FILEURL_SUCCESS ) {
    975 	    exit( ldaptool_fileurlerr2ldaperr( rc ));
    976 	}
    977     }
    978 }
    979 
    980 
    981 static int
    982 #ifdef SOLARIS_LDAP_CMD
    983 domodify( LDAP *ld, char *dn, LDAPMod **pmods, int newentry )
    984 #else
    985 domodify( char *dn, LDAPMod **pmods, int newentry )
    986 #endif	/* SOLARIS_LDAP_CMD */
    987 {
    988     int			i, j, notascii, op;
    989     struct berval	*bvp;
    990 
    991     if ( pmods == NULL ) {
    992 	fprintf( stderr, gettext("%s: no attributes to change or add (entry %s)\n"),
    993 		ldaptool_progname, dn );
    994 	return( LDAP_PARAM_ERROR );
    995     }
    996 
    997     if ( ldaptool_verbose ) {
    998 	for ( i = 0; pmods[ i ] != NULL; ++i ) {
    999 	    op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
   1000 	    printf( gettext("%s %s:\n"), op == LDAP_MOD_REPLACE ?
   1001 		    gettext("replace") : op == LDAP_MOD_ADD ?
   1002 		    gettext("add") : gettext("delete"), pmods[ i ]->mod_type );
   1003 	    if ( pmods[ i ]->mod_bvalues != NULL ) {
   1004 		for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
   1005 		    bvp = pmods[ i ]->mod_bvalues[ j ];
   1006 		    notascii = 0;
   1007 		    if ( !display_binary_values ) {
   1008 			notascii = !ldaptool_berval_is_ascii( bvp );
   1009 		    }
   1010 		    if ( notascii ) {
   1011 			printf( gettext("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len );
   1012 		    } else {
   1013 			printf( "\t%s\n", bvp->bv_val );
   1014 		    }
   1015 		}
   1016 	    }
   1017 	}
   1018     }
   1019 
   1020     if ( !ldapmodify_quiet) {
   1021 	if ( newentry ) {
   1022 	    printf( gettext("%sadding new entry %s\n"),
   1023 		ldaptool_not ? "!" : "", dn );
   1024 	} else {
   1025 	    printf( gettext("%smodifying entry %s\n"),
   1026 		ldaptool_not ? "!" : "", dn );
   1027 	}
   1028     }
   1029 
   1030     if ( !ldaptool_not ) {
   1031 	if ( newentry ) {
   1032 	unsigned int	sleep_interval = 2; /* seconds */
   1033 
   1034 #ifdef SOLARIS_LDAP_CMD
   1035 	    /* Backward compatibility with old Solaris command */
   1036 	    unsigned int nb = 0;
   1037 	    timestruc_t to;
   1038 	    while ((i = ldaptool_add_ext_s( ld, dn, pmods,
   1039 			ldaptool_request_ctrls, NULL, "ldap_add" ))
   1040 			!= LDAP_SUCCESS) {
   1041 		if (i == LDAP_BUSY) {
   1042 			if ( sleep_interval > 3600 ) {
   1043 				printf(gettext("ldap_add: Unable to complete "
   1044 						"request.  Server is too "
   1045 						"busy servicing other "
   1046 						"requests\n"));
   1047 				break;
   1048 			}
   1049 			if ( !ldapmodify_quiet ) {
   1050 				printf(gettext("ldap_add: LDAP_BUSY returned "
   1051 						"by server.  Will retry "
   1052 						"operation in %d seconds\n"),
   1053 						sleep_interval);
   1054 			}
   1055 			sleep( sleep_interval );
   1056 			sleep_interval *= 2;
   1057 		} else if (i == LDAP_NO_SUCH_OBJECT) {
   1058 			/*
   1059 			 * Wait for the parent entry to be created by
   1060 			 * another thread. Do not retry more than the
   1061 			 * number of threads.
   1062 			 */
   1063 			++nb;
   1064 			if (nb >= nbthreads)
   1065 				break;
   1066 			mutex_lock(&wait_mutex);
   1067 			to.tv_sec = 5;
   1068 			to.tv_nsec = 0;
   1069 			if (cond_reltimedwait(&wait_cond, &wait_mutex, &to)
   1070 				== ETIME) {
   1071 					nb = nbthreads; /* last chance */
   1072 			}
   1073 			mutex_unlock(&wait_mutex);
   1074 		} else {
   1075 			break;
   1076 		}
   1077 	    }
   1078 	    cond_broadcast(&wait_cond);
   1079 #else
   1080 	    while ((i = ldaptool_add_ext_s( ld, dn, pmods,
   1081 			ldaptool_request_ctrls, NULL, "ldap_add" ))
   1082 			== LDAP_BUSY) {
   1083 		if ( sleep_interval > 3600 ) {
   1084 			printf("ldap_add: Unable to complete request. ");
   1085 			printf("Server is too ");
   1086 			printf("busy servicing other requests\n");
   1087 			break;
   1088 		}
   1089 		if ( !ldapmodify_quiet ) {
   1090 			printf("ldap_add: LDAP_BUSY returned by server. ");
   1091 			printf("Will retry operation ");
   1092 			printf("in %d seconds\n", sleep_interval);
   1093 		}
   1094 		sleep( sleep_interval );
   1095 		sleep_interval *= 2;
   1096 	    }
   1097 #endif	/* SOLARIS_LDAP_CMD */
   1098 	} else {
   1099 	    i = ldaptool_modify_ext_s( ld, dn, pmods, ldaptool_request_ctrls,
   1100 		    NULL, "ldap_modify" );
   1101 	}
   1102 	if ( i == LDAP_SUCCESS && ldaptool_verbose ) {
   1103 	    printf( gettext("modify complete\n") );
   1104 	}
   1105     } else {
   1106 	i = LDAP_SUCCESS;
   1107     }
   1108 
   1109     if ( !ldapmodify_quiet) {
   1110 	putchar( '\n' );
   1111     }
   1112 
   1113     return( i );
   1114 }
   1115 
   1116 
   1117 static int
   1118 #ifdef SOLARIS_LDAP_CMD
   1119 dodelete( LDAP *ld, char *dn )
   1120 #else
   1121 dodelete( char *dn )
   1122 #endif	/* SOLARIS_LDAP_CMD */
   1123 {
   1124     int	rc;
   1125 
   1126     printf( gettext("%sdeleting entry %s\n"), ldaptool_not ? "!" : "", dn );
   1127     if ( !ldaptool_not ) {
   1128 	if (( rc = ldaptool_delete_ext_s( ld, dn, ldaptool_request_ctrls,
   1129 		NULL, "ldap_delete" )) == LDAP_SUCCESS && ldaptool_verbose ) {
   1130 	    printf( gettext("delete complete") );
   1131 	}
   1132     } else {
   1133 	rc = LDAP_SUCCESS;
   1134     }
   1135 
   1136     putchar( '\n' );
   1137 
   1138     return( rc );
   1139 }
   1140 
   1141 
   1142 static int
   1143 #ifdef SOLARIS_LDAP_CMD
   1144 dorename( LDAP *ld, char *dn, char *newrdn, char *newparent, int deleteoldrdn )
   1145 #else
   1146 dorename( char *dn, char *newrdn, char *newparent, int deleteoldrdn )
   1147 #endif	/* SOLARIS_LDAP_CMD */
   1148 {
   1149     int	rc;
   1150 
   1151     if ( ldaptool_verbose ) {
   1152 	if ( newparent == NULL ) {
   1153 	    printf(deleteoldrdn ?
   1154 		  gettext("new RDN: %s (do not keep existing values)\n"):
   1155 		  gettext("new RDN: %s (keep existing values)\n"));
   1156 	} else {
   1157 	    printf(deleteoldrdn ?
   1158 		  gettext("new RDN: %s, new parent %s ( do not keep existing values)\n"):
   1159 		  gettext("new RDN: %s, new parent %s ( keep existing values)\n"));
   1160 	}
   1161     }
   1162 
   1163     printf( gettext("%smodifying RDN of entry %s%s\n"),
   1164 	    ldaptool_not ? "!" : "", dn, ( newparent == NULL ) ? "" :
   1165 	    gettext(" and/or moving it beneath a new parent\n") );
   1166 
   1167     if ( !ldaptool_not ) {
   1168 	if (( rc = ldaptool_rename_s( ld, dn, newrdn, newparent, deleteoldrdn,
   1169 		ldaptool_request_ctrls, NULL, "ldap_rename" )) == LDAP_SUCCESS
   1170 		&& ldaptool_verbose ) {
   1171 	    printf( gettext("rename completed\n") );
   1172 	}
   1173     } else {
   1174 	rc = LDAP_SUCCESS;
   1175     }
   1176 
   1177     putchar( '\n' );
   1178 
   1179     return( rc );
   1180 }
   1181 
   1182 
   1183 static void
   1184 freepmods( LDAPMod **pmods )
   1185 {
   1186     int	i;
   1187 
   1188     for ( i = 0; pmods[ i ] != NULL; ++i ) {
   1189 	if ( pmods[ i ]->mod_bvalues != NULL ) {
   1190 	    ber_bvecfree( pmods[ i ]->mod_bvalues );
   1191 	}
   1192 	if ( pmods[ i ]->mod_type != NULL ) {
   1193 	    free( pmods[ i ]->mod_type );
   1194 	}
   1195 	free( pmods[ i ] );
   1196     }
   1197     free( pmods );
   1198 }
   1199 
   1200 
   1201 static char *
   1202 read_one_record( FILE *fp )
   1203 {
   1204     int         len, gotnothing;
   1205     char        *buf, line[ LDAPMOD_MAXLINE ];
   1206     int		lcur, lmax;
   1207 
   1208     lcur = lmax = 0;
   1209     buf = NULL;
   1210     gotnothing = 1;
   1211 
   1212 #ifdef SOLARIS_LDAP_CMD
   1213     mutex_lock(&read_mutex);
   1214 
   1215     if (fp == NULL) {
   1216     	mutex_unlock(&read_mutex);
   1217 	return(NULL);
   1218     }
   1219 #endif
   1220 
   1221     while ( fgets( line, sizeof(line), fp ) != NULL ) {
   1222 	if ( (len = strlen( line )) < 2 ) {
   1223 	    if ( gotnothing ) {
   1224 		continue;
   1225 	    } else {
   1226 		break;
   1227 	    }
   1228 	}
   1229 
   1230 	/* Check if the blank line starts with '\r' (CR) */
   1231 	if ( ((len = strlen( line )) == 2) && (line[0] == '\r') ) {
   1232 	    if ( gotnothing ) {
   1233 		continue;
   1234 	    } else {
   1235 		break;
   1236 	      }
   1237 	}
   1238 
   1239 	if ( *line == '#' ) {
   1240 	    continue;			/* skip comment lines */
   1241 	}
   1242 
   1243 	gotnothing = 0;
   1244         if ( lcur + len + 1 > lmax ) {
   1245             lmax = LDAPMOD_MAXLINE
   1246 		    * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
   1247 	    if (( buf = (char *)LDAPTOOL_SAFEREALLOC( buf, lmax )) == NULL ) {
   1248 		perror( "realloc" );
   1249 		#ifdef SOLARIS_LDAP_CMD
   1250     			mutex_unlock(&read_mutex);
   1251 		#endif
   1252 		exit( LDAP_NO_MEMORY );
   1253 	    }
   1254         }
   1255         strcpy( buf + lcur, line );
   1256         lcur += len;
   1257     }
   1258 
   1259 #ifdef SOLARIS_LDAP_CMD
   1260     mutex_unlock(&read_mutex);
   1261 #endif
   1262 
   1263     return( buf );
   1264 }
   1265 
   1266 
   1267 /*
   1268  * strdup and trim trailing blanks
   1269  */
   1270 static char *
   1271 strdup_and_trim( char *s )
   1272 {
   1273     char	*p;
   1274 
   1275     if (( s = strdup( s )) == NULL ) {
   1276 	perror( "strdup" );
   1277 	exit( LDAP_NO_MEMORY );
   1278     }
   1279 
   1280     p = s + strlen( s ) - 1;
   1281     while ( p >= s && isspace( *p )) {
   1282 	--p;
   1283     }
   1284     *++p = '\0';
   1285 
   1286     return( s );
   1287 }
   1288