!++			     MODULE libutil
!
!--

!++
! FACILITY:
!   LIBUTIL - Demonstration of using the TPU callable interface to access
! text libraries.
!
! ABSTRACT:
!   This module calls TPU and provides routines for accessing text libraries.
!
! ENVIRONMENT:
!   VAX/VMS
!
! AUTHOR: Barry Tannenbaum, CREATION DATE: November 19, 1985
!
! MODIFIED BY:
!
!--

MODULE libutil
	(
	ADDRESSING_MODE (EXTERNAL = GENERAL),
	IDENT = '1.0',
	MAIN = libutil_main
	) =

BEGIN

!
! TABLE OF CONTENTS:
!
FORWARD ROUTINE
    libutil_close,			! End access to a library entry
    libutil_fileio,			! Routine called by TPU to perform I/O
    libutil_get_record,			! Read a record from a library entry
    libutil_open,			! Access a library entry
    libutil_put_record;			! Write a record to a library entry

!
! INCLUDE FILES:
!
LIBRARY
    'sys$library:starlet';		! System macros and literals

LIBRARY
    'sys$library:xport';		! XPORT data structure macros

!
! Macros
!
MACRO
    init_dyndesc (d) =
	BEGIN
	LOCAL $d$: REF BLOCK [, BYTE];
	$d$ = d;
	$d$ [dsc$w_length] = 0;
	$d$ [dsc$b_class] = dsc$k_class_d;
	$d$ [dsc$b_dtype] = dsc$k_dtype_t;
	$d$ [dsc$a_pointer] = 0;
	END % ;

!
! Data structure declarations
!
$FIELD stream_fields =
    SET
    stream_file_id =	[$INTEGER],
    stream_allocation =	[$SHORT_INTEGER],
    stream_rat =	[$BYTE],
    stream_rfm =	[$BYTE],
    stream_file_name =	[$DESCRIPTOR (DYNAMIC)]
    TES;

LITERAL
    stream_bytes = $FIELD_SET_SIZE * %UPVAL,
    stream_size = $FIELD_SET_SIZE;

MACRO
    stream_block =
	BLOCK [stream_size]
	FIELD (stream_fields) % ;

$FIELD item_fields =
    SET
    item_length =	[$SHORT_INTEGER],
    item_code =		[$SHORT_INTEGER],
    item_buffer_addr =	[$ADDRESS],
    item_return_addr =	[$ADDRESS]
    TES;

LITERAL
    item_bytes = $FIELD_SET_SIZE * %UPVAL,
    item_size = $FIELD_SET_SIZE;

MACRO
    item_block =
	BLOCK [item_size]
	FIELD (item_fields) % ;

$FIELD library_data_fields =
    SET
    library_index =		[$INTEGER],
    library_old_rfa =		[$BYTES (8)],
    library_text_rfa =		[$BYTES (8)],
    library_access =		[$INTEGER],
    library_file_name =		[$DESCRIPTOR (DYNAMIC)],
    library_entry_name =	[$DESCRIPTOR (DYNAMIC)],
    library_lookup_status =	[$INTEGER]
    TES;

LITERAL
    library_data_bytes = $FIELD_SET_SIZE * %UPVAL,
    library_data_size = $FIELD_SET_SIZE;

MACRO
    library_data_block =
	BLOCK [library_data_size]
	FIELD (library_data_fields) % ;

!
! OWN STORAGE:
!
OWN
    library_id_vector:
	VECTOR [512]
	INITIAL (REP 512 OF (0));
!
! EXTERNAL REFERENCES:
!
EXTERNAL LITERAL
    tpu$k_access,			! Item code for access type
    tpu$k_close,			! Code to perform a close operation
    tpu$k_close_delete,			! Code to perform a close and delete operation
    tpu$k_filename,			! Item code for file name
    tpu$k_fileio,			! File I/O routine specified
    tpu$k_get,				! Code to perform a get operation
    tpu$k_input,			! Code for input file processing
    tpu$k_open,				! Code to perform an open operation
    tpu$k_options,			! TPU command line qualifiers present
    tpu$k_put,				! Code to perform a put operation
    tpu$k_sectionfile,			! Section file name specified
    tpu$m_section,			! Mask for section file present
    tpu$m_display,			! Mask for use display
    tpu$_failure,			! Invalid I/O code
    tpu$_success;			! Success status

