Example

The ldapmod.c example file provides an understanding about the transaction capability.

The following example is an ldapmod.c example file, which is modified for limited transaction capability:

static char sccsid[] = "@(#)17  1.35 11/18/02 progref.idd, ldap, 5.2 15:20:20";
/*
 * COMPONENT_NAME: ldap.clients
 *
 * ABSTRACT: generic program to modify or add entries using LDAP with a transaction
 *
 * ORIGINS: 202,27
 *
 * (C) COPYRIGHT International Business Machines Corp. 2002
 * All Rights Reserved
 * Licensed Materials - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

/*
 * Copyright (c) 1995 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

/* ldaptxmod.c - generic program to modify or add entries using LDAP 
using a single transaction */

#include <ldap.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>

#if !defined( WIN32 ) 
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#define LDAPMODIFY_REPLACE	1
#define LDAPMODIFY_ADD		2

#if defined( WIN32 ) 
#define strcasecmp stricmp
#endif

#define safe_realloc( ptr, size )	( ptr == NULL ? malloc( size ) : \
					 realloc( ptr, size ))

#define MAX_SUPPLIED_PW_LENGTH 256
#define LDAPMOD_MAXLINE		4096

/* Strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
#define T_REPLICA_STR  "replica"
#define T_DN_STR   "dn"
#define T_CHANGETYPESTR  "changetype"
#define T_ADDCTSTR   "add"
#define T_MODIFYCTSTR  "modify"
#define T_DELETECTSTR  "delete"
#define T_MODRDNCTSTR  "modrdn"
#define T_MODDNCTSTR   "moddn"
#define T_MODOPADDSTR  "add"
#define T_OPERSTR "transaction_operation"
#define T_MODOPREPLACESTR "replace"
#define T_MODOPDELETESTR "delete"
#define T_MODSEPSTR   "-"
#define T_NEWRDNSTR   "newrdn"
#define T_DELETEOLDRDNSTR "deleteoldrdn"
#define T_NEWSUPERIORSTR  "newsuperior"
#define T_CONTROLSTR "control"

extern char * str_getline(char**);
char * getPassword(void);
char * read_one_record(FILE *fp);

#if defined _WIN32
int getopt (int, char**, char*);
#endif
#ifndef -win32
#ifdef -GCC3
#include <errno.h>
#else
extern int errno;
#endif
#endif

/*Required for password prompting*/
#ifdef -win32
#include <conio.h>
#else
/*termios.h is defined by POSIX*/
#include <termios.h>
#endif

/* Global variables */
static LDAP     *ld         = NULL;  /* LDAP sesssion handle */
static FILE     *fp         = NULL;  /* input file handle */
static char	*prog       = NULL;  /* program name */
static char	*binddn     = NULL;  /* bind DN */
static char	*passwd     = NULL;  /* bind password */
static char	*ldaphost   = "localhost";  /* server host name */
static char     *mech       = NULL;  /* bind mechanism */
static char     *charset    = NULL;  /* character set for input */ 
static char     *keyfile    = NULL;  /* SSL key database file name*/
static char     *keyfile_pw = NULL;  /* SSL key database password */
static char     *cert_label = NULL;  /* client certificate label */
static int      hoplimit  = 10;      /* limit for referral chasing */
static int	ldapport  = LDAP_PORT; /* server port number */
static int	doit      = 1;       /* 0 to make believe */
static int	verbose   = 0;       /* 1 for more trace messages */
static int	contoper  = 0;       /* 1 to continue after errors */
static int	force     = 0;
static int	valsfromfiles = 0;   
static int	operation   = LDAPMODIFY_REPLACE;
static int	referrals   = LDAP_OPT_ON;
static int	ldapversion = LDAP_VERSION3;
static int	DebugLevel  = 0;          /* 1 to activate library traces */
static int	ssl         = 0;          /* 1 to use SSL */
static int  manageDsa  = LDAP_FALSE; /* LDAP_TRUE to modify referral objects */

static LDAPControl manageDsaIT = {
  "2.16.840.1.113730.3.4.2", /* OID */
  { 0, NULL },               /* no value */
  LDAP_OPT_ON                /* critical */
};

/* NULL terminated array of server controls*/
static LDAPControl *Server_Controls[3] = {NULL, NULL, NULL};   

static int Num_Operations = 0;  /* count of times one must go to 
        ldap_result to check result codes */
static int Message_ID = 0;      /* message ID returned by async 
        ldap operation, currently not tracked*/
static int abort_flag = 0;      /* abort transaction flag set by 
        -A parameter */

/* Implement getopt() for Windows to parse command line arguments. */
#if defined(_WIN32) 
char *optarg = NULL;
int   optind = 1;
int   optopt = 0;
#define EMSG ""

int getopt(int argc, char **argv, char *ostr) {
  static char	*place = EMSG;
  register char	*oli;
  
  if (!*place) {
    if (optind >= argc || *(place = argv[optind]) != '-' || !*++place) {
      return EOF;
    }
    if (*place == '-') {
      ++optind;
      return EOF;
    }
  }
  if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
    if (!*place) {
      ++optind;
    }
    fprintf(stderr, "%s: %s: %c\n", "getopt", "illegal option", optopt);
    return ( '?' ); 
  }
  if (*++oli != ':') {
    optarg = NULL;
    if (!*place)
      ++optind;
  } else {
    if (*place) {
      optarg = place;
    } else if (argc <= ++optind) {
      place = EMSG;
      fprintf(stderr, "%s: %s: %c\n", "getopt", "option requires an argument", 
			optopt);
      return 0;
    } else {
      optarg = argv[optind];
    }
    place = EMSG;
    ++optind;
  }
  return optopt;
}
#endif

