### The teldir.c Code

The following is a listing of the file teldir.c:

/*

* This sample program behaves like a simple telephone directory.

* It permits a user to add, read or delete entries in a GDS

* namespace or to a CDS namespace in any local or remote DCE cell

* (assuming that permissions are granted by the ACLs).

*

* Each entry is of class Organizational-Person and simply contains

* a person's surname and their phone number.

*

* The addition of an entry is followed by a read to verify that the

* information was entered properly.

*

* All valid names should begin with one of the following symbols:

* /... Fully qualified name (from global root).

* such as /.../C=de/O=sni/OU=ap/CN=klaus

*

* /.: Partially qualified name (from local cell root).

* such as /.:/brad/sni/com

* This program demonstrates the following techniques:

* - Using completely static XDS public objects (predefined at the top

* of the program and never altered). See xdsObjectClass,

* xdsAttributesToAdd, and xdsAttributeSelection below.

* - Using partially static XDS public objects (predefined at the top

* of the program but altered later). See xdsSurname and xdsPhoneNum

* below. See also the macros whose names begin with "FILL_OMD_".

* - Using dynamic XDS public objects (created and filled in only as

* needed). See the function stringToXdsName() below.

* - Parsing DCE-style names and converting them into XDS objects. See

* the function stringToXdsName() below.

* - Getting the value of an attribute from an object read from the

* namespace using ds_read(). See the function extractValue() below.

* - Getting the numeric value of an error (type DS_status) returned by

* one of the XDS calls. See the function handleDSError() below.

*/

#ifdef THREADSAFE

#include <pthread.h>

#endif

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <xom.h>

#include <xds.h>

#include <xdsbdcp.h>

#include <xdsgds.h>

#include <xdscds.h>

OM_EXPORT( DS_A_COMMON_NAME )

OM_EXPORT( DS_A_COUNTRY_NAME )

OM_EXPORT( DS_A_LOCALITY_NAME )

OM_EXPORT( DS_A_OBJECT_CLASS )

OM_EXPORT( DS_A_ORG_UNIT_NAME )

OM_EXPORT( DS_A_ORG_NAME )

OM_EXPORT( DS_A_SURNAME )

OM_EXPORT( DS_A_PHONE_NBR )

OM_EXPORT( DS_A_TITLE )

OM_EXPORT( DS_C_ATTRIBUTE )

OM_EXPORT( DS_C_ATTRIBUTE_LIST )

OM_EXPORT( DS_C_AVA )

OM_EXPORT( DS_C_DS_DN )

OM_EXPORT( DS_C_DS_RDN )

OM_EXPORT( DS_C_ENTRY_INFO_SELECTION )

OM_EXPORT( DS_O_ORG_PERSON )

OM_EXPORT( DS_O_PERSON )

OM_EXPORT( DS_O_TOP )

OM_EXPORT( DSX_TYPELESS_RDN ) /* For "typeless" pieces of a name, as */

/* found in cells with bind-style names*/

/* and in the CDS namespace. */

#define MAX_NAME_LEN 1024

/* These values can be found in */

/* the "Directory Class Definitions" chapter. */

/* (One byte must be added for the null terminator.) */

#define MAX_PHONE_LEN 33

#define MAX_SURNAME_LEN 66

/*********************************************************************

* Macros for help filling in static XDS objects.

*********************************************************************/

/* Put NULL value (equivalent to OM_NULL_DESCRIPTOR) in object */

#define FILL_OMD_NULL( desc, index )

desc[index].type = OM_NO_MORE_TYPES;

desc[index].syntax = OM_S_NO_MORE_SYNTAXES;

desc[index].value.object.padding = 0;

desc[index].value.object.object = OM_ELEMENTS_UNSPECIFIED;

/* Put C-style (null-terminated) string in object */

#define FILL_OMD_STRING( desc, index, typ, syntx, val )

desc[index].type = typ;

desc[index].syntax = syntx;

desc[index].value.string.length = (OM_string_length)

strlen( val );

desc[index].value.string.elements = val;

/* Put XOM string in object */

#define FILL_OMD_XOM_STRING( desc, index, typ, syntx, val )

desc[index].type = typ;

desc[index].syntax = syntx;

desc[index].value.string.length = val.length;

desc[index].value.string.elements = val.elements;

/* Put other value in object */