EXTERNAL ROUTINE
    lbr$close,				! End access to library
    lbr$delete_data,			! Delete library entry
    lbr$get_record,			! Read record from library entry
    lbr$ini_control,			! Initialize librarian
    lbr$lookup_key,			! Position librarian to entry
    lbr$open,				! Access a library file
    lbr$put_end,			! Write an EOR for an entry
    lbr$put_record,			! Write a record to a library entry
    lbr$replace_key,			! Replace entry key
    lib$free_vm,			! Deallocate dynamic_memory,
    str$free1_dx,			! Deallocate dynamic string
    lib$get_vm,				! Allocate dynamic memory
    lib$scopy_r_dx,			! Copy a string to a descriptor
    tpu$cleanup,			! Run down TPU
    tpu$control,			! Run the editor
    tpu$execute_inifile,
    tpu$fileio,				! TPU's file I/O routines
    tpu$handler,			! TPU's signal handler
    tpu$initialize;			! Initialize TPU

%SBTTL 'INIT_CALLBACK - TPU initialization callback routine'

ROUTINE init_callback =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine is called by TPU to provide an itemlist of initialization
!   information.
!
! IMPLICIT INPUTS:
!
!	LIBUTIL_FILEIO - Routine to handle file I/O and redirect it to
!   a text library.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	The address of the initialization item list is returned.
!
!--

    BEGIN

    OWN
	io_routine_bpv:
	    VECTOR [2]
	    INITIAL (libutil_fileio, 0),
	item_list:
	    BLOCK [item_size * 3],
	end_of_list:			! Must come immediately after item list
	    INITIAL (0),
	options;

    KEYWORDMACRO
	item_init (item,
		   length = 4,
		   code,
		   buffer_addr = 0,
		   return_addr = 0) =
	    BEGIN

	    LOCAL
		$item$:
		    REF item_block;
	    $item$ = item;
	    $item$ [item_length] = length;
	    $item$ [item_code] = code;
	    $item$ [item_buffer_addr] = buffer_addr;
	    $item$ [item_return_addr] = return_addr;
	    END % ;
!
! Set up the item list
!
    item_init (ITEM = item_list,
	       CODE = tpu$k_options,
	       BUFFER_ADDR = options);
    item_init (ITEM = item_list + item_bytes,
	       CODE = tpu$k_sectionfile,
	       BUFFER_ADDR = UPLIT (%ASCII 'tpu$section'),
	       LENGTH = 11);
    item_init (ITEM = item_list + item_bytes * 2,
	       CODE = tpu$k_fileio,
	       BUFFER_ADDR = io_routine_bpv);

    options = tpu$m_section OR tpu$m_display;

    RETURN item_list;

    END;					! Routine init_callback

%SBTTL 'LIBUTIL_CLOSE - End access to a library entry'

ROUTINE libutil_close (stream, data) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine ends access to a library entry.
!
! FORMAL PARAMETERS:
!
!	STREAM - The file control block for the file we're dealing with.
!   Passed by reference.
!	DATA - The I/O operation dependent data.  For a CLOSE operation, this
!   parameter is not used.  Passed by reference.
!
! IMPLICIT INPUTS:
!
!	LIBRARY_ID_VECTOR - Vector that holds the address of the library data
!   block for the library entry being processed.
!
! SUBROUTINES CALLED:
!
!	LIB$FREE_VM (bytes, address) - Deallocates dynamic memory.
!	LBR$CLOSE (library_index) - Closes an open library.
!	LBR$DELETE_DATA (library_index, old_rfa) - Deletes a module from
!   a library
!	LBR$REPLACE_KEY (library_index, entry_name, old_rfa, new_rfa) -
!   Changes the key for a module.
!	LBR$PUT_END (library_index) - Writes an end-of-entry in a library
!   module.
!	STR$FREE1_DX (desc) - Frees the storage allocated in a dynamic
!   string descriptor.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any error codes returned by the librarian routines called may be
!   returned by this routine.
!
!--

    BEGIN

    LOCAL
	library_data:
	    REF library_data_block,
	status;

    MAP
	stream:
	    REF stream_block;
!
! Access the library data based on the file id
!
    library_data = .library_id_vector [.stream [stream_file_id]];
!
! If we were writing to the library, 
!
    IF .library_data [library_access] NEQ tpu$k_input
    THEN
	BEGIN
	status = lbr$put_end (library_data [library_index]);
	IF NOT .status
	THEN
	    RETURN .status;
	status = lbr$replace_key (library_data [library_index],
				  library_data [library_entry_name],
				  library_data [library_old_rfa],
				  library_data [library_text_rfa]);
	IF NOT .status
	THEN
	    RETURN .status;
	IF .library_data [library_lookup_status]
	THEN
	    BEGIN
	    status = lbr$delete_data (library_data [library_index],
				      library_data [library_old_rfa]);
	    IF NOT .status
	    THEN
		RETURN .status;
	    END;
	END;