/* Display usage statement and exit. */
void usage()
{
  fprintf(stderr, "\nSends modify or add requests to an LDAP server.\n");
  fprintf(stderr, "usage:\n");
  fprintf(stderr, "    %s [options] [-f file]\n", prog);
  fprintf(stderr, "where:\n");
  fprintf(stderr, "    file: name of input file\n");
  fprintf(stderr, "note:\n");
  fprintf(stderr, "    standard input is used if file is not specified\n");
  fprintf(stderr, "options:\n" );
  fprintf(stderr, "    -h host      LDAP server host name\n");
  fprintf(stderr, "    -p port      LDAP server port number\n");
  fprintf(stderr, "    -D dn        bind DN\n");
  fprintf(stderr, "    -w password  bind password or '?' for non-echoed prompt\n");
  fprintf(stderr, "    -Z           use a secure ldap connection (SSL)\n");
  fprintf(stderr, "    -K keyfile   file to use for keys\n");
  fprintf(stderr, "    -P key_pw    keyfile password\n");
  fprintf(stderr, "    -N key_name  private key name to use in keyfile\n");
  fprintf(stderr, "    -R           do not chase referrals\n");
  fprintf(stderr, "    -M           Manage referral objects as normal entries.\n");
  fprintf(stderr, "    -m mechanism perform SASL bind with the given mechanism\n");
  fprintf(stderr, "    -O maxhops   maximum number of referrals to follow in a 
			sequence\n");
  fprintf(stderr, "    -V version   LDAP protocol version (2 or 3; only 3 is 
			supported)\n");
  fprintf(stderr, "    -C charset   character set name to use, as registered with 
			IANA\n");
  fprintf(stderr, "    -a           force add operation as default\n");
  fprintf(stderr, "    -r           force replace operation as default\n");
  fprintf(stderr, "    -b           support binary values from files (old style 
			paths)\n");
  fprintf(stderr, "    -c           continuous operation; do not stop processing 
			on error\n");
  fprintf(stderr, "    -n           show what would be done but don't actually do 
			it\n");
  fprintf(stderr, "    -v           verbose mode\n");
  fprintf(stderr, "    -A           set transaction abort flag\n");
  fprintf(stderr, "    -d level     set debug level in LDAP library\n");
  exit(1);
}

/* Parse command line arguments. */
void parse_arguments(int argc, char **argv) {
  int i = 0;
  int port = 0;
  char *optpattern = "FaAbcRMZnrv?h:V:p:D:w:d:f:K:P:N:C:O:m:";
#ifndef _WIN32
  extern char	*optarg;
  extern int	optind;
#endif  

  fp = stdin;
  while ((i = getopt(argc, argv, optpattern)) != EOF) {
    switch ( i ) {
    case 'V':
      ldapversion = atoi(optarg);
      if (ldapversion != LDAP_VERSION3) {
	fprintf(stderr, "Unsupported version level supplied.\n");
	usage();
      }
      break;
    case 'A':       /* force all changes records to be used */
      abort_flag = 1;
      break;
    case 'a': 
      operation = LDAPMODIFY_ADD;
      break;
    case 'b':	/* read values from files (for binary attributes)*/ 
      valsfromfiles = 1;
      break;
    case 'c':	/* continuous operation*/ 
      contoper = 1;
      break;
    case 'F':	/* force all changes records to be used*/ 
      force = 1;
      break;
    case 'h':	/* ldap host*/ 
      ldaphost = strdup( optarg );
      break;
    case 'D':	/* bind DN */ 
      binddn = strdup( optarg );
      break;
    case 'w':	/* password*/ 
      if (optarg && optarg[0] == '?') {
	passwd = getPassword();	
      } else 
	if (!(passwd = strdup( optarg )))
	  perror("password");
      break;
    case 'd':
      DebugLevel = atoi(optarg);
      break;
    case 'f':	/* read from file */ 
      if ((optarg[0] == '-') && (optarg[1] == '\0'))
	fp = stdin;
      else if ((fp = fopen( optarg, "r" )) == NULL) {
	perror( optarg );
	exit( 1 );
      }
      break;
    case 'p':
      ldapport = atoi( optarg );
      port = 1;
      break;
    case 'n':	/* print adds, don't actually do them*/ 
      doit = 0;
      break;
    case 'r':	/* default is to replace rather than add values*/ 
      operation = LDAPMODIFY_REPLACE;
      break;
    case 'R': 	/* don't automatically chase referrals*/ 
      referrals = LDAP_OPT_OFF;
      break;
    case 'M':   /* manage referral objects as normal entries */ 
      manageDsa = LDAP_TRUE;
      break;
    case 'O':   /* set maximum referral hop count  */
      hoplimit = atoi( optarg );
      break;
    case 'm':   /* use SASL bind mechanism  */
      if (!(mech = strdup ( optarg )))
	perror("mech");
      break;
    case 'v':   /* verbose mode */ 
      verbose++;
      break;
    case 'K':
      keyfile = strdup( optarg );
      break;
    case 'P':
      keyfile_pw = strdup( optarg );
      break;
    case 'N':
      cert_label = strdup( optarg );
      break;
    case 'Z':
      ssl = 1;
      break;
    case 'C':
      charset = strdup(optarg);
      break;
    case '?':
    default:
      usage();
    }
  }

  if (argc - optind !=  0)
    usage();

  /* Use default SSL port if none specified*/ 
  if (( port == 0 ) && ( ssl ))
    ldapport = LDAPS_PORT;

  if ( ! DebugLevel ) {
    char *debug_ptr = NULL;

    if ( ( debug_ptr = getenv ( "LDAP_DEBUG" ) ) )
      DebugLevel = atoi ( debug_ptr );
  }
}

/* Get a password from the user but don't display it. */
char* getPassword( void ) {
  char supplied_password[ MAX_SUPPLIED_PW_LENGTH + 1 ]; /* Buffer for password */
  
#ifdef _WIN32
  char in  = '\0';                 /* Input character */
  int  len = 0;                    /* Length of password */
#else
  struct termios echo_control; 
  struct termios save_control;
  
  int fd = 0;                 /* File descriptor */
  int attrSet = 0;            /* Checked later for reset */
  
  /* Get the file descriptor associated with stdin. */
  fd = fileno( stdin );
  
  if (tcgetattr( fd, &echo_control ) != -1) {
    save_control = echo_control;
    echo_control.c_lflag &= ~( ECHO | ECHONL );
    
    if (tcsetattr( fd, TCSANOW, &echo_control ) == -1) {
      fprintf(stderr, "Internal error setting terminal attribute.\n");
      exit( errno );
    }
    
    attrSet = 1;
  }
#endif 
  
  /* Prompt for a password. */
  fputs( "Enter password ==> ", stdout );
  fflush( stdout );
  
#ifdef _WIN32
  /* Windows 9x/NT will always read from the console, i.e.,
     piped or redirected input will be ignored. */ 
  while ( in != '\r' && len <= MAX_SUPPLIED_PW_LENGTH ) {
    in = _getch();
    
    if (in != '\r') {
      supplied_password[len] = in;
      len++;      
    } else {
      supplied_password[len] = '\0';
    }
  }
#else
  /* Get the password from stdin. */
  fgets( supplied_password, MAX_SUPPLIED_PW_LENGTH, stdin );
  
  /* Remove the newline at the end. */
  supplied_password[strlen( supplied_password ) - 1] = '\0';
  
#endif
  
#ifndef _WIN32
  /* Reset the terminal. */
  if (attrSet && tcsetattr( fd, TCSANOW, &save_control ) == -1) {
    fprintf(stderr, "Unable to reset the display.\n");
  }
#endif  
  fprintf( stdout, "\n" );
  
  return ( supplied_password == NULL )? supplied_password : 
			strdup( supplied_password );
}

/* Rebind callback function. */
int rebindproc(LDAP *ld, char **dnp, char **pwp, int *methodp, int freeit) {
  if ( !freeit ) {
    *methodp = LDAP_AUTH_SIMPLE;
    if ( binddn != NULL ) {
      *dnp = strdup( binddn );
      *pwp = strdup ( passwd );
    } else {
      *dnp = NULL;
      *pwp = NULL;
    }
  } else {
    free ( *dnp );
    free ( *pwp );
  }
  return LDAP_SUCCESS;
}

/* Connect and bind to server. */
void connect_to_server() {
  int	failureReasonCode, rc, authmethod;
  struct berval		ber;
  struct berval		*server_creds;
  
  /* call ldap_ssl_client_init if V3 and SSL */
  if (ssl && (ldapversion == LDAP_VERSION3)) {
    if ( keyfile == NULL ) {
      keyfile = getenv("SSL_KEYRING");
      if (keyfile != NULL) {
	keyfile = strdup(keyfile);
      }
    }
    
    if (verbose)
      printf( "ldap_ssl_client_init( %s, %s, 0, &failureReasonCode )\n",
	      ((keyfile) ? keyfile : "NULL"), 
	      ((keyfile_pw) ? keyfile_pw : "NULL"));
#ifdef LDAP_SSL_MAX
    rc = ibm_set_unrestricted_cipher_support();
    if (rc != 0) {
      fprintf( stderr, "Warning: ibm_gsk_set_unrestricted_cipher_support failed! 
        rc == %d\n", rc ); 
    }
#endif
    
    rc = ldap_ssl_client_init( keyfile, keyfile_pw, 0, &failureReasonCode );
    if (rc != LDAP_SUCCESS) {
      fprintf( stderr, 
	       "ldap_ssl_client_init failed! rc == %d, failureReasonCode == %d\n", 
	       rc, failureReasonCode );
      exit( 1 );
    }
  }
	
  /* Open connection to server */
  if (ldapversion == LDAP_VERSION3) {
    if (ssl) {
      if (verbose)
	printf("ldap_ssl_init( %s, %d, %s )\n", ldaphost, ldapport, 
	       ((cert_label) ? cert_label : "NULL"));
      ld = ldap_ssl_init( ldaphost, ldapport, cert_label );
      if (ld == NULL) {
	fprintf( stderr, "ldap_ssl_init failed\n" );
	perror( ldaphost );
	exit( 1 );
      }
    } else {
      if (verbose)
	printf("ldap_init(%s, %d) \n", ldaphost, ldapport);
      if ((ld = ldap_init(ldaphost, ldapport)) == NULL) {
	perror(ldaphost);
	exit(1);
      }
    }
  } 
  
  /* Set options */
  ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, (void * )&ldapversion);
  
  if (ldapversion == LDAP_VERSION3) {
    ldap_set_option (ld, LDAP_OPT_DEBUG, (void * )&DebugLevel);
    ldap_set_option( ld, LDAP_OPT_REFHOPLIMIT, (void *)&hoplimit);
  }
  ldap_set_option (ld, LDAP_OPT_REFERRALS, (void * )referrals);
  if (binddn != NULL)
    ldap_set_rebind_proc( ld, (LDAPRebindProc)rebindproc );
  if (charset != NULL) {
    if (ldap_set_iconv_local_charset(charset) != LDAP_SUCCESS) {
      fprintf(stderr, "unsupported charset %s\n", charset);
      exit(0);
    }
    ldap_set_option(ld, LDAP_OPT_UTF8_IO, (void *)LDAP_UTF8_XLATE_ON);
  }
  
  /* Bind to server */
  if (ldapversion == LDAP_VERSION3) {
    if ( ! mech ) /* Use simple bind */ {
      rc = ldap_simple_bind_s(ld, binddn, passwd);
      if ( rc != LDAP_SUCCESS ) {
	ldap_perror( ld, "ldap_simple_bind" );
	/* LDAP_OPT_EXT_ERROR only valuable for ssl communication.
	   In this example, for LDAP v3, the bind is the first 
	   instance in which communication actually flows to the
	   server.  So, if there is an ssl configuration error or
	   other ssl problem, this will be the first instance where
	   it will be detected. */
	if (ssl) {
	  ldap_get_option( ld, LDAP_OPT_EXT_ERROR, &failureReasonCode);
	  fprintf( stderr, "Attempted communication over SSL.\n");
	  fprintf( stderr, "  The extended error is %d.\n", failureReasonCode);
	}
	exit( rc );
      }
    } else /* Presence of mechanism means SASL bind */ {
      /* Special case for mech="EXTERNAL".  Unconditionally set bind DN
	 and credentials to NULL.  This option should be used in tandem
	 with SSL and client authentication.  For other SASL mechanisms,
	 use the specified bind DN and credentials. */
      if (strcmp(mech, LDAP_MECHANISM_EXTERNAL) == 0) {
	rc = ldap_sasl_bind_s (ld, NULL, mech, NULL, NULL, NULL, &server_creds);
	if (rc != LDAP_SUCCESS ) {
	  ldap_perror ( ld, "ldap_sasl_bind_s" );
	  exit( rc );
	}
      } else {
	if (strcmp(mech, LDAP_MECHANISM_GSSAPI) == 0) {
	  rc = ldap_sasl_bind_s (ld, NULL, mech, NULL, NULL, NULL, &server_creds);
	  if (rc != LDAP_SUCCESS ) {
	    ldap_perror ( ld, "ldap_sasl_bind_s" );
	    exit( rc );
	  }
	} else /* other SASL mechanisms */ {
	  ber.bv_len = strlen ( passwd );
	  ber.bv_val = passwd;
	  rc = ldap_sasl_bind_s (ld, binddn, mech, &ber, NULL, NULL, &server_creds);
	  if (rc != LDAP_SUCCESS ) {
	    ldap_perror ( ld, "ldap_sasl_bind_s" );
	    exit( rc );
	  }
	}
      }
    }
  }
}

/* Read a record from the file. */
char * read_one_record(FILE *fp)
{
  int	len = 0;
  int	lcur = 0; 
  int   lmax = 0;
  char line[LDAPMOD_MAXLINE];
  char temp[LDAPMOD_MAXLINE];
  char *buf = NULL;
	
  /* Reads in and changes to ldif form */
  while (( fgets( line, sizeof(line), fp ) != NULL )) {
    if (!(strncmp(line,"changenumber",10)))
      {do
	fgets(line,sizeof(line),fp);
      while(strncmp(line,"targetdn",8)); /*changes the = to : for parse*/
      line[8]=':';}
		
    if (!(strncmp(line,"changetype",9)))		
      line[10]=':';
    if (!(strncmp(line,"changetype:delete",16)))
      (fgets(temp,sizeof(line),fp)); /*gets rid of the changetime line after 
			a delete.*/
    if (!(strncmp(line,"changetime",9)))
      {fgets(line,sizeof(line),fp);
      if (!(strncmp(line,"newrdn",6)))
	line[6]=':';
      else
	line[7]=':';
      }
    if (!(strncmp(line,"deleteoldrdn",12)))
      line[12]=':';
    if ( *line != '\n' ) {
      len = strlen( line );
      if ( lcur + len + 1 > lmax ) {
	lmax = LDAPMOD_MAXLINE
	  *(( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
	if (( buf = (char *)safe_realloc( buf, lmax )) == NULL ) {
	  perror( "safe_realloc" );
	  exit( 1 );
	}
      }
      strcpy( buf + lcur, line );
      lcur += len;
    } 
    else {
      if ( buf == NULL )
	continue; /* 1st line keep going */
      else
	break;
    }			
  }

  return buf;
}

/* Read binary data from a file. */
int fromfile(char *path, struct berval *bv) {
  FILE	*fp  = NULL;
  long	 rlen = 0;
  int	 eof  = 0;

  /* "r" changed to "rb", defect 39803. */
  if (( fp = fopen( path, "rb" )) == NULL ) {
    perror( path );
    return -1;
  }

  if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
    perror( path );
    fclose( fp );
    return -1;
  }

  bv->bv_len = ftell( fp );

  if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) {
    perror( "malloc" );
    fclose( fp );
    return -1;
  }

  if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
    perror( path );
    fclose( fp );
    return -1;
  }

  rlen = fread( bv->bv_val, 1, bv->bv_len, fp );
  eof = feof( fp );
  fclose( fp );

  if ( rlen != (bv->bv_len) ) {
    perror( path );
    return -1;
  }

  return bv->bv_len;
}

/* Read binary data from a file specified with a URL. */
int fromfile_url(char *value, struct berval *bv) {
  char *file = NULL;
  char *src  = NULL;
  char *dst  = NULL;

  if (strncmp(value, "file:///", 8)) 
    return -1;

  /* unescape characters */
  for (dst = src = &value[8]; (*src != '\0'); ++dst) {
    *dst = *src;
    if (*src++ != '%')
      continue;
    if ((*src >= '0') && (*src <= '9'))
      *dst = (*src++ - '0') << 4;
    else if ((*src >= 'a') && (*src <= 'f'))
      *dst = (*src++ - 'a' + 10) << 4;
    else if ((*src >= 'A') && (*src <= 'F'))
      *dst = (*src++ - 'A' + 10) << 4;
    else 
      return -1;
    if ((*src >= '0') && (*src <= '9'))
      *dst += (*src++ - '0');
    else if ((*src >= 'a') && (*src <= 'f'))
      *dst += (*src++ - 'a' + 10);
    else if ((*src >= 'A') && (*src <= 'F'))
      *dst += (*src++ - 'A'+ 10);
    else 
      return -1;
  }
  *dst = '\0';

  /* On WIN32 platforms the URL must begin with a drive letter.
     On UNIX platforms the initial '/' is kept to indicate absolute
     file path.
  */
#ifdef _WIN32
  file = value + 8;
#else
  file = value + 7;
#endif
  return fromfile(file, bv);
}

/* Add operation to the modify structure. */
void addmodifyop(LDAPMod ***pmodsp, int modop, char *attr, 
		 char *value, int vlen, int isURL, int isBase64)
{
  LDAPMod **pmods = NULL;
  int i = 0;
  int j = 0;
  struct berval *bvp = NULL;

  /* Data can be treated as binary (wire ready) if one of the
     following applies:
     1) it was base64 encoded
     2) charset is not defined
     3) read from an external file
  */
  if (isBase64 || 
      (charset == NULL) || 
      isURL || 
      ((value != NULL) && valsfromfiles && (*value == '/'))) {
    modop |= LDAP_MOD_BVALUES;
  }
  
  i = 0;
  pmods = *pmodsp;
  if ( pmods != NULL ) {
    for (; pmods[ i ] != NULL; ++i ) {
      if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 && 
	   pmods[ i ]->mod_op == modop ) {
	break;
      }
    }
  }
  
  if ( pmods == NULL || pmods[ i ] == NULL ) {
    if (( pmods = (LDAPMod * *)safe_realloc( pmods, (i + 2) * 
					     sizeof( LDAPMod * ))) == NULL ) {
      perror( "safe_realloc" );
      exit( 1 );
    }
    *pmodsp = pmods;
    pmods[ i + 1 ] = NULL;
    if (( pmods[ i ] = (LDAPMod * )calloc( 1, sizeof( LDAPMod ))) == NULL ) {
      perror( "calloc" );
      exit( 1 );
    }
    pmods[ i ]->mod_op = modop;
    if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
      perror( "strdup" );
      exit( 1 );
    }
  }
  
  if ( value != NULL ) {
    if (modop & LDAP_MOD_BVALUES) {
      j = 0;
      if ( pmods[ i ]->mod_bvalues != NULL ) {
	for (; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
	  ;
	}
      }
      if (( pmods[ i ]->mod_bvalues = 
	    (struct berval **)safe_realloc( pmods[ i ]->mod_bvalues,
					    (j + 2) * sizeof( struct berval *))) == NULL ) {
	perror( "safe_realloc" );
	exit( 1 );
      }

      pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
      if (( bvp = (struct berval *)malloc( sizeof( struct berval )))
	  == NULL ) {
	perror( "malloc" );
	exit( 1 );
      }
      pmods[ i ]->mod_bvalues[ j ] = bvp;

      /* get value from file */
      if ( valsfromfiles && *value == '/' ) {
	if (fromfile( value, bvp ) < 0 )
	  exit(1);
      } else if (isURL) {
	if (fromfile_url(value, bvp) < 0)
	  exit(1);
      } else {
	bvp->bv_len = vlen;
	if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
	  perror( "malloc" );
	  exit( 1 );
	}
	memmove( bvp->bv_val, value, vlen );
	bvp->bv_val[ vlen ] = '\0';
      }
    } else {
      j = 0;
      if ( pmods[ i ]->mod_values != NULL ) {
	for ( ; pmods[ i ]->mod_values[ j ] != NULL; ++j ) {
	  ;
	}
      }
      if (( pmods[ i ]->mod_values = 
	    (char **)safe_realloc( pmods[ i ]->mod_values,
				   (j + 2) * sizeof( char *))) == NULL ) {
	perror( "safe_realloc" );
	exit( 1 );
      }
      pmods[ i ]->mod_values[ j + 1 ] = NULL;
      if (( pmods[ i ]->mod_values[ j ] = strdup( value )) == NULL) {
	perror( "strdup" );
	exit( 1 );
      }
    }
  }
}

