static char *RCSid="$Id: write_genx_tape.c,v 1.1 2000/01/17 19:23:54 jwm Exp $"; /*********************************************************************** write_genx_tape.c ********************************************************************** Description: Controls the generation of tape exchange data files for ultimate writing to an exchange tape. The overview of this program follows: 1) Get adequate information from get_patient_info() to be able to build a linked list of exch_file_t structures which have basic information about the file to be process, but not necessarily all information to fill the structure until processing time. The information contained in site_data_t should help in this process. If multiple patients are to be supported, this must also build the linked list of cases. 2) Process the exch_file_t information for each case. If the implementation does not initially create an exch_file_t structure for every file, additional ones are inserted as needed. An example of where this might happen is if a flag is used to indicate that beams for a given plan are to be included and an exch_file_t node is created for a beam without worrying about how many beams are in the plan yet. Then when the beams for the plan are processed, the appropriate nodes are added. This means that image numbers must be incremented throughout the linked list of exch_file_t and case_t data structures. This processing includes writing the image files in the appropriate buffered fashion. 3) Write the directory file out for all exch_file_t nodes in the list and for all case_t nodes in the list using the required buffered writes. The steps in 1 & 2 are so RTP system dependent, that this code will be specific to a particular planning system. However, several of the utilities required to perform these tasks may be standardized and are supplied herein. Modules Included: main() Obtains information about data to be formatted in an AAPM file set, generates appropriate data files, and writes directory file. write_directory_file() Writes the directory file based on the information in the linked list of exch_file_t structures for each image file. insert_exch_file_node() Inserts and exch_file_t node in a linked list for files which are added during processing. create_exch_file_node() Builds the linked list of exch_file_t structures required by write_directory_file. get_patient_info() Must be written by recipient. process_case() Traverses the exch_file_t linked list for a case, generates the actual data image file and fills in remaining exch_file_t structure data not obtained from get_patient_info. Revision History: $Log: write_genx_tape.c,v $ * Revision 1.1 2000/01/17 19:23:54 jwm * Upgrade generic version to 4.00 standard * * Revision 1.0 2000/01/17 15:37:27 jwm * Initial revision **********************************************************************/ /* Include Files */ #include #include #include #include #include #include #include #include #include #include #include "sitedata.h" #include "exchkeys.h" #include "patexchange.h" #include "writexch.h" /* GLOBAL FILE STREAM POINTER FOR LOG FILE */ FILE *aapm_log_file=NULL; char aapm_log_string[180]; static int rtog_case_number; /* RTOG CASE NUMBER */ static int create_exch_file_node ( tapedir_t *tape, /* TAPE DIRECTORY STRUCTURE */ image_type_t image_flag, /* IMAGE TYPE FLAG */ case_t *patcase, /* CASE STRUCTURE */ exch_file_t **current_node /* CURRENT NODE IN LINKED LIST */ ); static int process_case ( tapedir_t *tape, /* TAPE DIRECTORY STRUCTURE TO FILE */ case_t *patcase, /* PATIENT CASE POINTER */ int *file_number /* NUMBER OF NEXT DATA FILE TO BE WRITTEN */ ); static int get_patient_info ( tapedir_t *tape /* TAPE DIRECTORY STRUCTURE */ ); static int write_directory_file ( tapedir_t *tape /* TAPE DIRECTORY STRUCTURE */ ); /* */ /***************************************************************************** main() **************************************************************************** Function Description: Handles dispatching required to generate tape exchange format files. It is invoked from the command line (UNIX) with the following arguments: write_genx_tape where: destination directory is the directory for the output files to be written to blocksize is the buffer size in bytes. It should generally be 2048. This routine calls a routine which must be written specifically for the site using this code as it is RTP system dependent. This routine is: get_patient_info fills appropriate data into the structures defined in sitedata.h (customized for user) which is needed to generate a file data set. ***************************************************************************/ void main ( int argc, /* COMMAND LINE ARGUMENT COUNT */ char *argv[] /* ARGUMENT LIST */ ) { int file_number; /* NUMBER OF NEXT TAPE FILE TO WRITE */ case_t *patcase; /* POINTER TO CASE STRUCTURE */ tapedir_t tape; /* TAPE DIRECTORY STRUCTURE */ /* ENSURE THAT THE CORRECT NUMBER OF INVOKING ARGUMENTS ARE USED */ if ( argc != 3 ) { fprintf( stderr, "Usage: write_genx_tape " ); exit( 1 ); } /* INITIALIZE THE TAPE DIRECTORY STRUCTURE */ memset( (void *)&tape, 0, sizeof( tapedir_t )); tape.path = dup_string( argv[1] ); tape.block_size = atoi( argv[2] ); /* GET THE PATIENT ID AND ALL APPROPRIATE FLAGS AND PATHS */ if ( get_patient_info( &tape ) != SUCCESS ) { sprintf( aapm_log_string, "Error getting patient information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); exit( 1 ); } patcase = tape.patient_case; /* SET POINTER TO THE CASE */ file_number = 1; /* set tape file number to one */ /* PROCESS ALL FILES REQUIRED FOR THIS CASE */ if ( process_case( &tape, patcase, &file_number ) != SUCCESS ) { /* LET USER KNOW A FATAL ERROR OCCURRED */ sprintf( aapm_log_string, "Processing case for patient: %s", patcase->patient_id ); aapm_status( eAAPM_ERROR, aapm_log_string ); exit( 1 ); } /* LET USER KNOW THIS CASE IS COMPLETE */ sprintf( aapm_log_string, "Case #: %d is complete", rtog_case_number ); aapm_status( eAAPM_INFO, aapm_log_string ); /* BUILD THE DIRECTORY FILE */ if ( write_directory_file( &tape ) != SUCCESS ) { sprintf( aapm_log_string, "Error occurred writing the directory file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); exit( 1 ); } exit( 0 ); } /********************** End of main() *************************************/ /*********************************************************************** process_case() ********************************************************************** Function Description: Process all requisite patient files and convert them to tape format while concurrently building the directory data structure in memory. It does this by processing each image directory entry in order of linked list. This routine calls several other routines which must be written for the specific RTP system targeted. These routines are: wprocess_comment process a comment file, generating the image file and setting remaining directory structure values required. wprocess_ct process a ct file, generating the image file and setting remaining directory structure values required. wprocess_mr process an mr file, generating the image file and setting remaining directory structure values required. wprocess_structure process a structure file, generating the image file and setting remaining directory structure values required. wprocess_beam process a beam geometry file, generating the image file and setting remaining directory structure values required. wprocess_seed process a seed geometry file, generating the image file and setting remaining directory structure values required. wprocess_film process a digital film file, generating the image file and setting remaining directory structure values required. wprocess_dose process a tele dose file, generating the image file and setting remaining directory structure values required. wprocess_sdose process a seed dose file, generating the image file and setting remaining directory structure values required. wprocess_dvh process a tele dvh file, generating the image file and setting remaining directory structure values required. wprocess_sdvh process a seed dvh file, generating the image file and setting remaining directory structure values required. *********************************************************************/ static int process_case ( tapedir_t *tape, /* TAPE DIRECTORY STRUCTURE TO FILE */ case_t *patcase, /* PATIENT CASE POINTER */ int *file_number /* NUMBER OF NEXT DATA FILE TO BE WRITTEN */ ) { int comment_num = 0; /* NUMBER OF COMMENTS PROCESSED */ int ct_num = 0; /* NUMBER OF CTs PROCESSED */ int mr_num = 0; /* NUMBER OF MRs PROCESSED */ int us_num = 0; /* NUMBER OF USs PROCESSED */ int tplan_num = 0; /* INDEX OF TELE PLAN TO PROCESS */ int film_num = 0; /* NUMBER OF FILMS PROCESSED */ int struct_num = 0; /* NUMBER OF STRUCTURES PROCESSED */ int splan_num = 0; /* INDEX OF SEED PLAN TO PROCESS */ int *plan_num_ptr; /* PTR TO tplan_num OR splan_num */ exch_file_t *cur_file = patcase->head_file; /* LL TRAVERSAL PTR */ int doing_seed_plan; /* ARE THERE ANY TELE PLANS? */ if( patcase->site_data.num_tplans > 0 ) { plan_num_ptr = &tplan_num; doing_seed_plan = FALSE; } else { plan_num_ptr = &splan_num; doing_seed_plan = TRUE; } /* HANDLE EACH FILE TYPE IN ORDER REQUIRED ON TAPE */ while( cur_file != NULL ) { switch( cur_file->image_flag ) { case eCOMMENT: /* PROCESS COMMENT FILE */ /* PROCESS THIS COMMENT FILE */ if ( wprocess_comment( cur_file, patcase, tape, &comment_num ) == NULL ) { sprintf( aapm_log_string, "Error processing comment file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eCTSCAN: /* PROCESS THIS CT FILE */ if ( wprocess_ct( cur_file, patcase, tape, &ct_num ) == NULL ) { sprintf( aapm_log_string, "Error processing ct file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eMRISCAN: /* PROCESS THIS MR FILE */ if ( wprocess_mrus( cur_file, patcase, tape, &mr_num ) == NULL ) { sprintf( aapm_log_string, "Error processing MR file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eUSSCAN: /* PROCESS THIS US FILE */ if ( wprocess_mrus( cur_file, patcase, tape, &us_num ) == NULL ) { sprintf( aapm_log_string, "Error processing US file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eSTRUCTURE: /* PROCESS THIS STRUCTURE FILE */ if ( wprocess_structure( cur_file, patcase, tape, &struct_num ) == NULL ) { sprintf( aapm_log_string, "Error processing structure file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eBEAMGEOM: /* PROCESS ALL BEAMS IN THIS PLAN FILE */ if ( (cur_file = wprocess_beam( cur_file, patcase, tape, plan_num_ptr )) == NULL ) { sprintf( aapm_log_string, "Error processing beam file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* INCREMENT THE PLAN NUMBER IF NEXT FILE IS NOT FILM, DOSE, OR DVH */ break; case eDIGITALFILM: /* PROCESS THIS FILM FILE (OR ALL DRR'S IF film_num < 0) */ if ( (cur_file = wprocess_film( cur_file, patcase, tape, &film_num, plan_num_ptr )) == NULL ) { sprintf( aapm_log_string, "Error processing film files" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eDOSEDIST: /* PROCESS THIS DOSE FILE */ if( doing_seed_plan ) { if( wprocess_sdose(cur_file,patcase,tape,plan_num_ptr) == NULL ) { sprintf( aapm_log_string, "Error processing SEED dose file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } } else { if( wprocess_dose(cur_file,patcase,tape,plan_num_ptr) == NULL ) { sprintf( aapm_log_string, "Error processing TELE dose file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } } break; case eDVH: /* PROCESS THIS DOSE VOLUME HISTOGRAM FILE, ADDING WHATEVER NODES ARE NEEDED */ if( doing_seed_plan ) { if ( (cur_file = wprocess_sdvh( cur_file, patcase, tape, plan_num_ptr )) == NULL ) { sprintf( aapm_log_string, "Error processing SEED dvh file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } } else { if ( (cur_file = wprocess_dvh( cur_file, patcase, tape, plan_num_ptr )) == NULL ) { sprintf( aapm_log_string, "Error processing TELE dvh file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } } break; case eSEEDGEOM: /* PROCESS SEED GEOMETRY FOR THIS PLAN */ if ( (cur_file = wprocess_seed( cur_file, patcase, tape, plan_num_ptr )) == NULL ) { sprintf( aapm_log_string, "Error processing SEED Geometry" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } break; case eIMAGEUNDEF: /* THIS IS A BLANK TO SIGNAL THE END OF THE PLAN */ (*plan_num_ptr)++; /* increment the plan index to next plan */ if( patcase->site_data.num_tplans && *plan_num_ptr == patcase->site_data.num_tplans ) { plan_num_ptr = &splan_num; doing_seed_plan = TRUE; } break; default: sprintf(aapm_log_string,"Unknown Image type in process_case()"); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* WORK THROUGH THE LINKED LIST */ cur_file = (exch_file_t *)cur_file->next; } /* RETURN STATUS */ return( SUCCESS ); } /************************* End of process_case() **************************/ /***************************************************************************** get_patient_info() **************************************************************************** Function Description: Gets information necessary to document the data to be put into patient data exchange files. It fills the requisite information into the site_data_t structure and the image file directory linked list. ***************************************************************************/ static int get_patient_info ( tapedir_t *tape /* TAPE DIRECTORY STRUCTURE */ ) { /* SUBTLE WARNING TO RECIPIENT */ fprintf( stderr, "\nThe 'get_patient_info' routine must be written\n" ); /* RETURN STATE CONSIDERING THAT IT IS NOT YET CODED */ return( FAILURE ); } /************************** End of get_patient_info() **********************/ /***************************************************************************** create_exch_file_node() **************************************************************************** Function Description: Creates a node in a linked list of exchange file directory entries. ***************************************************************************/ static int create_exch_file_node ( tapedir_t *tape, /* TAPE DIRECTORY STRUCTURE */ image_type_t image_flag, /* IMAGE TYPE FLAG */ case_t *patcase, /* CURRENT CASE STRUCTURE */ exch_file_t **current_node /* CURRENT NODE IN LINKED LIST */ ) { exch_file_t *new_node; /* CREATE A NEW FILE NODE */ if ( (new_node = (exch_file_t *)calloc( (size_t)1, sizeof(exch_file_t))) == NULL ) { sprintf( aapm_log_string, "Allocating memory for exch file node" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* FILL THE APPROPRIATE DATA INTO THE NODE */ new_node->case_number = rtog_case_number; new_node->image_flag = image_flag; new_node->patient_name = dup_string( patcase->patient_name ); /* ONLY INCREMENT COUNT IF THIS IS A DEFINED IMAGE */ if ( image_flag != eIMAGEUNDEF ) tape->total_images += 1; new_node->image_number = tape->total_images; /* DETERMINE IF WE ARE SETTING HEAD OF LIST, OR ADDITIONAL NODE */ if ( patcase->head_file == NULL ) /* THIS IS THE HEAD OF THE LIST */ *current_node = patcase->head_file = new_node; else { /* THIS IS THE NEXT NODE IN THE LIST */ (*current_node)->next = (struct exch_file_t *)new_node; *current_node = new_node; } /* RETURN STATE */ return( SUCCESS ); } /********************** End of create_exch_file_node() *******************/ /***************************************************************************** insert_exch_file_node() **************************************************************************** Function Description: Inserts an exchange file node in the middle of a linked list. It is used primarily for adding beams and dvh's where only one entry is originally put into the list as a place holder and is expanded at write time. ***************************************************************************/ exch_file_t *insert_exch_file_node /* INSERT EXCH FILE NODE IN LIST */ ( case_t *p_case, /* CASE POINTER */ exch_file_t *cur_node /* CURRENT NODE TO ADD ONTO */ ) { exch_file_t *new_node; /* NEW NODE ADDED IN TO LIST */ exch_file_t *walk_ptr; /* POINTER TO WALK LIST TO INCREMENT IMAGE NUMBER */ /* ALLOCATE THE NEW NODE */ if ( (new_node = (exch_file_t *)calloc( (size_t)1, sizeof( exch_file_t ))) == NULL ) { sprintf( aapm_log_string, "Allocating exchange file node in insert_exch_file_node" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( NULL ); } /* RELOCATE POINTER VALUES */ new_node->next = (struct exch_file_t *)cur_node->next; cur_node->next = (struct exch_file_t *)new_node; /* SET THE BASIC INFORMATION INTO THIS NODE */ new_node->image_number = cur_node->image_number + 1; new_node->image_flag = cur_node->image_flag; new_node->case_number = cur_node->case_number; if ( (new_node->patient_name = dup_string( cur_node->patient_name )) == NULL ) { /* ERROR IN MEMORY USAGE */ sprintf( aapm_log_string, "Allocating patient name string in insert_exch" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( NULL ); } /* WALK THE LIST AND INCREMENT THE IMAGE NUMBER */ walk_ptr = (exch_file_t *)new_node->next; while( walk_ptr != NULL ) { walk_ptr->image_number++; walk_ptr = (exch_file_t *)walk_ptr->next; } /* RETURN POINTER TO THIS NEW NODE */ return( new_node ); } /************************** End of insert_exch_file_node() ****************/ /***************************************************************************** write_directory_file() **************************************************************************** Function Description: Creates and writes the directory file for the AAPM Patient Data Exchange Tape format. ***************************************************************************/ static int write_directory_file ( tapedir_t *tape /* TAPE DIRECTORY STRUCTURE */ ) { FILE *output; /* FILE STREAM POINTER FOR OUTPUT */ case_t *patcase; /* LIST TRAVERSAL POINTER */ exch_file_t *file_entry; /* LIST TRAVERSAL POINTER */ char string[80]; /* TEMP TEXT STORAGE */ time_t clock; /* EXTERNAL TIME STRUCTURE */ struct tm *time_now; /* TIME STRUCTURE FOR DATE STRING */ char tape_standard[20]; /* holds string version of standard */ /* INITIALIZE THE AAPM OUTPUT FILE */ if ( init_aapm_file( tape, 0, &output ) != SUCCESS ) { sprintf( aapm_log_string, "Initializing aapm output for comment file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* OUTPUT THE TAPE STANDARD */ sprintf(tape_standard,"%4.2f", TAPE_STANDARD); if ( aapm_entry( tape, ekTAPESTANDARDNUMBER, &tape_standard, aSTRING, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing tape standard # to directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* OUTPUT THE INSTITUTION */ if ( aapm_entry( tape, ekINSTITUTION, (void *)tape->institution, aSTRING, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing institution to directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* OUTPUT THE WRITER */ if ( aapm_entry( tape, ekWRITER, (void *)tape->writer, aSTRING, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing institution to directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* OUTPUT THE DATE CREATED */ time( &clock ); time_now = localtime( &clock ); sprintf( string, "%02d, %02d, %04d", time_now->tm_mday, time_now->tm_mon+1, time_now->tm_year + 1900 ); if ( aapm_entry( tape, ekDATECREATED, (void *)string, aSTRING, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing date to directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* PROCESS THE ONE AND ONLY CASE */ patcase = tape->patient_case; file_entry = patcase->head_file; while ( file_entry != NULL ) { /* PUT A BLANK LINE BETWEEN EACH IMAGE */ if ( aapm_blank_line( tape, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing blank line to directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* PROCESS USING APPROPRIATE ROUTINE */ switch( file_entry->image_flag ) { case eCOMMENT: /* PROCESS COMMENT FILE */ if ( write_comment_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing comment directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eCTSCAN: /* PROCESS CT SCAN DIRECTORY ENTRY */ if ( write_ct_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing ct directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eMRISCAN: /* PROCESS MR SCAN DIRECTORY ENTRY */ if ( write_mrus_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing mr directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eUSSCAN: /* PROCESS US SCAN DIRECTORY ENTRY */ if ( write_mrus_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing US directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eSTRUCTURE: /* PROCESS STRUCTURE DIRECTORY ENTRY */ if ( write_struct_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing struct directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eBEAMGEOM: /* PROCESS BEAM GEOMETRY DIRECTORY ENTRY */ if ( write_beam_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing beam directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eDIGITALFILM: /* PROCESS DIGITAL FILM DIRECTORY ENTRY */ if ( write_film_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing film directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eDOSEDIST: /* PROCESS DOSE DISTRIBUTION DIRECTORY ENTRY */ if ( write_dose_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing dose directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eDVH: /* PROCESS DVH DIRECTORY ENTRY */ if ( write_dvh_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing dvh directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eSEEDGEOM: /* PROCESS SEED GEOMETRY DIRECTORY ENTRY */ if ( write_seed_directory( tape, patcase, file_entry, output ) != SUCCESS ) { sprintf( aapm_log_string, "Writing SEED GEOMETRY directory information" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } break; case eIMAGEUNDEF: /* PLAN NUMBER BREAK. IGNORE */ break; default: sprintf( aapm_log_string, "Invalid image flag encountered in directory" ); aapm_status( eAAPM_ERROR, aapm_log_string ); fclose( output ); return( FAILURE ); } /* END OF SWITCH file_entry BLOCK */ /* TRAVERSE THE LINKED LIST */ file_entry = (exch_file_t *)file_entry->next; } /* END OF WHILE FILE_ENTRY LOOP */ /* CLOSE THE AAPM FILE */ if ( close_aapm_file( tape, output ) != SUCCESS ) { sprintf( aapm_log_string, "Closing aapm directory file" ); aapm_status( eAAPM_ERROR, aapm_log_string ); return( FAILURE ); } /* ALL IS WELL */ return( SUCCESS ); } /*********************** End of write_directory_file() ****************/ /******************* End of write_genx_tape.c *************************/