Blame | Last modification | View Log | Download
/******************************************************************************** Copyright 1995 DeltaLink b.v. All Rights Reserved.******************************************************************************** DeltaLink Mailbox Communication Interface** File : mci.c** Version : 3.00** Developed by : Leon Westervoort.** This file contains the MCI functions used by DeltaLink Protocol Drivers and* Decoders.** The MCI uses the FactoryLink mailbox types and functions for communication* between two tasks which exchange large amounts of data.**/#include <stdlib.h>#include <string.h>#include <stdio.h>#include <stdarg.h>#include <flib.h> /* FactoryLink definitions */#ifdef FLECS#include <objct.h> /* Object definitions */#include <flntag.h> /* Normalized tag definitions */#endif#include <mci3.h> /* Mailbox Communication Interface definitions */#include <mci3_int.h> /* Mailbox Communication Interface definitions */#include <format3.h> /* Mailbox Communication Interface definitions *//* structure for searching tag id's */#ifndef FLECStypedef struct _ct_object{char tagname[ MAX_TAG_NAME];char tagdomain[ MAX_USR_NAME];char tagtype[ MAX_TYPE_NAME];char tagdesc[ MAX_PROC_DESC];char tagdimen[ MAX_DIM_LENGTH];u16 tagperwhen;u16 tagchgbits;TAG tagno;} CT_OBJECT;#endif/** define a NULL tag which has type 0 (DIGITAL) and index 0 for uninitialized* MCI messages*/TAG null_tag;/** global variable for internal use of the MCI library*/uint _Mci_q_num_active; /* total number of active messages in the queue */MCISYSTEM _Mci_system; /* system information of the MCI */uint _Mci_num_ds; /* total number of registered datasets */TAG *_Mci_R_ds; /* array with remote dataset control tags */char *_Mci_R_station_id; /* array with remote station id numbers */TAG *_Mci_L_ds; /* array with local dataset control tags */MCIDS **_Mci_ds; /* array with pointers to dataset information */int ( **_Mci_fnc)( void *, VAL); /* array with function pointers to event functions *//** FUNCTION: Mci_Init** PURPOSE: Initializes the Mailbox Communication Interface.**/int Mci_Init( MCISYSTEM *mci ) {int ret;/* register the local station id and the MCI mailbox*/memcpy( ( char *)&_Mci_system, mci, sizeof( MCISYSTEM ));/* initialize the MCI mailbox queue */if( ( ret = Mci_Q_Init( mci )) != GOOD)return ret;return GOOD;}/** FUNCTION: Mci_Exit** PURPOSE: Exits the Mailbox Communication Interface.**/int Mci_Exit( void) {uint i;_Mci_q_num_active = 0; /* total number of active messages in the queue */free( _Mci_R_ds); /* array with remote dataset control tags */free( _Mci_R_station_id); /* array with remote station id numbers */free( _Mci_L_ds); /* array with local dataset control tags */for( i = 0; i < _Mci_num_ds; i++)free( _Mci_ds[ i]); /* array with pointers to dataset information */free( _Mci_fnc); /* array with function pointers to event functions */_Mci_num_ds = 0; /* total number of registered datasets */return GOOD;}/** FUNCTION: Mci_Buffer_Alloc** PURPOSE: This function allocates a buffer for transferring data using the MCI* protocol. An additional header will be allocated for internal use.**/int Mci_Buffer_Alloc( MSG *msg) {/* try to allocate space for a user buffer with internal MCI header */msg->m_max += sizeof( MCIHDR);if( ( msg->m_ptr = ( char *)calloc( msg->m_max, 1)) == NULL)return FLE_OUT_OF_MEMORY;return GOOD;}/** FUNCTION: Mci_Recv** PURPOSE: Receives a dataset which has been queried from the queue first.* The header part will also be set in the wright data direction.**/int Mci_Recv( MCISYSTEM *system , MCIDS *ds ){/** check the origination of this dataset and if from a trigger then it does not have to be read* from the mailbox queue*/if( Mci_Get_Org( ds) == MCI_ORG_TRIGGER) {/* fake a read request message received here */Mci_Ds_Prepare( ds, Mci_Get_T_Buffer( ds));Mci_Set_Type( ds, MCI_READ_REQ);Mci_Set_CDE ( ds, CDE_BLOCK);/*return MCI_MSG_NOT_QUEUED;*/return GOOD;}else if( Mci_Get_Org( ds) == MCI_ORG_QUERY) {return GOOD;}/** check if the index lies in the range of active MCI messages*/if( Mci_Get_Index( ds) >= _Mci_q_num_active )return MCI_WRONG_INDEX;/* reinstate the buffer pointer and maximum length from tne MCI message */if( !ds->msg.mbx.mm_msg.m_ptr || !ds->msg.mbx.mm_msg.m_max) {if( ds->msg.buffer.m_ptr && ds->msg.buffer.m_max) {ds->msg.mbx.mm_msg.m_ptr = ds->msg.buffer.m_ptr;ds->msg.mbx.mm_msg.m_max = ds->msg.buffer.m_max;}elsereturn MCI_MSG_NOT_QUEUED;}/** do the reading from the receive mailbox which is specified in the MCI* message*/if( fl_read_app_mbx( Mci_Sys_Get_Task_Id( system ),Mci_Sys_Get_Task_Id( system ),/* task id of owner receive mailbox */Mci_Sys_Get_Mbx( system ), /* mailbox tag to receive from */&ds->msg.mbx, /* mailbox message to receive in */ds->msg.i) != GOOD) { /* index in receive queue */return fl_errno( Mci_Sys_Get_Task_Id( system ) );}/* update the number of active messages in the queue */_Mci_q_num_active--;/* check the data direction of the data and if necessary adjust the header information */if( (Mci_Get_Version( ds) >= 3) && Mci_Get_Org_Host_Dir( ds) != HOST_DIRECTION)Mci_Swap_Hdr( ds);/* check the validity of the MBXCTRL Tag *//*if( fl_get_tag_info( Mci_Get_Ds_Ctrl( ds),1,NULL,NULL) != GOOD)return MCI_BAD_CTRL;*/return GOOD;}/** FUNCTION: Mci_Send** PURPOSE: This function sends a message through the mailbox to the other task.* Always the local dataset control tag will used. Depeneding on the type* of the message the length of the MBXMSG will be set. The user must* specify the length of its user data first using Mci_Set_Len.**/int Mci_Send( MCISYSTEM *system , MCIDS *ds ){i16 data;u16 type;uint count;/* check the validity of the CTRL Tag */if( fl_get_tag_info( Mci_Get_Ds_Ctrl( ds ),1,NULL,NULL) != GOOD){return MCI_BAD_CTRL;}/* check the validity of the mailbox to write to */if( fl_get_tag_info( ( TAG *)&Mci_Get_Snd_Mbx( ds),1,&data,&type) != GOOD) {return MCI_BAD_SNDMBX;}/* check the number of datasets in the mailbox to write to */if( fl_count_mbx( Mci_Sys_Get_Task_Id( system ),Mci_Get_Snd_Mbx( ds ),&count ) != GOOD ){return MCI_BAD_SNDMBX;}if ( count > MCI_MAX_MSG_COUNT ){return MCI_MAX_MESSAGE;}/* set the buffer parameters on the MBXMSG mailbox message */if( ds->msg.mbx.mm_msg.m_ptr == NULL){ds->msg.mbx.mm_msg.m_ptr = ds->msg.buffer.m_ptr;ds->msg.mbx.mm_msg.m_max = ds->msg.buffer.m_max;}/* set initial the response mailbox to undefined */ds->msg.mbx.mm_mbx.t_type = ( u16)FL_BAD_TYPE; /* set our mailbox to undefined */ds->msg.mbx.mm_mbx.t_data = ( u16)FL_BAD_TYPE; /* set our mailbox to undefined *//* set the length parameters depending on which type of MCI message */switch( Mci_Get_Type( ds)) {case MCI_QUERY_CMD : /* request for dataset information *//* set the data length to the tag name plus header always in characters */ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds));/* set response mailbox for compatibilty reasons *//*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( &_Mci_system);*//* Set the source station identification of this system */Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));break;case MCI_QUERY_RSP : /* request for dataset information *//* set the data length to the tag name plus header always in characters */ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds));/* reinstate the defined length of the registered dataset */Mci_Set_Len( ds, ds->len);break;case MCI_READ_REQ : /* request for dataset information *//* set response mailbox for compatibilty reasons *//*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( system );*//* Set the source station identification of this system */Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));case MCI_WRITE_RSP : /* write response */ds->msg.mbx.mm_msg.m_len = ( u16)sizeof( MCIHDR);break;case MCI_WRITE_REQ : /* write request *//* set response mailbox for compatibilty reasons *//*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( system );*//* Set the source station identification of this system */Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));case MCI_QUERY_ERROR : /* MCI info message feed back */case MCI_READ_ERROR : /* read feed back */case MCI_RCV_ERROR : /* unsolicited receive ready */case MCI_WRITE_ERROR : /* write feed back */case MCI_RCV_RDY : /* unsolicited receive ready */case MCI_READ_RSP : /* read feed back */if( Mci_Get_Boundary( ds) == MCI_BIT_BND)ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + ( Mci_Get_Len( ds) / 8) +1);elseds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds) * Mci_Get_Boundary( ds));break;default:return MCI_BAD_TYPE;}ds->msg.mbx.mm_sendid = Mci_Sys_Get_Task_Id( system );/* set the host parameters of this machine */Mci_Set_Version ( ds, MCI_VERSION);Mci_Set_Org_Host_Dir ( ds, HOST_DIRECTION);Mci_Set_Station_Id ( ds, Mci_Sys_Get_Station_Id( system ));/* do the actual writing in the mailbox */if( fl_write_app_mbx( Mci_Sys_Get_Task_Id( system ), /* our own task task id */Mci_Sys_Get_Task_Id( system ), /* Task_id of owner write mailbox */ds->msg.snd_mbx, /* mailbox tag to write to */&ds->msg.mbx) != GOOD) {return fl_errno( Mci_Sys_Get_Task_Id( system ));}return GOOD;}/** FUNCTION: Mci_Send_Error** PURPOSE: Sends an error MCI message with the type depending on the current message**/int Mci_Send_Error( MCISYSTEM *system , MCIDS *ds, u16 error){MSG hdr;int ret;/** allocate a MCI header with the size of the error*/hdr.m_max = sizeof( MCIHDR) + sizeof( u16);if( ( hdr.m_ptr = ( char *)calloc( 1, hdr.m_max)) == NULL)return FLE_OUT_OF_MEMORY;/** set the buffer on the message to the temporary error*/Mci_Set_T_Buffer( ds, &hdr);/* return a function not supported error to the other task */Mci_Set_Len ( ds, sizeof( u16));Mci_Set_Boundary ( ds, MCI_WORD_BND);*( u16 *)Mci_Get_U_Buffer( ds) = error;/* determine what kind of error message type to send back */switch( Mci_Get_Type( ds) ) {case MCI_QUERY_CMD: /* MCI info message */Mci_Set_Type( ds, MCI_QUERY_ERROR);ret = Mci_Send( system, ds );break;case MCI_READ_REQ: /* read request */Mci_Set_Type( ds, MCI_READ_ERROR);ret = Mci_Send( system, ds );break;case MCI_WRITE_REQ: /* write request */Mci_Set_Type( ds, MCI_WRITE_ERROR);ret = Mci_Send( system, ds );break;default:ret = GOOD;break;}/* reset the buffer pointers in case this message will be used in the future */Mci_Reset_Buffer( ds);free( hdr.m_ptr);return ret;}/** FUNCTION: Mci_Array_Ds_Srch** PURPOSE: Search the internal arrays and check if the dataset is registered**/MCIDS *Mci_Array_Ds_Srch( MCIDS *srch) {uint i;/** check first which array to search, the remote or local*/if( Mci_Get_Station_Id( srch) != Mci_Sys_Get_Station_Id( &_Mci_system ) ){for( i = 0; i < _Mci_num_ds; i++)if( MCI_CMPTAG( Mci_Get_Ds_Ctrl( srch), &_Mci_R_ds[ i]) == GOOD) {Mci_Set_Ds_Ctrl( srch, _Mci_L_ds[ i]);return _Mci_ds[ i];}}else {for( i = 0; i < _Mci_num_ds; i++)if( MCI_CMPTAG( Mci_Get_Ds_Ctrl( srch), &_Mci_L_ds[ i]) == GOOD) return _Mci_ds[ i];}return NULL;}/** FUNCTION: Mci_Get_Fnc** PURPOSE: Returns the group of functions depending on the dataset type**/int Mci_Get_Fnc( MCIDS *ds) {switch( Mci_Get_Type( ds) ) {case MCI_QUERY_CMD : /* MCI info message */case MCI_QUERY_RSP : /* MCI info message feed back */case MCI_QUERY_ERROR : /* MCI info message feed back */return MCI_QUERY_FNC; /* Query functionality */case MCI_READ_REQ : /* read request */case MCI_READ_RSP : /* read feed back */case MCI_READ_ERROR : /* read feed back */return MCI_READ_FNC; /* Read functionality */case MCI_WRITE_REQ : /* write request */case MCI_WRITE_RSP : /* write feed back */case MCI_WRITE_ERROR : /* write feed back */return MCI_WRITE_FNC; /* Query functionality */case MCI_RCV_ACT : /* unsolicited receive active */case MCI_RCV_RDY : /* unsolicited receive ready */case MCI_RCV_ERROR : /* unsolicited receive ready */return MCI_RCV_FNC; /* Unsolicited Receive functionality */default:return ERROR;}}/** FUNCTION: Mci_Get_CDE_Size()** PURPOSE: This function returns the size of the data on which the operation will be* performed*/int Mci_Get_CDE_Size( ushort cde) {if( cde < CDE_SIGNED) return 1;if( cde < CDE_LONG) return 2;if( cde < CDE_DOUBLE_IEEE_FLT) return 4;if( cde == CDE_DOUBLE_IEEE_FLT) return 8;return ERROR;}/** FUNCTION: Mci_Cast( MCIDS *ds, char new_boundary)** PURPOSE: Cast data from old boundary to new boundary*/void Mci_Cast( MCIDS *ds, char new_boundary){void *data = ( void *)Mci_Get_U_Buffer( ds);/* check if the direction of the data is in host format */if ( Mci_Get_Bnd_Dir( ds) != HOST_DIRECTION ){Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));Mci_Set_Bnd_Dir( ds, HOST_DIRECTION);}/** The data in the MCI message represents always a valid value and at this point is always* in the host sirection. This means that the data is not placed on the final position where* it must be written because the MCI message can be in any data direction and boundary. This* way it is allowed here to let the compiler do some conversions*/switch ( Mci_Get_Boundary( ds) ){case MCI_BYTE_BND:switch( new_boundary){case MCI_WORD_BND:*( ushort *)data = *( uchar *)data;Mci_Set_Boundary( ds, MCI_WORD_BND);break;case MCI_LONG_BND:*( ulong *)data = *( uchar *)data;Mci_Set_Boundary( ds, MCI_LONG_BND);break;case MCI_DBL_BND:*( double *)data = *( uchar *)data;Mci_Set_Boundary( ds, MCI_DBL_BND);break;}break;case MCI_WORD_BND:switch( new_boundary){case MCI_BYTE_BND:*( uchar *)data = ( uchar)*( ushort *)data;Mci_Set_Boundary( ds, MCI_BYTE_BND);break;case MCI_LONG_BND:*( ulong *)data = *( ushort *)data;Mci_Set_Boundary( ds, MCI_LONG_BND);break;case MCI_DBL_BND:*( double *)data = *( ushort *)data;Mci_Set_Boundary( ds, MCI_DBL_BND);break;}break;case MCI_LONG_BND:switch( new_boundary){case MCI_BYTE_BND:*( uchar *)data = ( uchar)*( ulong *)data;Mci_Set_Boundary( ds, MCI_BYTE_BND);break;case MCI_WORD_BND:*( ushort *)data = ( ushort)*( ulong *)data;Mci_Set_Boundary( ds, MCI_WORD_BND);break;case MCI_DBL_BND:*( double *)data = *( ulong *)data;Mci_Set_Boundary( ds, MCI_DBL_BND);break;}break;case MCI_DBL_BND:switch( new_boundary){case MCI_BYTE_BND:*( uchar *)data = ( uchar)*( double *)data;Mci_Set_Boundary( ds, MCI_BYTE_BND);break;case MCI_WORD_BND:*( ushort *)data = ( ushort)*( double *)data;Mci_Set_Boundary( ds, MCI_WORD_BND);break;case MCI_LONG_BND:*( ulong *)data = ( ulong)*( double *)data;Mci_Set_Boundary( ds, MCI_LONG_BND);break;}break;}}/** FUNCTION: Mci_Place_Data( MCIDS *ds, char bnd_dir)** PURPOSE: places the data on the wright spot depending on the direction of the data.* It is assumed that the data will be deliverd in the whrigt format which means* that the type is big enough.** The original value of the data won't be preserved but shifted out.** The bit operation don't mirror the bits but take the numbering of the bits* in account (start from left or right side numbering).*/void Mci_Place_Data( MCIDS *ds, char bnd_dir) {u16 shift;/* check if the direction of the data is in host format */if( Mci_Get_Bnd_Dir( ds) != HOST_DIRECTION){Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));Mci_Set_Bnd_Dir( ds, HOST_DIRECTION);}/** shift the value which is in host format the number of times*/if( Mci_Get_CDE( ds) < CDE_NIBBLE) /* do a bit operation */{/** check in this case the numbering of the bits*/if( Mci_Get_Bit_Dir( ds) != HOST_DIRECTION)shift = ( u16) (((Mci_Get_Boundary( ds) * 8) -1) - (Mci_Get_CDE( ds) - CDE_B0_15 - Mci_Get_Bit_Offset( ds)));elseshift = ( u16)( Mci_Get_CDE( ds) - CDE_B0_15 - Mci_Get_Bit_Offset( ds) );}else if( Mci_Get_CDE( ds) < CDE_BYTE) /* do a nibble operation */shift = ( u16)( Mci_Get_CDE( ds) - CDE_NIBBLE - Mci_Get_Bit_Offset( ds));else if( Mci_Get_CDE( ds) < CDE_SIGNED) /* do a byte operation */shift = ( u16)( Mci_Get_CDE( ds) - CDE_BYTE - Mci_Get_Bit_Offset( ds));else if( Mci_Get_CDE( ds) < CDE_LONG) /* do a word operation */shift = ( u16)( Mci_Get_CDE( ds) - CDE_SIGNED - Mci_Get_Bit_Offset( ds));else if( Mci_Get_CDE( ds) < CDE_DOUBLE_IEEE_FLT) /* do a long operation */shift = ( u16)( Mci_Get_CDE( ds) - CDE_LONG - Mci_Get_Bit_Offset( ds));else /* do a double operation */shift = ( u16)( Mci_Get_CDE( ds) - CDE_DOUBLE_IEEE_FLT - Mci_Get_Bit_Offset( ds));/** set the actual value to the wright place*/switch( Mci_Get_Boundary( ds)) {case MCI_BIT_BND: /* bit boundary already on place ! */break;case MCI_BYTE_BND: /* shift a nibble in a byte */*( char *)Mci_Get_U_Buffer( ds) <<= shift;break;case MCI_WORD_BND: /* shift a nibble in a word */*( ushort *)Mci_Get_U_Buffer( ds) <<= shift;break;case MCI_LONG_BND: /* shift a nibble in a long */*( ulong *)Mci_Get_U_Buffer( ds) <<= shift;break;case MCI_DBL_BND: /* shift a nibble in a double *//* *( double *)Mci_Get_U_Buffer( ds) <<= shift; */break;}/** check the data direction of the data*/if( bnd_dir != HOST_DIRECTION)Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));return;}/** FUNCTION: Mci_Swap_Hdr( MCIHDR *hdr)** PURPOSE: Swaps the elements in the header*/void Mci_Swap_Hdr( MCIDS *ds) {MCIHDR hdr;swab( ds->msg.mbx.mm_msg.m_ptr, ( char *)&hdr, sizeof( MCIHDR));memcpy( ds->msg.mbx.mm_msg.m_ptr, ( char *)&hdr, sizeof( MCIHDR));return;}/*-----------------------------------------------------------------------------* FUNCTION: void Mci_Mirror( char *buf, ushort length)** PURPOSE: reverses a character buffer with length 'length'-----------------------------------------------------------------------------*/void Mci_Mirror( char *buf, ushort length) {uchar temp;ushort i;/** of start = 1 then the real start of the data is on index 0*/for( i = 0; i < length/2; i++) {temp = buf[ (length -i) -1];buf[ (length -i) -1] = buf[ i];buf[ i] = temp;}return;}/*-----------------------------------------------------------------------------** FUNCTION: char * Mci_Reverse( char *to, char *from, char nr)* PURPOSE : reverse a string of bytes*-----------------------------------------------------------------------------*/char * Mci_Reverse( char *to, char *from, char nr) {char i;for( i = 0; i < nr; i++) to[ i] = from[ (nr - i) -1];return to;}/*-----------------------------------------------------------------------------** FUNCTION: mci_fl_name2tag*** arguments:* char *name Name of first tag in array* char *dimension Dimension of array. Format like '12,3,5'** purpose:* Calculate the number of tags left in the array based on the* name and the dimension.**-----------------------------------------------------------------------------*/#ifndef FLECSint Mci_name2tag( char *name, TAG *tag) {CT ct_buf;uint num_tags,t, m, b;uint i,depth,offset,len[5];CT_OBJECT t_info,b_info,m_info;char tagname[48];char *index,*dim;tag->t_type = 0xFFFF;tag->t_data = 0xFFFF;if( ct_open( &ct_buf, "{FLAPP}", "ct/object.ct") != GOOD)return ERROR;if( ct_read_index( &ct_buf, 0) != GOOD ) {ct_close( &ct_buf);return ERROR;}num_tags = ct_get_nrecs( &ct_buf);/** Separate the tagname form the index*/strcpy( tagname, name);index = strstr( tagname, "[");if( index) *index++ = '\0';/** Check to see if the tagname is a array element* If so skip the part after the first '['*/t = 0;b = num_tags - 1;m = ( t + b) / 2;if( ( ct_read_rec( &ct_buf, &t_info, t) != GOOD) ||( ct_read_rec( &ct_buf, &m_info, m) != GOOD) ||( ct_read_rec( &ct_buf, &b_info, b) != GOOD)) {tag->t_type = 0xFFFF;tag->t_data = 0xFFFF;ct_close( &ct_buf);return ERROR;}/** Check the Top and Bottom records, if not then* perform a binary search until found*/if ( strcmp( t_info.tagname, tagname) == 0)memcpy( &m_info, &t_info, sizeof( CT_OBJECT));if ( strcmp( b_info.tagname, tagname) == 0)memcpy( &m_info, &b_info, sizeof( CT_OBJECT));while (( m != t) && ( strcmp(m_info.tagname, tagname) != 0)){if ( ct_read_rec( &ct_buf, &m_info, m) != GOOD) {ct_close( &ct_buf);return ERROR;}if ( strcmp(m_info.tagname, tagname) < 0)t = m;elseb = m;m = ( t + b) / 2;}if ( strcmp( m_info.tagname, tagname) != 0) {ct_close( &ct_buf);return ERROR;}memcpy( tag, &m_info.tagno, sizeof( TAG));if( index != NULL){/** Get the dimension of the total array based on 'dimension'* Format like '12,3,5'*/dim = m_info.tagdimen;i = 0;dim--;do{dim++;len[ i++] = atoi( dim);} while (( dim = strstr( dim, ",")) != NULL);/** Calculate the offset of this particalr tag on top of the* general tag value*/offset = 0;depth = i - 1;i = 0;index--;do{index++;offset += atoi( index);if( i < depth)offset *= len[ i + 1];i++;} while( ( index = strstr( index, "[")) != NULL);tag->t_data += offset;}ct_close( &ct_buf);return GOOD;}#elseint Mci_name2tag( char *name, TAG *tag) {CT ct_buf;FLNTAG *ntag;int result = GOOD;/** Open the object CT file*/if( ct_open_obj( &ct_buf,"{FLAPP}") != GOOD)return ERROR;/** Create a normailzed tag*/if( ( ntag = flntag_create()) == NULL)return ERROR;/** Initialize the ntag with the correct values*/if( flntag_parse_ref( ntag, name) != FLNTAG_E_GOOD)return ERROR;/** Find the the in the object CT*/if( flntag_find_tag( ntag, &ct_buf, tag) != FLNTAG_E_GOOD)result = ERROR;/** Destroy the normalized tag*/flntag_destroy( ntag);/** Close the object CT file*/ct_close_obj( &ct_buf);return result;}#endif