/* Delete record */
int dodelete( char *dn ) {
  int rc = 0;

  printf( "%sdeleting entry %s\n", (!doit) ? "!" : "", dn );
  if (!doit)
    return LDAP_SUCCESS;

  rc = ldap_delete_ext( ld, dn,
			Server_Controls, 
			NULL, &Message_ID);
  if ( rc != LDAP_SUCCESS )
    ldap_perror( ld, "ldap_delete" );
  else 
    printf( "delete complete\n" );

  putchar('\n');
  /* Increment results to check after end transaction. */
  Num_Operations++;    
  return rc;
}

/* Copy or move an entry. */
int	domodrdn( char *dn, char *newrdn, int deleteoldrdn ) {
  int rc = 0; 

  printf( "%s%s %s to %s\n", ((!doit) ? "!" : ""), 
	  ((deleteoldrdn) ? "moving" : "copying"), dn, newrdn);
  if (!doit)
    return LDAP_SUCCESS;

  rc = ldap_rename( ld, dn, newrdn, NULL, deleteoldrdn,
		    Server_Controls , NULL, 
		    &Message_ID );
  if ( rc != LDAP_SUCCESS )
    ldap_perror( ld, "ldap_rename" );
  else
    printf( "rename operation complete\n" );
  putchar('\n');

  /* Increment the count of results to check after end transaction is sent */
  Num_Operations++; 
  return rc;
}