#define FILL_OMD_STRUCT( desc, index, typ, syntx, val )

desc[index].type = typ;

desc[index].syntax = syntx;

desc[index].value.object.padding = 0;

desc[index].value.object.object = val;

/*********************************************************************

* Static XDS objects.

********************************************************************/

/*

* To identify which packages we need for this program. We only need

* the basic package because we are not doing anything fancy with

* session parameters, etc.

*/

DS_feature featureList[] = {

{ OM_STRING(OMP_O_DS_BASIC_DIR_CONTENTS_PKG), OM_TRUE },

{ 0 }

};

/*

* To hold the attributes we want to attach to the name being added.

* One attribute is the class of the object (DS_O_ORG_PERSON), the

* rest of the attributes are the surname (required for all objects

* of class DS_O_ORG_PERSON) and phone number. In addition, we need

* an object to hold all this information to pass it

* into ds_add_entry().

*/

static OM_descriptor xdsObjectClass[] = {

/* This object is an attribute -- an object class. */

OM_OID_DESC( OM_CLASS, DS_C_ATTRIBUTE ),

OM_OID_DESC( DS_ATTRIBUTE_TYPE, DS_A_OBJECT_CLASS ),

/* Not only must the class be listed, but also all */

/* its superclasses. */

OM_OID_DESC( DS_ATTRIBUTE_VALUES, DS_O_TOP ),

OM_OID_DESC( DS_ATTRIBUTE_VALUES, DS_O_PERSON ),

OM_OID_DESC( DS_ATTRIBUTE_VALUES, DS_O_ORG_PERSON ),

/* Null terminator */

OM_NULL_DESCRIPTOR

};

static OM_descriptor xdsSurname[] = {

/* This object is an attribute -- a surname. */

OM_OID_DESC( OM_CLASS, DS_C_ATTRIBUTE ),

OM_OID_DESC( DS_ATTRIBUTE_TYPE, DS_A_SURNAME ),

/* No default -- so we need a placeholder for the actual surname. */

OM_NULL_DESCRIPTOR,

/* Null terminator */

OM_NULL_DESCRIPTOR

};

static OM_descriptor xdsPhoneNum[] = {

/* This object is an attribute -- a telephone number. */

OM_OID_DESC( OM_CLASS, DS_C_ATTRIBUTE ),

OM_OID_DESC( DS_ATTRIBUTE_TYPE, DS_A_PHONE_NBR ),

/* By default, phone numbers are unlisted. If the user specifies */

/* an actual phone number, it will go into this position. */

{ DS_ATTRIBUTE_VALUES, OM_S_PRINTABLE_STRING,

OM_STRING( "unlisted" ) },

/* Null terminator */

OM_NULL_DESCRIPTOR

};

static OM_descriptor xdsAttributesToAdd[] = {

/* This object is an attribute list. */

OM_OID_DESC( OM_CLASS, DS_C_ATTRIBUTE_LIST ),

/* These are "pointers" to the attributes in the list. */

{ DS_ATTRIBUTES, OM_S_OBJECT, { 0, xdsObjectClass } },

{ DS_ATTRIBUTES, OM_S_OBJECT, { 0, xdsSurname } },

{ DS_ATTRIBUTES, OM_S_OBJECT, { 0, xdsPhoneNum } },

/* Null terminator */

OM_NULL_DESCRIPTOR

};

/*

* To hold the list of attributes we want to read.

*/

static OM_descriptor xdsAttributeSelection[] = {

/* This is an entry information selection. */

OM_OID_DESC( OM_CLASS, DS_C_ENTRY_INFO_SELECTION ),

/* No, we don't want all attributes. */

{ DS_ALL_ATTRIBUTES, OM_S_BOOLEAN, OM_FALSE },

/* These are the ones we want to read. */

OM_OID_DESC( DS_ATTRIBUTES_SELECTED, DS_A_SURNAME ),

OM_OID_DESC( DS_ATTRIBUTES_SELECTED, DS_A_PHONE_NBR ),

/* Give us both the types and their values. */

{ DS_INFO_TYPE, OM_S_ENUMERATION, { DS_TYPES_AND_VALUES, NULL } },

/* Null terminator */

OM_NULL_DESCRIPTOR

};

/*********************************************************************

* dce_cf_get_cell_name()

* Use this dummy function if CDS is not available.

********************************************************************/

void

