gint64 gst_aiur_stream_cache_read (GstAiurStreamCache * cache, guint64 size, char *buffer) { gint64 readsize = -1; gint retrycnt = 0; if (cache == NULL) { return readsize; } try_read: if (cache->closed == TRUE) { return readsize; } g_mutex_lock (cache->mutex); if (cache->seeking == TRUE) goto not_enough_bytes; if ((cache->threshold_max) && (cache->threshold_max < size + cache->threshold_pre)) { cache->threshold_max = size + cache->threshold_pre; /* enlarge maxsize means consumed */ g_cond_signal (cache->consume_cond); } if (size > AVAIL_BYTES (cache)) { if (cache->eos) { /* not enough bytes when eos */ readsize = AVAIL_BYTES (cache); if (readsize) { READ_BYTES (cache, buffer, readsize); } goto beach; } goto not_enough_bytes; } readsize = size; READ_BYTES (cache, buffer, readsize); goto beach; not_enough_bytes: //g_print("not enough %lld, try %d\n", size, retrycnt++); WAIT_COND_TIMEOUT (cache->produce_cond, cache->mutex, 1000000); g_mutex_unlock (cache->mutex); goto try_read; beach: g_mutex_unlock (cache->mutex); return readsize; }
static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t seg_offset = (uint32_t) -1; uint8_t *buffer = module->data + FRAME_HEADER_LEN; if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS; module->data_len = FRAME_HEADER_LEN; module->hdr.len = BI32(module->data); module->hdr.timestamp = BI32(module->data+4); module->hdr.sequence = BI16(module->data+8); module->hdr.flags = BI16(module->data+10); module->hdr.num_segments = BI32(module->data+16); module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len; // if we have space, we store up the segments in memory so we can tell the frame // type, since most streams have their type byte as the first follow the segment information. // if we don't have space, then we just don't know the frame type, so will not emit timestamp // information as we don't know if it's reliable. if(module->hdr.num_segments <= MAX_NUM_SEGMENTS) { uint32_t i; if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS; module->data_len += (module->hdr.num_segments * 8); for (i=0; i<module->hdr.num_segments; i++) { uint32_t valid_seg; uint32_t offset; valid_seg = BI32(buffer); offset = BI32(buffer+4); if (valid_seg && seg_offset > offset) seg_offset = offset; // this boolean field should have only 0 or 1 values if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID; buffer += 8; } } if(seg_offset == 0) { if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS; module->data_len += 1; module->type = (*buffer >> 5) & 3; } else
/**************************************************************************//** * Read/skip data from the container. * Can also be used to query information about the next block of data. * * @param p_ctx The reader context. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_TRACK_T *track; VC_CONTAINER_TRACK_MODULE_T *t_module; VC_CONTAINER_STATUS_T status; if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; track = p_ctx->tracks[0]; t_module = track->priv->module; CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET); while (!BITS_AVAILABLE(p_ctx, &t_module->payload)) { uint32_t bytes_read; /* No data left from last RTP packet, get another one */ bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE); if (!bytes_read) return STREAM_STATUS(p_ctx); BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read); decode_rtp_packet_header(p_ctx, t_module); SET_BIT(t_module->flags, TRACK_NEW_PACKET); } if (p_packet) { uint32_t timestamp_top = t_module->timestamp >> 30; /* Determine whether timestamp has wrapped forwards or backwards around zero */ if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3)) t_module->timestamp_wraps++; else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0)) t_module->timestamp_wraps--; t_module->last_timestamp_top = timestamp_top; p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp; p_packet->track = 0; p_packet->flags = 0; } status = t_module->payload_handler(p_ctx, track, p_packet, flags); if (p_packet && status == VC_CONTAINER_SUCCESS) { /* Adjust timestamps from RTP clock rate to microseconds */ p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; } STREAM_STATUS(p_ctx) = status; return status; }
void *readTask(FILE *inputFile){ Task *task = createTask(); TaskIdList *list = NULL; int endedTasks=-1, metaSize=-1, taskState=-1, id=-1; char *metadata = NULL; READ_BEGIN(inputFile); list = (TaskIdList *)readTaskIdList(inputFile); setTaskDependsOnMe(task, list); taskIdListDestroy(list); list = (TaskIdList *)readTaskIdList(inputFile); setTaskMyDeps(task, list); taskIdListDestroy(list); hashIntVoidDeserialize(inputFile, task->children); // make the children point to its mother HashIntVoidIterator *it = createHashIntVoidIterator(task->children, 0); PosHandlerIntVoid pos = hashIntVoidIteratorNext(it, task->children); while (pos != NULL) { Task *child = posGetValue(pos); child->mother = task; pos = hashIntVoidIteratorNext(it, task->children); } hashIntVoidIteratorDestroy(it, task->children); READ_NUM("id", id); setTaskId(task, id); READ_NUM("endedTasks", endedTasks); setTaskEndedTasks(task, endedTasks); READ_NUM("metaSize", metaSize); if (metaSize > 0) { metadata = malloc(metaSize); } else { metadata = NULL; } READ_BYTES(inputFile, metadata, metaSize); setTaskMetadata(task, metadata, metaSize); if(metadata != NULL){ free(metadata); } READ_NUM("taskState", taskState); setTaskState(task, taskState); DataSpace* dataSpace = createDataSpace(); readDataSpace(inputFile, dataSpace); setTaskDataSpace(task, dataSpace); READ_END return (void *)task; }
static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx ) { uint8_t header[8]; uint32_t length; VC_CONTAINER_BITS_T bits; if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "system_header"); ctx->priv->module->level++; length = READ_U16(ctx, "header_length"); if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 6); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 22, "rate_bound"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 6, "audio_bound"); BITS_SKIP(ctx, &bits, 1, "fixed_flag"); BITS_SKIP(ctx, &bits, 1, "CSPS_flag"); BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag"); BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 5, "video_bound"); BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag"); BITS_SKIP(ctx, &bits, 7, "reserved_bits"); length -= 6; while(length >= 3 && (PEEK_U8(ctx) & 0x80)) { SKIP_U8(ctx, "stream_id"); SKIP_BYTES(ctx, 2); length -= 3; } SKIP_BYTES(ctx, length); ctx->priv->module->level--; return STREAM_STATUS(ctx); }
/***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int size; if(module->status != VC_CONTAINER_SUCCESS) return module->status; if(!module->block_size) { module->block_size = module->default_block_size; module->init = 0; } packet->size = module->block_size; packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN; if(module->init) packet->dts = packet->pts = 0; packet->track = 0; packet->flags = 0; if(flags & VC_CONTAINER_READ_FLAG_SKIP) { size = SKIP_BYTES(p_ctx, module->block_size); module->block_size -= size; module->status = STREAM_STATUS(p_ctx); return module->status; } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(module->block_size, packet->buffer_size); size = READ_BYTES(p_ctx, packet->data, size); module->block_size -= size; packet->size = size; module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); return module->status; }
static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_FOURCC_T codec; uint8_t dummy[12]; uint32_t length; if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; length = BI32(dummy); if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' || dummy[8] != 'R' || dummy[9] != 'V' || dummy[11] != '0') return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; switch(dummy[10]) { case '4': codec = VC_CONTAINER_CODEC_RV40; break; case '3': codec = VC_CONTAINER_CODEC_RV30; break; case '2': codec = VC_CONTAINER_CODEC_RV20; break; case '1': codec = VC_CONTAINER_CODEC_RV10; break; default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } if (!track) return VC_CONTAINER_SUCCESS; status = vc_container_track_allocate_extradata(p_ctx, track, length); if(status != VC_CONTAINER_SUCCESS) return status; if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS; track->format->extradata_size = length; track->format->codec = codec; return STREAM_STATUS(p_ctx); }
void *readChildTask(FILE *inputFile){ Task *task = createTask(); TaskIdList *list = NULL; int metaSize = 0; char *metadata = NULL; int id = -1; READ_BEGIN(inputFile); list = (TaskIdList *)readTaskIdList(inputFile); setTaskDependsOnMe(task, list); taskIdListDestroy(list); list = (TaskIdList *)readTaskIdList(inputFile); setTaskMyDeps(task, list); taskIdListDestroy(list); READ_NUM("id", id); setTaskId(task, id); assert(id != -1); READ_NUM("metaSize", metaSize); if (metaSize > 0) { metadata = malloc(metaSize); } else { metadata = NULL; } READ_BYTES(inputFile, metadata, metaSize); setTaskMetadata(task, metadata, metaSize); if(metadata != NULL){ free(metadata); } READ_END return (void *)task; }
static struct manifest * read_manifest(gzFile f) { struct manifest *mf = create_empty_manifest(); uint32_t magic; READ_INT(4, magic); if (magic != MAGIC) { cc_log("Manifest file has bad magic number %u", magic); goto error; } READ_BYTE(mf->version); if (mf->version != MANIFEST_VERSION) { cc_log("Manifest file has unknown version %u", mf->version); goto error; } READ_BYTE(mf->hash_size); if (mf->hash_size != 16) { // Temporary measure until we support different hash algorithms. cc_log("Manifest file has unsupported hash size %u", mf->hash_size); goto error; } READ_INT(2, mf->reserved); READ_INT(4, mf->n_files); mf->files = x_calloc(mf->n_files, sizeof(*mf->files)); for (uint32_t i = 0; i < mf->n_files; i++) { READ_STR(mf->files[i]); } READ_INT(4, mf->n_file_infos); mf->file_infos = x_calloc(mf->n_file_infos, sizeof(*mf->file_infos)); for (uint32_t i = 0; i < mf->n_file_infos; i++) { READ_INT(4, mf->file_infos[i].index); READ_BYTES(mf->hash_size, mf->file_infos[i].hash); READ_INT(4, mf->file_infos[i].size); READ_INT(8, mf->file_infos[i].mtime); READ_INT(8, mf->file_infos[i].ctime); } READ_INT(4, mf->n_objects); mf->objects = x_calloc(mf->n_objects, sizeof(*mf->objects)); for (uint32_t i = 0; i < mf->n_objects; i++) { READ_INT(4, mf->objects[i].n_file_info_indexes); mf->objects[i].file_info_indexes = x_calloc(mf->objects[i].n_file_info_indexes, sizeof(*mf->objects[i].file_info_indexes)); for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) { READ_INT(4, mf->objects[i].file_info_indexes[j]); } READ_BYTES(mf->hash_size, mf->objects[i].hash.hash); READ_INT(4, mf->objects[i].hash.size); } return mf; error: cc_log("Corrupt manifest file"); free_manifest(mf); return NULL; }
static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; uint8_t header[10]; int64_t scr, scr_base, scr_ext = INT64_C(0); uint64_t pack_offset = STREAM_POSITION(ctx); uint32_t mux_rate, stuffing; VC_CONTAINER_BITS_T bits; VC_CONTAINER_STATUS_T status; if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "pack_header"); module->level++; if (PEEK_U8(ctx) & 0x40) /* program stream */ { if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 10); if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 5, "reserved"); stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length"); SKIP_BYTES(ctx, stuffing); } else /* system stream */ { if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 8); if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; } if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status; module->level--; /* Set or update system_clock_reference, adjust bias if necessary */ scr = scr_base * INT64_C(300) + scr_ext; if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN) module->scr_offset = scr; if (module->scr == VC_CONTAINER_TIME_UNKNOWN) module->scr_bias = -scr; else if (scr < module->scr) module->scr_bias = module->scr - scr; if (module->scr != VC_CONTAINER_TIME_UNKNOWN) { /* system_clock_reference is not necessarily continuous across the entire stream */ if (scr > module->scr) { int64_t data_rate; data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr); if (module->data_rate) { /* Simple moving average over data rate seen so far */ module->data_rate = (module->data_rate * 31 + data_rate) >> 5; } else {
tex_font_metrics_t* load_tex_font_metrics( char *filename ) { tex_font_metrics_t *tfm = NULL; FILE *tfm_file = NULL; int i; char magic[4]; char *err_msg; int endian_check; bool_t swap_bytes; struct char_dims ch_dims; int num_chars; int texture_width, texture_height; char dummy; check_assertion( sizeof(int) == 4, "This architecture's integer size is != 4" ); check_assertion( sizeof(short) == 2, "This architecture's short integer size is != 2" ); check_assertion( sizeof(char) == 1, "This architecture's char size is != 1" ); /* Open file */ tfm_file = fopen( filename, "rb" ); if ( tfm_file == NULL ) { print_warning( MISSING_FILE_WARNING, "Couldn't open font metrics file %s", filename ); return NULL; } tfm = (tex_font_metrics_t*)malloc( sizeof(tex_font_metrics_t) ); check_assertion( tfm != NULL, "out of memory" ); /* Initialize tfm */ for (i=0; i<MAX_TEX_FONT_CHARS; i++) { tfm->char_data[i] = NULL; } /* Check magic number */ READ_BYTES( tfm_file, magic, sizeof(magic), False ); if ( strncmp( magic, "\377tfm", 4 ) != 0 ) { err_msg = "File is not a valid tfm file"; goto bail; } /* Check endian-ness */ READ_BYTES( tfm_file, &endian_check, sizeof(int), False ); if ( endian_check == 0x12345678 ) { swap_bytes = False; } else if ( endian_check == 0x78563412 ) { swap_bytes = True; } else { err_msg = "File is not a valid tfm file"; goto bail; } /* Read in texture_width, texture_height, max_ascent, max_descent */ READ_BYTES( tfm_file, &texture_width, sizeof(int), swap_bytes ); READ_BYTES( tfm_file, &texture_height, sizeof(int), swap_bytes ); READ_BYTES( tfm_file, &tfm->max_ascent, sizeof(int), swap_bytes ); READ_BYTES( tfm_file, &tfm->max_descent, sizeof(int), swap_bytes ); READ_BYTES( tfm_file, &num_chars, sizeof(int), swap_bytes ); for (i=0; i<num_chars; i++) { tfm_char_data_t *cd; scalar_t sstep = 0.5/texture_width; scalar_t tstep = 0.5/texture_height; READ_BYTES( tfm_file, &ch_dims.ch, sizeof(unsigned short), swap_bytes ); READ_BYTES( tfm_file, &ch_dims.w, sizeof(unsigned char), False ); READ_BYTES( tfm_file, &ch_dims.h, sizeof(unsigned char), False ); READ_BYTES( tfm_file, &ch_dims.x_offset, sizeof(char), False ); READ_BYTES( tfm_file, &ch_dims.y_offset, sizeof(char), False ); READ_BYTES( tfm_file, &ch_dims.kern_width, sizeof(char), False ); READ_BYTES( tfm_file, &dummy, sizeof(char), False ); READ_BYTES( tfm_file, &ch_dims.x_pixel, sizeof(short), swap_bytes ); READ_BYTES( tfm_file, &ch_dims.y_pixel, sizeof(short), swap_bytes ); if ( ch_dims.ch >= MAX_TEX_FONT_CHARS ) { err_msg = "Two-byte characters are not supported"; goto bail; } cd = ( tfm_char_data_t * ) malloc( sizeof( tfm_char_data_t ) ); check_assertion( cd != NULL, "out of memory" ); cd->ll = make_point2d( ch_dims.x_offset, ch_dims.y_offset ); cd->lr = make_point2d( cd->ll.x + ch_dims.w, cd->ll.y ); cd->ur = make_point2d( cd->lr.x, cd->lr.y + ch_dims.h ); cd->ul = make_point2d( cd->ur.x - ch_dims.w, cd->ur.y ); cd->tex_ll = make_point2d( ch_dims.x_pixel / (scalar_t)texture_width + sstep, ch_dims.y_pixel / (scalar_t)texture_height + tstep ); cd->tex_lr = make_point2d( cd->tex_ll.x + sstep + ch_dims.w / (scalar_t)texture_width, cd->tex_ll.y + tstep ); cd->tex_ur = make_point2d( cd->tex_lr.x + sstep, cd->tex_lr.y + tstep + ch_dims.h / (scalar_t)texture_height ); cd->tex_ul = make_point2d( cd->tex_ur.x + sstep - ch_dims.w / (scalar_t)texture_width, cd->tex_ur.y + tstep ); cd->kern_width = ch_dims.kern_width; tfm->char_data[ch_dims.ch] = cd; } fclose( tfm_file ); return tfm; bail: if ( tfm != NULL ) { for (i=0; i<MAX_TEX_FONT_CHARS; i++) { if ( tfm->char_data[i] != NULL ) { free( tfm->char_data[i] ); } } free( tfm ); } if ( tfm_file != NULL ) { fclose( tfm_file ); } print_warning( IMPORTANT_WARNING, "Error opening font metrics file `%s': %s\n", filename, err_msg ); return NULL; }