/* Print a binary value. If charset is not specified then check to
   see if string is printable anyway. */
void print_binary(struct berval *bval) {
  int i = 0; 
  int binary = 0;
	
  printf( "\tBINARY (%ld bytes) ", bval->bv_len);
  if (charset == NULL) {
    binary = 0;
    for (i = 0; (i < (bval->bv_len)) && (!binary); ++i)
      if (!isprint(bval->bv_val[i]))
	binary = 1;
    if (!binary)
      for (i = 0; (i < (bval->bv_len)); ++i)
	putchar(bval->bv_val[i]);
  }
  putchar('\n');
}

/* Modify or add an entry. */
int domodify( char *dn, LDAPMod **pmods, int newentry ) {
  int	i, j, op, rc;
  struct berval *bvp;

  if ( pmods == NULL ) {
    fprintf( stderr, "%s: no attributes to change or add (entry %s)\n",
	     prog, dn );
    return LDAP_PARAM_ERROR;
  }

  if ( verbose ) {
    for ( i = 0; pmods[ i ] != NULL; ++i ) {
      op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
      printf( "%s %s:\n", op == LDAP_MOD_REPLACE ? 
	      "replace" : op == LDAP_MOD_ADD ? 
	      "add" : "delete", pmods[ i ]->mod_type );
      if (pmods[i]->mod_op & LDAP_MOD_BVALUES) {
	if (pmods[ i ]->mod_bvalues != NULL) {
	  for (j = 0; pmods[i]->mod_bvalues[j] != NULL; ++j)
	    print_binary(pmods[i]->mod_bvalues[j]);
	}
      } else {
	if (pmods[i]->mod_values != NULL) {
	  for (j = 0; pmods[i]->mod_values[j] != NULL; ++j)
	    printf("\t%s\n", pmods[i]->mod_values[j]);
	}
      }
    }
  }

  if ( newentry )
    printf( "%sadding new entry %s as a transaction\n", (!doit) ? "!" : "", dn );
  else
    printf( "%smodifying entry %s as a transaction\n", (!doit) ? "!" : "", dn );
  if (!doit)
    return LDAP_SUCCESS;

  if ( newentry ) {
    rc = ldap_add_ext( ld, dn, pmods,
		       Server_Controls, NULL, 
		       &Message_ID);
  } else {
    rc = ldap_modify_ext( ld, dn, pmods,
			  Server_Controls, NULL, 
			  &Message_ID );
  }   
  if ( rc != LDAP_SUCCESS ) {
    ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
  } else if ( verbose ) {
    printf( "%s operation complete\n", newentry ? "add" : "modify" );
  }
  putchar( '\n' );

  /* Increment the count of results to check after end transaction is sent */
  Num_Operations++;
  return rc;
}