!
! End access to the library entry
!
    status = lbr$close (library_data [library_index]);
!
! Free the library data
!
    IF .status
    THEN
	BEGIN
	str$free1_dx (library_data [library_file_name]);
	str$free1_dx (library_data [library_entry_name]);
	lib$free_vm (UPLIT (library_data_bytes), library_data);
	library_id_vector [.stream [stream_file_id]] = 0;
	END;
!
! All done
!
    RETURN .status;

    END;					! Routine libutil_close

%SBTTL 'LIBUTIL_GET_RECORD - Get a record from the library entry'

ROUTINE libutil_get_record (stream, data) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine reads one record from the library entry.
!
! FORMAL PARAMETERS:
!
!	STREAM - The file control block for the file we're dealing with.
!   Passed by reference.
!	DATA - The I/O operation dependent data.  For a GET operation, this
!   is the descriptor that will receive a record from the text library.  Passed
!   by reference.
!
! IMPLICIT INPUTS:
!
!	LIBRARY_ID_VECTOR - Vector that holds the address of the library data
!   block for the library entry being processed.
!
! SUBROUTINES CALLED:
!
!	LBR$GET_RECORD (library_index, inbufdes [, outbufdes]) - Returns
!   next text record associated with a key.
!	LIB$SCOPY_R_DX (src_desc, dest_desc) - Copies a source string specified
!   by its length and buffer address to a destination string specified by a
!   descriptor.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any error codes returned by the librarian routines called may be
!   returned by this routine.
!
!--

    BEGIN

    OWN
	text_buffer:			! Receives record text
	    VECTOR [132, BYTE],
	text_desc:			! Describes text buffer
	    BLOCK [8, BYTE]
	    PRESET (
		[dsc$w_length] = 132,
		[dsc$b_class] = dsc$k_class_s,
		[dsc$b_dtype] = dsc$k_dtype_t,
		[dsc$a_pointer] = text_buffer);

    LOCAL
	library_data:
	    REF library_data_block,
	res_desc:
	    BLOCK [8, BYTE],
	status;

    MAP
	stream:
	    REF stream_block;
!
! Access the library data based on the file id
!
    library_data = .library_id_vector [.stream [stream_file_id]];
!
! Ask for next record
!
    status = lbr$get_record (library_data [library_index], text_desc, res_desc);
!
! If we got it, give it to TPU
!
    IF .status
    THEN
	lib$scopy_r_dx (%REF (.res_desc [dsc$w_length]),
			.res_desc [dsc$a_pointer],
			.data);

    RETURN .status;

    END;					! Routine libutil_get_record

%SBTTL 'LIBUTIL_FILEIO - I/O routine for use by VAXTPU'

ROUTINE libutil_fileio (code, stream, data) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine is called by VAXTPU to perform file I/O.
!
! FORMAL PARAMETERS:
!
!	CODE - Indicated the type of I/O operation that is to be performed.
!   Passed by reference.
!	STREAM - The file control block for the file we're dealing with.
!   Passed by reference.
!	DATA - The I/O operation dependent data.  Passed by reference.
!
! SUBROUTINES CALLED:
!
!	libutil_close (stream, data) - Routine to end access to a library entry.
!	libutil_get (stream, data) - Routine to read a record from a library
!   entry.
!	libutil_open (stream, data) - Routine to access a library entry.
!	libutil_put (stream, data) - Routine to write a record to a library
!   entry.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any of the codes returned by the libutil routines called by
!   this routine will be returned.
!
!--

    BEGIN

    LOCAL
	status;

    MAP
	stream:
	    REF stream_block;
!
! Is this one of ours, or do we pass it to TPU's file I/O routines?
!
    IF (..code NEQ tpu$k_open) AND (.stream [stream_file_id] GTR 511)
    THEN
	RETURN tpu$fileio (.code, .stream, .data);
!
! Either we're opening the file, or we know it's one of ours
!
    SELECTONE ..code OF
	SET

	[tpu$k_open]:
	    status = libutil_open (.stream, .data);

	[tpu$k_close, tpu$k_close_delete]:
	    status = libutil_close (.stream, .data);

	[tpu$k_get]:
	    status = libutil_get_record (.stream, .data);

	[tpu$k_put]:
	    status = libutil_put_record (.stream, .data);

	[OTHERWISE]:
	    status = tpu$_failure;

	TES;

    RETURN .status;

    END;					! Routine libutil_fileio