dce_cf_get_cell_name(

char ** cellname,

unsigned long * status

)

{

fprintf( stderr, "CDS unavailable: dce_cf_get_cell_name() error\n" );

*status = 1;

} /* end dce_cf_get_cell_name() */

/*********************************************************************

* showUsage()

* Display "usage" information.

********************************************************************/

void

showUsage(

char * cmd /* In -- Name of command being called */

)

{

fprintf( stderr, "\nusage: %s [option]\n\n", cmd );

fprintf( stderr, "option: -a : add an entry\n" );

fprintf( stderr, " -r : read an entry\n" );

fprintf( stderr, " -d : delete an entry\n" );

} /* end showUsage() */

/*********************************************************************

* numNamePieces()

* Returns the number of pieces in a string name.

********************************************************************/

int

numNamePieces(

char * string /* In -- String whose pieces are to be counted*/

)

{

int count; /* Number of pieces found */

char * currSep; /* Pointer to separator between pieces */

if( string == NULL ) /* If nothing there, no pieces */

return( 0 );

count = 1; /* Otherwise, there's at least one */

/*

* If the first character is a /, it's not really separating

* two pieces so we want to ignore it here.

*/

if( *string == '/' )

currSep = string + 1;

else

currSep = string;

/* How many pieces are there? */

while( (currSep = strchr( currSep, '/' )) != NULL ) {

count++;

currSep++; /* Begin at next character */

}

return( count );

} /* end numNamePieces() */

/*********************************************************************

* splitNamePiece()

* Divides a piece of a name (string) into its XDS attribute type

* and value.

********************************************************************/

void

splitNamePiece(

char * string, /* In -- String to be broken down */

OM_string * type, /* Out -- XDS type of this piece of the name */

char ** value /* Out -- Pointer to beginning of the value */

) /* part of string */

{

char * equalSign; /* Location of the = within string */

/*

* If the string contains an equal sign, this is probably a

* typed name. Check for all the attribute types allowed by

* the default schema.

*/

if( (equalSign = strchr( string, '=' )) != NULL ) {

*value = equalSign + 1;

if(( strncmp( string, "C=", 2 ) == 0 ) ||

( strncmp( string, "c=", 2 ) == 0 ))

*type = DS_A_COUNTRY_NAME;

else if(( strncmp( string, "O=", 2 ) == 0 ) ||

( strncmp( string, "o=", 2 ) == 0 ))

*type = DS_A_ORG_NAME;

else if(( strncmp( string, "OU=", 3 ) == 0 ) ||

( strncmp( string, "ou=", 3 ) == 0 ))

*type = DS_A_ORG_UNIT_NAME;

else if(( strncmp( string, "LN=", 3 ) == 0 ) ||

( strncmp( string, "ln=", 3 ) == 0 ))

*type = DS_A_LOCALITY_NAME;

else if(( strncmp( string, "CN=", 3 ) == 0 ) ||

( strncmp( string, "cn=", 3 ) == 0 ))

*type = DS_A_COMMON_NAME;

/*

* If this did not appear to be a type allowed by the

* default schema, consider the whole string as the

* value (whose type is "typeless").

*/

else {

*type = DSX_TYPELESS_RDN;

*value = string;

}

}

/*

* If the string does not contain an equal sign, this is a

* typeless name.

*/

else {

*type = DSX_TYPELESS_RDN;

*value = string;

}

} /* end splitNamePiece() */

/*********************************************************************

* extractValue()

* Pulls the value of a particular attribute from a private object

* that was received using ds_read().

* Returns:

* OM_SUCCESS If successful.

* OM_NO_SUCH_OBJECT If no values for the attribute

* were found.

* other Any value returned by one of the

* om_get() calls.

*********************************************************************/

OM_return_code

extractValue(

OM_private_object object, /* In -- Object to extract from */

OM_string * attribute, /* In -- Attribute to extract */

char * value /* Out -- Value found */

)