/* Process an ldif record. */
int process_ldif_rec(char *rbuf) {
  char *line     = NULL; 
  char *dn       = NULL; 
  char *type     = NULL; 
  char *value    = NULL; 
  char *newrdn   = NULL; 
  char *p        = NULL;
  int	is_url   = 0; 
  int   is_b64   = 0;
  int	rc       = 0;
  int   linenum  = 0; 
  int   vlen     = 0; 
  int   modop    = 0; 
  int   replicaport   = 0;
  int	expect_modop  = 0; 
  int   expect_sep    = 0; 
  int   expect_ct     = 0; 
  int   expect_newrdn = 0;
  int	expect_deleteoldrdn = 0; 
  int   deleteoldrdn  = 1;
  int	saw_replica   = 0; 
  int use_record      = force;
  int new_entry = (operation == LDAPMODIFY_ADD);
  int delete_entry = 0;
  int got_all = 0;
  LDAPMod **pmods = NULL;
  int version = 0;
  int str_rc  = 0;

  while ( rc == 0 && ( line = str_getline( &rbuf )) != NULL ) {
    ++linenum;
    
    /* Is this a separator line ("-")? */
    if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
      /* If modifier has not been added yet then go ahead and add 
	 it. The can happen on sequences where there are no 
	 attribute values, such as:
	 DELETE: title
	 -
      */
      if (value != NULL)
	addmodifyop(&pmods, modop, value, NULL, 0, 0, 0);
      value = NULL;
      expect_sep = 0;
      expect_modop = 1;
      continue;
    }

    str_rc = str_parse_line_v_or_bv(line, &type, &value, &vlen, 1, &is_url, 
			&is_b64);
    if ((strncmp(type,"changes",7))==0)
      {str_parse_line_v_or_bv(value, &type, &value, &vlen, 1, &is_url, &is_b64);}
    if ((linenum == 1) && (strcmp(type, "version") == 0)) {
      version = atoi(value);
      continue;
    }
	
    if ((linenum == 2) && (version == 1) && 
	(strcmp(type, "charset") == 0)) {
      if (charset != NULL)
	free(charset);
      charset = strdup(value);
      if ((rc = ldap_set_iconv_local_charset(charset)) != LDAP_SUCCESS) {
	fprintf(stderr, "unsupported charset %s\n", charset);
	break;
      }
      ldap_set_option(ld, LDAP_OPT_UTF8_IO, (void *)LDAP_UTF8_XLATE_ON);
      continue;
    }
    
    if ( dn == NULL ) {
      if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
	++saw_replica;
	if (( p = strchr( value, ':' )) == NULL ) {
	  replicaport = LDAP_PORT;
	} else {
	  *p++ = '\0';
	  replicaport = atoi( p );
	}
	if ( strcasecmp( value, ldaphost ) == 0 && 
	     replicaport == ldapport ) {
	  use_record = 1;
	}
      } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
	if (( dn = strdup( value )) == NULL ) {
	  perror( "strdup" );
	  exit( 1 );
	}
	expect_ct = 1;
      }
      continue;	/* skip all lines until we see "dn:" */
    }
    
    if ( expect_ct ) {
      expect_ct = 0;
      if ( !use_record && saw_replica ) {
	printf( "%s: skipping change record for entry: %s\n\t(LDAP host/port does 
        not match replica: lines)\n", prog, dn );
	free( dn );
	return 0;
      }

      /* this is an ldif-change-record */
      if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
	if ( strcasecmp( value, T_MODIFYCTSTR ) == 0 ) {
	  new_entry = 0;
	  expect_modop = 1;
	} else if ( strcasecmp( value, T_ADDCTSTR ) == 0 ) {
	  modop = LDAP_MOD_ADD;
	  new_entry = 1;
	} else if ( strcasecmp( value, T_MODRDNCTSTR ) == 0 ) {
	  expect_newrdn = 1;
	} else if ( strcasecmp( value, T_DELETECTSTR ) == 0 ) {
	  got_all = delete_entry = 1;
	} else {
	  fprintf( stderr,
		   "%s:  unknown %s \"%s\" (line %d of entry: %s)\n",
		   prog, T_CHANGETYPESTR, value, linenum, dn );
	  rc = LDAP_PARAM_ERROR;
	}
	continue;

	/* this is an ldif-attrval-record */
      } else {
	if (operation == LDAPMODIFY_ADD) {
	  new_entry = 1;
	  modop = LDAP_MOD_ADD;
	} else
	  modop = LDAP_MOD_REPLACE;
      }
    }

    if (expect_modop) {
      expect_modop = 0;
      expect_sep = 1;
      if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
	modop = LDAP_MOD_ADD;
	continue;
      } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
	modop = LDAP_MOD_REPLACE;
	continue;
      } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
	modop = LDAP_MOD_DELETE;
	continue;
      } else {
	fprintf(stderr,
		"%s: unknown mod_spec \"%s\" (line %d of entry: %s)\n",
		prog, type, linenum, dn);
	rc = LDAP_PARAM_ERROR;
	continue;
      }
    }

    if ( expect_newrdn ) {
      if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
	if (( newrdn = strdup( value )) == NULL ) {
	  perror( "strdup" );
	  exit( 1 );
	}
	expect_deleteoldrdn = 1;
	expect_newrdn = 0;
      } else {
	fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
		 prog, T_NEWRDNSTR, type, linenum, dn );
	rc = LDAP_PARAM_ERROR;
      }
    } else if ( expect_deleteoldrdn ) {
      if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
	deleteoldrdn = ( *value == '0' ) ? 0 : 1;
	got_all = 1;
      } else {
	fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
		 prog, T_DELETEOLDRDNSTR, type, linenum, dn );
	rc = LDAP_PARAM_ERROR;
      }
    } else if ( got_all ) {
      fprintf( stderr, "%s: extra lines at end (line %d of entry %s)\n",
	       prog, linenum, dn );
      rc = LDAP_PARAM_ERROR;
    } else {
			
      addmodifyop(&pmods, modop, type, value, vlen, is_url, is_b64);
      type = NULL;
      value = NULL;
    }
  }

  /* If last separator is missing go ahead and handle it anyway, even
     though it is technically invalid ldif format. */
  if (expect_sep && (value != NULL))
    addmodifyop(&pmods, modop, value, NULL, 0, 0, 0);

  if ( rc == 0 ) {
    if (delete_entry)
      rc = dodelete( dn );
		
    else if (newrdn != NULL)
      rc = domodrdn( dn, newrdn, deleteoldrdn );
    else if (dn != NULL)
      rc = domodify( dn, pmods, new_entry );
  }

  if (dn != NULL)
    free( dn );
  if ( newrdn != NULL )
    free( newrdn ); 
  if ( pmods != NULL )
    ldap_mods_free( pmods, 1 );

  return rc;
}