%SBTTL 'LIBUTIL_MAIN - Main procedure'

ROUTINE libutil_main =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This is the main entry point for the library editing utility.
!
! IMPLICIT INPUTS:
!
!	INIT_CALLBACK - The routine that is to be specified as the
!   initialization callback routine.
!
! SUBROUTINES CALLED:
!
!	TPU$CLEANUP () - Make TPU cleanup after itself.
!	TPU$CONTROL () - Pass control to TPU.
!	TPU$EXECUTE_INIFILE () - Cause TPU to execute the section file
!  initialization procedure and the command file, if specified.
!	TPU$FILEIO (code, stream, data) - TPU routine to perform file I/O
!	TPU$INITIALIZE (initialize_arg) - Initialize TPU.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any error codes returned by the TPU routines called may be
!   returned by this routine.
!
!--

    BEGIN

    LOCAL
	initialize_arg:
	    VECTOR [2],
	status;
!
! Set up the TPU condition handler
!
    ENABLE
	tpu$handler;
!
! Initialize the editor
!
    initialize_arg [0] = init_callback;
    initialize_arg [1] = 0;
    IF NOT (status = tpu$initialize (initialize_arg))
    THEN
	RETURN .status;
!
! Execute the command file
!
    IF NOT (status = tpu$execute_inifile())
    THEN
	RETURN .status;
!
! Let TPU do its thing
!
    IF NOT (status = tpu$control ())
    THEN
	RETURN .status;
!
! We're done
!
    tpu$cleanup ();
!
! Bye-bye
!
    RETURN tpu$_success;

    END;					! Routine libutil_main

%SBTTL 'LIBUTIL_OPEN - Access a text library'

ROUTINE libutil_open (stream, data) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine initializes access to a library entry.  If the library
!   specified does not exist, it will be created.
!
! FORMAL PARAMETERS:
!
!	STREAM - The file control block for the file we're dealing with.
!   Passed by reference.
!	DATA - The I/O operation dependent data.  For an OPEN operation, this
!   parameter is an item list of information about the file (library) to be
!   accessed.  Passed by reference.
!
! IMPLICIT INPUTS:
!
!	LIBRARY_ID_VECTOR - Vector that holds the address of the library data
!   block for the library entry being processed.
!
! SUBROUTINES CALLED:
!
!	LIB$FREE_VM (bytes, address) - Deallocates dynamic memory.
!	LIB$GET_VM (bytes, address) - Allocates dynamic memory.
!	LIB$SCOPY_R_DX (src_desc, dest_desc) - Copies a source string specified
!   by its length and buffer address to a destination string specified by a
!   descriptor.
!	LBR$INI_CONTROL (library_index, library_function, library_type) -
!   Initializes the librarian for our access.
!	LBR$LOOKUP_KEY (library_index, entry_name, old_rfa) - Position the
!   librarian to the proper module.
!	LBR$OPEN (library_index, library_name, create_options, default_name) -
!   Opens the library.
!	STR$FREE1_DX (desc) - Frees the storage allocated in a dynamic
!   string descriptor.
!	TPU$FILEIO (code, stream, data) - TPU routine to perform file I/O
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any error codes returned by the librarian routines called may be
!   returned by this routine.
!
!--

    BEGIN

    LOCAL
	create_options:
	    BLOCK [cre$c_length, BYTE]
	    PRESET (
		[cre$l_type] = lbr$c_typ_txt,
		[cre$l_keylen] = 31,
		[cre$l_alloc] = 100,
		[cre$l_idxmax] = 1,
		[cre$l_uhdmax] = 0,
		[cre$l_entall] = 100,
		[cre$l_luhmax] = 0,
		[cre$l_vertyp] = cre$c_vmsv3,
		[cre$l_idxopt] = cre$c_mactxtcas),
	entry_name_length,
	file_id,
	file_name_length,
	item:
	    REF item_block,
	library_data:
	    REF library_data_block,
	library_function,
	slash_ptr,
	status;

    MACRO
	abort_library_open =
	    BEGIN
	    str$free1_dx (library_data [library_file_name]);
	    str$free1_dx (library_data [library_entry_name]);
	    lib$free_vm (UPLIT (library_data_bytes), library_data);
	    library_id_vector [.file_id] = 0;
	    END % ;

    MAP
	stream:
	    REF stream_block;
!
! Allocate a file id and space for the library data block
!
    file_id = 0;
    INCR i FROM 0 TO 511 DO
	IF .library_id_vector [.file_id] EQL 0
	THEN
	    BEGIN
	    file_id = .i;
	    EXITLOOP;
	    END;

    stream [stream_file_id] = .file_id;