{

OM_public_object attrList;

OM_public_object attrType;

OM_public_object attrValue;

OM_public_object entry;

int i;

OM_return_code omStatus;

OM_value_position total;

OM_value_position totalAttributes;

OM_type xdsIncludedTypes[] = { 0, /* Place holder */

0 }; /* Null terminator*/

/*

* Get the entry from the object returned by ds_read().

*/

xdsIncludedTypes[0] = DS_ENTRY;

omStatus = om_get( object, /* Object to extract from */

OM_EXCLUDE_ALL_BUT_THESE_TYPES+OM_EXCLUDE_SUBOBJECTS,

/* Only want what is in */

/* xdsIncludedTypes, don't*/

/* include subobjects */

xdsIncludedTypes, /* What to get */

OM_FALSE, /* Currently ignored */

OM_ALL_VALUES, /* Start with first value */

OM_ALL_VALUES, /* End with last value */

&entry, /* Put the entry here */

&total ); /* Put number of attribut */

/* descriptors here */

if( omStatus != OM_SUCCESS ) {

fprintf( stderr, "om_get( entry ) returned error %d\n",

omStatus );

return( omStatus );

}

if( total <= 0 ) { /* Make sure something was returned */

fprintf( stderr,

"Number of descriptors returned by om_get( entry )

was %d\n", total );

return( OM_NO_SUCH_OBJECT );

}

/*

* Get the attribute list from the entry.

*/

xdsIncludedTypes[0] = DS_ATTRIBUTES;

omStatus = om_get( entry->value.object.object,

OM_EXCLUDE_ALL_BUT_THESE_TYPES+OM_EXCLUDE_SUBOBJECTS,

xdsIncludedTypes, OM_FALSE, OM_ALL_VALUES,

OM_ALL_VALUES, &attrList, &totalAttributes );

if( omStatus != OM_SUCCESS ) {

fprintf( stderr, "om_get( attrList ) returned error %d\n",

omStatus );

return( omStatus );

}

if( total <= 0 ) { /* Make sure something was returned */

fprintf( stderr,

"Number of descriptors returned by om_get( attrList )

was %d\n", total );

return( OM_NO_SUCH_OBJECT );

}

/*

* Search the list for the attribute with the proper type.

*/

for( i = 0; i < totalAttributes; i++ ) {

xdsIncludedTypes[0] = DS_ATTRIBUTE_TYPE;

omStatus = om_get( (attrList+i)->value.object.object,

OM_EXCLUDE_ALL_BUT_THESE_TYPES+OM_EXCLUDE_SUBOBJECTS,

xdsIncludedTypes, OM_FALSE, OM_ALL_VALUES,

OM_ALL_VALUES, &attrType, &total );

if( omStatus != OM_SUCCESS ) {

fprintf( stderr, "om_get( attrType ) [i = %d] returned

error %d\n", i, omStatus );

return( omStatus );

}

if( total <= 0 ) { /* Make sure something was returned */

fprintf( stderr,

"Number of descriptors returned by om_get( attrType )

[i = %d] was %d\n", i, total );

return( OM_NO_SUCH_OBJECT );

}

if( strncmp( attrType->value.string.elements,

attribute->elements,

attribute->length ) == 0 )

break; /* If we found a match, quit looking. */

}

if( i == totalAttributes ) { /* Verify that we found a match. */

fprintf( stderr,

"%s: extractValue() could not find requested attribute\n" );

return( OM_NOT_PRESENT );

}

/*

* Get the attribute value from the corresponding item in the

* attribute list.

*/

xdsIncludedTypes[0] = DS_ATTRIBUTE_VALUES;

omStatus = om_get( (attrList+i)->value.object.object,

OM_EXCLUDE_ALL_BUT_THESE_TYPES+OM_EXCLUDE_SUBOBJECTS,

xdsIncludedTypes, OM_FALSE, OM_ALL_VALUES,

OM_ALL_VALUES, &attrValue, &total );

if( omStatus != OM_SUCCESS ) {

fprintf( stderr, "om_get( attrValue ) returned error %d\n",

omStatus );

return( omStatus );

}

if( total <= 0 ) { /* Make sure something was returned */

fprintf( stderr,

"Number of descriptors returned by om_get( attrValue )

was %d\n", total );

return( OM_NO_SUCH_OBJECT );

}

/*

* Copy the value into the buffer for return to the caller.

*/

strncpy( value, attrValue->value.string.elements,

attrValue->value.string.length );

value[attrValue->value.string.length] = '\0';

/*

* Free up the resources we don't need any more and return.

*/

om_delete( attrValue );

om_delete( attrType );

om_delete( attrList );

om_delete( entry );

return( OM_SUCCESS );

} /* end extractValue() */

/**********************************************************************

* stringToXdsName()

* Converts a string that is a DCE name to an XDS name object (class

* DS_C_DS_DN). Returns one of the following:

* OM_SUCCESS If successful.

* OM_MEMORY_INSUFFICIENT If a malloc fails.

* OM_PERMANENT_ERROR If the name is not in a valid format.

* OM_SYSTEM_ERROR If the local cell's name cannot

* be determined.

*

* Technically, the space obtained here through malloc() needs

* to be returned to the system when it is no longer needed.

* If this was a more complex application, this function would

* probably malloc all the space it needs at once and require

* calling routines to free the space when finished with it.

*********************************************************************/

OM_return_code

stringToXdsName(

char * origString, /* In -- String name to be converted */

OM_object * xdsNameObj /* Out -- Pointer to XDS name object */

)

{

OM_descriptor * ava; /* DS_C_AVA object */

char * cellName; /* Name of this cell */

OM_object dsdn; /* DS_C_DS_DN object */

char * end; /* End of name piece */

int index; /* Index into DS_C_DS_DN object */

int numberOfPieces; /* Number of pieces in the name */

unsigned long rc; /* Return code for some functions*/

OM_descriptor * rdn; /* DS_C_RDN object */

char * start; /* Beginning of piece of name */

char * string; /* Copy of origString that we can use*/

OM_string type; /* Type of one piece of the name */

char * value; /* Piece of the name */

/*

* A DS_C_AVA object only contains pointers to the strings that

* represent the pieces of the name, not the contents of the

* strings themselves. So we'll make a copy of the string passed

* in to guarantee that these pieces survive in case the programmer

* alters or reuses the original string.

*

* In addition, all valid names should begin with one of the

* following symbols:

* /... Fully qualified name (from global root). For

* these, we need to ignore the /...

* /.: Partially qualified name (from local cell root).

* For these, we must replace the /.: with the name

* of the local cell name

* If we see anything else, we'll return with an error. (Notice

* that /: is a valid DCE name, but refers to the file system's

* namespace. Filenames cannot be accessed through

* CDS, GDS, or XDS.)

*/

if( strncmp( origString, "/.../", 5 ) == 0 ) {

string = (char *)malloc( strlen(origString+5) + 1 );

if( string == NULL ) /* malloc() failed */

return OM_MEMORY_INSUFFICIENT;

strcpy( string, origString+5 );

}

else if( strncmp( origString, "/.:/", 4 ) == 0 ) {

dce_cf_get_cell_name( &cellName, &rc );

if( rc != 0 ) /* Could not get cell name */

return OM_SYSTEM_ERROR;

/*

* The cell name will have /.../ on the front, so we will

* skip over it as we add it to the string (by always

* starting at its fifth character).

*/

string = (char *)malloc( strlen

(origString+4) + strlen(cellName+5) + 2 );

if( string == NULL ) /* malloc() failed */

return OM_MEMORY_INSUFFICIENT;

strcpy( string, cellName+5 );

strcat( string, "/" );

strcat( string, origString+4 );

}

else /* Invalid name format */

return OM_PERMANENT_ERROR;

/*

* Count the number of pieces in the name that will have to

* be dealt with.

*/

numberOfPieces = numNamePieces( string );

/*

* Allocate memory for the DS_C_DS_DN object. We will need an

* OM_descriptor for each name piece, one for the class

* identifier, and one for the null terminator.

*/

dsdn = (OM_object)malloc(

(numberOfPieces + 2) * sizeof(OM_descriptor) );

if( dsdn == NULL ) /* malloc() failed */

return OM_MEMORY_INSUFFICIENT;

/*

* Initialize it as a DS_C_DS_DN object by placing that class

* identifier in the first position.

*/

FILL_OMD_XOM_STRING( dsdn, 0, OM_CLASS,

OM_S_OBJECT_IDENTIFIER_STRING, DS_C_DS_DN )

/*

* For each piece of string, do the following:

* Break off the next piece of the string

* Build a DS_C_AVA object to show the type and value

* of this piece of the name

* Wrap the DS_C_AVA up in a DS_C_RDN object

* Add the DS_C_RDN to the DS_C_DS_DN object

*/

for( start=string, index=1 ; index <= numberOfPieces ;

index++, start=end+1 ) {

/*

* Find the next delimiter and replace it with a null byte

* so the piece of the name is effectively separated from

* the rest of the string.

*/

end = strchr( start, '/' );

if( end != NULL )

*end = '\0';

else /* If this is the last piece, there won't be */

/* a '/' at the end, just a null byte. */

end = strchr( start, '\0' );

/*

* Allocate space for a DS_C_AVA object and fill in its entries:

* DS_C_AVA class identifier

* AVA's type

* AVA's value

* null terminator

*/

ava = (OM_descriptor *)malloc( sizeof(OM_descriptor) * 4 );

if( ava == NULL ) /* malloc() failed */

return OM_MEMORY_INSUFFICIENT;

FILL_OMD_XOM_STRING( ava, 0, OM_CLASS,

OM_S_OBJECT_IDENTIFIER_STRING, DS_C_AVA )

splitNamePiece( start, &type, &value );

FILL_OMD_XOM_STRING( ava, 1, DS_ATTRIBUTE_TYPE,

OM_S_OBJECT_IDENTIFIER_STRING, type )

FILL_OMD_STRING( ava, 2, DS_ATTRIBUTE_VALUES,

OM_S_PRINTABLE_STRING, value )

FILL_OMD_NULL( ava, 3 )

/*

* Allocate space for a DS_C_RDN object and fill in its entries:

* DS_C_RDN class identifier

* AVA it contains

* null terminator

*/

rdn = (OM_descriptor *)malloc( sizeof(OM_descriptor) * 3 );

if( rdn == NULL ) /* malloc() failed */

return OM_MEMORY_INSUFFICIENT;

FILL_OMD_XOM_STRING( rdn, 0, OM_CLASS,

OM_S_OBJECT_IDENTIFIER_STRING, DS_C_DS_RDN )

FILL_OMD_STRUCT( rdn, 1, DS_AVAS, OM_S_OBJECT, ava )

FILL_OMD_NULL( rdn, 2 )

/* Add the DS_C_RDN object to the DS_C_DS_DN object. */

FILL_OMD_STRUCT( dsdn, index, DS_RDNS, OM_S_OBJECT, rdn )

}

/*

* Null terminate the DS_C_DS_DN, tell the calling routine

* where to find it, and return.

*/

FILL_OMD_NULL( dsdn, index )

*xdsNameObj = dsdn;

return( OM_SUCCESS );

} /* end stringToXdsName() */

/***********************************************************************

* handleDSError()

* Extracts the error number from a DS_status return code, prints it

* in an error message, then terminates the program.

**********************************************************************/

void

handleDSError(

char * header, /* In -- Name of function whose return code */

/* is being checked */

DS_status returnCode /* In -- Return code to be checked */

)

{

OM_type includeDSProblem[] = { DS_PROBLEM,

0 };

OM_return_code omStatus;

OM_public_object problem;

OM_value_position total;

/*

* A DS_status return code is an object. It will be one of the

* subclasses of the class DS_C_ERROR. What we want from it is

* the value of the attribute DS_PROBLEM.

*/

omStatus = om_get( returnCode,

OM_EXCLUDE_ALL_BUT_THESE_TYPES+OM_EXCLUDE_SUBOBJECTS,

includeDSProblem,

OM_FALSE,

OM_ALL_VALUES,

OM_ALL_VALUES,

&problem,

&total );

/*

* Make sure we successfully extracted the problem number and print

* the error message before quitting.

*/

if( (omStatus == OM_SUCCESS) && (total > 0) )

printf( "%s returned error %d\n", header,

problem->value.enumeration );

else

printf( "%s failed for unknown reason\n", header );

exit( 1 );

}

/********************************************************************

* Main program

*/

void

main(

int argc,

char * argv[]

)

{

DS_status dsStatus;

OM_sint invokeID;

char newName[MAX_NAME_LEN];

char newPhoneNum[MAX_PHONE_LEN];

char newSurname[MAX_SURNAME_LEN];

OM_return_code omStatus;

char phoneNumRead[MAX_PHONE_LEN];

int rc = 0;

OM_private_object readResult;

OM_private_object session;

char surnameRead[MAX_SURNAME_LEN];

OM_object xdsName;

OM_workspace xdsWorkspace;

int operation;

/* Step 1

*

* Examine command-line argument.

*/

operation = getopt( argc, argv, "rad" );

if ( (operation == '?') || (operation == EOF) ) {

showUsage( argv[0] );

exit( 1 );

}

/* Step 2

*

* Initialize the XDS workspace.

*/

xdsWorkspace = ds_initialize( );

if( xdsWorkspace == NULL ) {

fprintf( stderr, "ds_initialize() failed\n" );

exit( 1 );

}

/* Step 3

*

* Pull in the packages that contain the XDS features we need.

*/

dsStatus = ds_version( featureList, xdsWorkspace );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_version()", dsStatus );

/* Step 4

*

* Find out what name the user wants to use in the namespace and

* convert it to and XDS object. We do this conversion dynamically

* (not using static structures defined at the top of the program)

* because we don't know how long the name will be.

*/

switch( operation ) {

case 'r' :

printf( "What name do you want to read? " );

break;

case 'a' :

printf( "What name do you want to add? " );

break;

case 'd' :

printf( "What name do you want to delete? " );

break;

}

/* Step 5 */

gets( newName );

omStatus = stringToXdsName( newName, &xdsName );

if( omStatus != OM_SUCCESS ) {

fprintf( stderr, "stringToXdsName() failed with OM error %d\n",

omStatus );

exit( 1 );

}

if ( operation == 'a' ) {

/* add operation requires additional input */

/*

* Get the person's real name from the user and place it in

* the XDS object already defined at the top of the program

* (xdsSurname). We are requiring a name, so we will loop

* until we get one.

*/

do {

printf( "What is this person's surname? " );

gets( newSurname );

} while ( *newSurname == '\0' );

FILL_OMD_STRING( xdsSurname, 2, DS_ATTRIBUTE_VALUES,

OM_S_TELETEX_STRING, newSurname )

/*

* Get the person's phone number from the user and place it

* in the XDS object already defined at the top of the

* program (xdsPhoneNum). A phone number is not required,

* so if none is given we will use the default already

* stored in the structure.

*/

printf( "What is this person's phone number? " );

gets( newPhoneNum );

if( *newPhoneNum != '\0' ) {

FILL_OMD_STRING( xdsPhoneNum, 2, DS_ATTRIBUTE_VALUES,

OM_S_PRINTABLE_STRING, newPhoneNum )

}

}

/* Step 6

*

* Open the session with the namespace:

* bind (without credentials) to the default server.

*/

dsStatus = ds_bind( DS_DEFAULT_SESSION, xdsWorkspace, &session );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_bind()", dsStatus );

/* Step 7 */

switch( operation ) { /* perform the requested operation */

/*

* Add entry to the namespace. The xdsSurname and xdsPhoneNum

* objects are already contained within an attribute list object

* (xdsAttributesToAdd).

*/

case 'a' :

dsStatus = ds_add_entry( session, DS_DEFAULT_CONTEXT, xdsName,

xdsAttributesToAdd, &invokeID );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_add_entry()", dsStatus );

/* FALL THROUGH */

/*

* Read the entry of the name supplied.

*/

case 'r' :

dsStatus = ds_read( session, DS_DEFAULT_CONTEXT, xdsName,

xdsAttributeSelection, &readResult, &invokeID );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_read()", dsStatus );

/*

* Get each attribute from the object read and print them.

*/

omStatus = extractValue( readResult, &DS_A_SURNAME,

surnameRead );

if( omStatus != OM_SUCCESS ) {

printf( "** Surname could not be read\n" );

strcpy( surnameRead, "(unknown)" );

rc = 1;

}

omStatus = extractValue( readResult, &DS_A_PHONE_NBR,

phoneNumRead );

if( omStatus != OM_SUCCESS ) {

printf( "** Phone number could not be read\n" );

strcpy( phoneNumRead, "(unknown)" );

rc = 1;

}

printf( "The phone number for %s is %s.\n", surnameRead,

phoneNumRead );

break;

/*

* delete the entry from the namespace.

*/

case 'd' :

dsStatus = ds_remove_entry( session, DS_DEFAULT_CONTEXT,

xdsName, &invokeID );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_remove_entry()", dsStatus );

else

printf( "The entry has been deleted.\n" );

break;

}

/*

* Clean up and exit.

*/

/* Step 8 */

dsStatus = ds_unbind( session );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_unbind()", dsStatus );

/* Step 9 */

dsStatus = ds_shutdown( xdsWorkspace );

if( dsStatus != DS_SUCCESS )

handleDSError( "ds_shutdown()", dsStatus );

exit( rc );

} /* end main() */

##