/* Process a mod record. */
int process_ldapmod_rec( char *rbuf ) {
  char *line      = NULL; 
  char *dn        = NULL;
  char *p         = NULL; 
  char *q         = NULL; 
  char *attr      = NULL; 
  char *value     = NULL;
  int	rc        = 0; 
  int   linenum   = 0; 
  int   modop     = 0;
  LDAPMod **pmods = NULL;

  while ( rc == 0 && rbuf != NULL && *rbuf != '\0' ) {
    ++linenum;
    if (( p = strchr( rbuf, '\n' )) == NULL ) {
      rbuf = NULL;
    } else {
      if ( *(p - 1) == '\\' ) {	/* lines ending in '\' are continued */
	strcpy( p - 1, p );
	rbuf = p;
	continue;
      }
      *p++ = '\0';
      rbuf = p;
    }

    if ( dn == NULL ) {	/* first line contains DN */
      if (( dn = strdup( line )) == NULL ) {
	perror( "strdup" );
	exit( 1 );
      }
    } else {
      if (( p = strchr( line, '=' )) == NULL ) {
	value = NULL;
	p = line + strlen( line );
      } else {
	*p++ = '\0';
	value = p;
      }

      for ( attr = line; *attr != '\0' && isspace( *attr ); ++attr ) {
	;	/* skip attribute leading white space */
      }

      for ( q = p - 1; q > attr && isspace( *q ); --q ) {
	*q = '\0';	/* remove attribute trailing white space */
      }

      if ( value != NULL ) {
	while ( isspace( *value )) {
	  ++value;		/* skip value leading white space */
	}
	for ( q = value + strlen( value ) - 1; q > value && 
		isspace( *q ); --q ) {
	  *q = '\0';	/* remove value trailing white space */
	}
	if ( *value == '\0' ) {
	  value = NULL;
	}
      }

      if ((value == NULL) && (operation == LDAPMODIFY_ADD)) {
	fprintf( stderr, "%s: missing value on line %d (attr is %s)\n",
		 prog, linenum, attr );
	rc = LDAP_PARAM_ERROR;
      } else {
	switch ( *attr ) {
	case '-':
	  modop = LDAP_MOD_DELETE;
	  ++attr;
	  break;
	case '+':
	  modop = LDAP_MOD_ADD;
	  ++attr;
	  break;
	default:
	  modop = (operation == LDAPMODIFY_REPLACE) 
	    ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
	  break;
	}

	addmodifyop( &pmods, modop, attr, value,
		     ( value == NULL ) ? 0 : strlen( value ), 0, 0);
      }
    }
    line = rbuf;
  }

  if ( rc == 0 ) {
    if ( dn == NULL )
      rc = LDAP_PARAM_ERROR;
    else 
      rc = domodify(dn, pmods, (operation == LDAPMODIFY_ADD));
  }

  if ( pmods != NULL )
    ldap_mods_free( pmods, 1 );
  if ( dn != NULL )
    free( dn );

  return rc;
}