!
! Initialize the library data
!
    lib$get_vm (UPLIT (library_data_bytes), library_data);
    library_data [library_access] = tpu$k_input;
    init_dyndesc (library_data [library_file_name]);
    init_dyndesc (library_data [library_entry_name]);

    library_id_vector [.file_id] = .library_data;
!
! Process each of the data items passed in the data parameter.  Only look at
!   the items we care about.  Ignore all others.
!
    item = .data;

    WHILE (.item [item_code] NEQ 0) AND (.item [item_length] NEQ 0) DO
	BEGIN

	SELECTONE .item [item_code] OF
	    SET
	    [tpu$k_access]:
		library_data [library_access] = .item [item_buffer_addr];

	    [tpu$k_filename]:
		BEGIN
		slash_ptr = CH$FIND_CH (.item [item_length],
					.item [item_buffer_addr],
					%C'\');
		IF CH$FAIL (.slash_ptr)
		THEN
		    BEGIN
		    abort_library_open;
		    RETURN tpu$fileio (%REF (tpu$k_open), .stream, .data);
		    END;

		file_name_length = .slash_ptr - .item [item_buffer_addr];
		lib$scopy_r_dx (file_name_length,
				.item [item_buffer_addr],
				library_data [library_file_name]);
		lib$scopy_r_dx (%REF (.item [item_length] - .file_name_length
									 - 1),
			       .item [item_buffer_addr] + .file_name_length + 1,
			       library_data [library_entry_name]);
		END;

	    TES;
	item = .item + item_bytes;		! Point to next item
	END;
!
! Initialize the librarian for this entry
!
    IF .library_data [library_access] EQL tpu$k_input
    THEN
	library_function = lbr$c_read
    ELSE
	library_function = lbr$c_update;

    status = lbr$ini_control (library_data [library_index],
			      library_function,
			      UPLIT (lbr$c_typ_txt));
    IF NOT .status
    THEN
	BEGIN
	abort_library_open;
	RETURN .status;
	END;
!
! Try to open the library
!
    status = lbr$open (library_data [library_index],
		       library_data [library_file_name],
		       create_options,
		       %ASCID '.TLB');
!
! If the status is a failure, and we're trying to write, try to create
!   the library file
!
    IF NOT .status and (.library_data [library_access] NEQ tpu$k_input)
    THEN
	BEGIN
	library_function = lbr$c_create;
	status = lbr$ini_control (library_data [library_index],
				  library_function,
				  UPLIT (lbr$c_typ_txt));
	IF NOT .status
	THEN
	    BEGIN
	    abort_library_open;
	    RETURN .status;
	    END;

	status = lbr$open (library_data [library_index],
			   library_data [library_file_name],
			   create_options,
			   %ASCID '.TLB');
	END;
!
! Was the open successful?
!
    IF NOT .status
    THEN
	BEGIN
	abort_library_open;
	RETURN .status;
	END;
!
! Access the entry
!
    status = lbr$lookup_key (library_data [library_index],
			     library_data [library_entry_name],
			     library_data [library_old_rfa]);

    library_data [library_lookup_status] = .status;
!
! All done
!
    RETURN tpu$_success;

    END;					! Routine libutil_open

%SBTTL 'LIBUTIL_PUT_RECORD - Write a record to the library'

ROUTINE libutil_put_record (stream, data) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine writes a record to a text library entry.
!
! FORMAL PARAMETERS:
!
!	STREAM - The file control block for the file we're dealing with.
!   Passed by reference.
!	DATA - The I/O operation dependent data.  For a PUT operation, this
!   is the descriptor that contains a record to be written to the text library
!   entry.  Passed by reference.
!
! IMPLICIT INPUTS:
!
!	LIBRARY_ID_VECTOR - Vector that holds the address of the library data
!   block for the library entry being processed.
!
! SUBROUTINES CALLED:
!
!	LBR$PUT_RECORD (library_index, text, text_rfa) - Write a record to
!   a library module.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	Any error codes returned by the librarian routines called may be
!   returned by this routine.
!
!--

    BEGIN

    LOCAL
	library_data:
	    REF library_data_block;

    MAP
	stream:
	    REF stream_block;
!
! Access the library data based on the file id
!
    library_data = .library_id_vector [.stream [stream_file_id]];

    RETURN lbr$put_record (library_data [library_index], .data,
			   library_data [library_text_rfa]);

    END;					! Routine libutil_put_record

END						! Module libutil

ELUDOM