main( int argc, char **argv ) {   
  char *rbuf = NULL;
  char *start = NULL;
  char *p = NULL;
  char *q = NULL;
  char *tmpstr = NULL;
  int	rc = 0; 
  int   i = 0;
  int   use_ldif = 0;
  int   num_checked = 0;
  char  *Start_Transaction_OID      = LDAP_START_TRANSACTION_OID;
  char  *End_Transaction_OID        = LDAP_END_TRANSACTION_OID;
  char  *Control_Transaction_OID    = LDAP_TRANSACTION_CONTROL_OID;
  char  *Returned_OID               = NULL;
  struct berval *Returned_BerVal    = NULL;
  struct berval Request_BerVal      = {0,0};
  char  *Berval                     = NULL;
  LDAPMessage *LDAP_result          = NULL;

  /* Strip off any path info on program name */
#if defined( _WIN32 )
  if ((prog = strrchr(argv[0], '\\')) != NULL)	
    ++prog;
  else
    prog = argv[0];
#else
  if (prog = strrchr(argv[0], '/'))
    ++prog;
  else
    prog = argv[0];
#endif

#if defined( _WIN32 )
  /* Convert string to lowercase */
  for (i = 0; prog[i] != '\0'; ++i)
    prog[i] = tolower(prog[i]);

  /* Strip ending .exe from program name */
  if ((tmpstr = strstr(prog, ".exe")) != NULL)
    *tmpstr = '\0';
#endif
  if ( strcmp( prog, "ldaptxadd" ) == 0 )
    operation = LDAPMODIFY_ADD;
  
  /* Parse command line arguments. */
  parse_arguments(argc, argv);

  /* Connect to server. */
  if (doit)
    connect_to_server();

  /* Disable translation if reading from file (they must specify the
     translation in the file). */
  if (fp != stdin)
    ldap_set_option(ld, LDAP_OPT_UTF8_IO, (void *)LDAP_UTF8_XLATE_OFF);

  /* Do the StartTransaction extended operation.
     The transaction ID returned must be put into the server control 
     sent with all update operations. */
  rc = ldap_extended_operation_s ( ld, Start_Transaction_OID, 
				   &Request_BerVal, NULL, NULL,
				   &Returned_OID, 
				   &Returned_BerVal);
  if (verbose) {
    printf("ldap_extended_operation(start transaction) RC=%d\n", rc);
  }

  if ( rc != LDAP_SUCCESS) {
    fprintf(stderr, "Start transaction rc=%d -> %s\n", 
	    rc, ldap_err2string(rc));
    exit( rc );
  }

  /* Allocate the server control for transactions. */
  if (( Server_Controls[0] = 
	(LDAPControl *)malloc( sizeof( LDAPControl ))) == NULL ) {
    perror("malloc");
    exit( 1 );
  }

  /* Allocate the server control's berval. */
  if ((Server_Controls[0]->ldctl_value.bv_val = 
       (char *) calloc (1, Returned_BerVal->bv_len + 1)) == NULL) {
    perror("calloc");
    exit(1);
  }

  /* Copy the returned berval length and value into the server control */
  Server_Controls[0]->ldctl_value.bv_len = Returned_BerVal-> bv_len;   
  memcpy(Server_Controls[0]->ldctl_value.bv_val, 
	 Returned_BerVal->bv_val , Returned_BerVal->bv_len);

  /* Set the control type to Transaction_Control_OID */
  Server_Controls[0]->ldctl_oid = Control_Transaction_OID;

  /* Set the criticality in the control to TRUE */
  Server_Controls[0]->ldctl_iscritical = LDAP_OPT_ON;

  /* If referral objects are to be modified directly, */
  if (manageDsa == LDAP_TRUE) {
    /* then set that server control as well. */
    Server_Controls[1] = &manageDsaIT
  }

  /* Initialize the count of operations that will be in the transaction. 
     This count will be incremented by each operation that is performed. 
     The count will be the number of calls that must be made to ldap_result 
     to get the results for the operations.
  */
  Num_Operations = 0;
	
  /* Do operations */
  rc = 0;
  while ((rc == 0 || contoper) && (rbuf = read_one_record( fp )) != NULL ) {
    /* We assume record is ldif/slapd.replog if the first line
       has a colon that appears to the left of any equal signs, OR
       if the first line consists entirely of digits (an entry ID). */
    
    use_ldif=1;
    start = rbuf;
	  
    if ( use_ldif )
      rc = process_ldif_rec( start );
    else
      rc = process_ldapmod_rec( start );
    free( rbuf );
  }

  /* Finish the transaction, committing or rolling back based on input parameter. */
  rc = 0;
  Request_BerVal.bv_len = Returned_BerVal->bv_len + 1;
  if ((Berval = 
       ( char *) malloc (Returned_BerVal->bv_len + 1)) == NULL) {
    perror("malloc");
    exit(1);
  }
	
  memcpy (&Berval[1], Returned_BerVal->bv_val, Returned_BerVal->bv_len); 
  Berval[0] = abort_flag ? '\1' : '\0';
  Request_BerVal.bv_val = Berval;

  rc = ldap_extended_operation_s ( ld, 
				   End_Transaction_OID, 
					&Request_BerVal, NULL, NULL,
				   &Returned_OID, 
				   &Returned_BerVal);
  if (verbose) {
    printf("ldap_extended_operation(end transaction) RC=%d\n", rc);
  }
  
  if ( rc != LDAP_SUCCESS) {
    fprintf(stderr, "End transaction rc=%d -> %s\n", 
	    rc, ldap_err2string(rc));
    exit( rc );
  }
	
  /* Process the results of the operations in the transaction. 
     At this time we will not be concerned about the correctness 
     of the message numbers, just whether the operations succceeded or not.
     We could keep track of the operation types and make sure they are all
     accounted for. */

  for ( num_checked = 0; num_checked < Num_Operations; num_checked++ ) {
    if (verbose) {
      printf("processing %d of %d operation results\n", 
	     1 + num_checked, Num_Operations);
    }

    rc = ldap_result (ld , LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &LDAP_result);
    if ( rc <= 0) {
      if (rc == 0)
	fprintf(stderr, "Operation %d timed out\n", num_checked);
      if (rc < 0 )
	fprintf(stderr, "Operation %d failed\n", num_checked);
      exit( 1 );
    }   
  }

  /* Unbind and exit */
  if (doit)
    ldap_unbind(ld);

  exit(0);
}

The following example shows the makefile:

#-----------------------------------------------------------------------------
# COMPONENT_NAME: examples
#
# ABSTRACT: makefile to generate LDAP client programs for transactions 
#
# ORIGINS: 202,27
#
# (C) COPYRIGHT International Business Machines Corp. 2002
# All Rights Reserved
# Licensed Materials - Property of IBM
#
# US Government Users Restricted Rights - Use, duplication or
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
#
#############################################################################
# Default definitions
#############################################################################
CC	= cl.exe
LD      = link.exe
RM	= erase /f 
HARDLN	= copy
### Note: Your install path may be different 
LDAPHOME = D:\Program Files\IBM\ldap\V10.0.4

#############################################################################
# General compiler options                                                
#############################################################################

DEFINES	= /DNDEBUG /DWIN32 /D_CONSOLE /D_MBCS /DNT /DNEEDPROTOS
INCLUDES= /I"$(LDAPHOME)/include"
CFLAGS	= /nologo /MD /GX /Z7 $(INCLUDES) $(DEFINES)

#############################################################################
# General linker options                                                  
#############################################################################

LIBS    = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
 advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
 odbccp32.lib wsock32.lib

# Use the following definition to link the sample programs statically.
#CLIENT_LIBS = ldapstatic.lib libidsldifstatic.lib setloci.lib iconvi.lib

# Use the following definition to link the sample programs with
# the LDAP shared library.  
CLIENT_LIBS = ldap.lib libldif.lib setloci.lib
LDIR	= /LIBPATH:"$(LDAPHOME)"/lib
LFLAGS  = /nologo /subsystem:console /incremental:no  \
	$(LDIR) $(LIBS) $(CLIENT_LIBS)

#############################################################################
# Targets                                                                 
#############################################################################

all: ldaptxmod.exe ldaptxadd.exe

ldaptxmod.exe: ldaptxmod.obj 
	$(LD) $(LFLAGS) /out:$@ $**

ldaptxadd.exe:	ldaptxmod.exe
	$(RM) $@ 
	$(HARDLN) ldaptxmod.exe ldaptxadd.exe

.c.obj::
   $(CC) $(CFLAGS) /c $<

ldaptxmod.obj: ldaptxmod.c

clean:	
	$(RM) ldaptxmod.exe ldaptxadd.exe ldaptxmod.obj 

See the source file, ldapmodify.c, in the DS_INSTALL_ROOT/examples for more information about transaction.