mirror of https://github.com/ecmwf/eccodes.git
GRIB-803: grib_index_release does not close the file
This commit is contained in:
parent
cf155e26e5
commit
04c370da62
|
@ -35,387 +35,390 @@ static short next_id=0;
|
|||
|
||||
GRIB_INLINE static int grib_inline_strcmp(const char* a,const char* b)
|
||||
{
|
||||
if (*a != *b) return 1;
|
||||
while((*a!=0 && *b!=0) && *(a) == *(b) ) {a++;b++;}
|
||||
return (*a==0 && *b==0) ? 0 : 1;
|
||||
if (*a != *b) return 1;
|
||||
while((*a!=0 && *b!=0) && *(a) == *(b) ) {a++;b++;}
|
||||
return (*a==0 && *b==0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static grib_file_pool file_pool= {
|
||||
0, /* grib_context* context;*/
|
||||
0, /* grib_file* first;*/
|
||||
0, /* grib_file* current; */
|
||||
0, /* size_t size;*/
|
||||
0, /* int number_of_opened_files;*/
|
||||
GRIB_MAX_OPENED_FILES /* int max_opened_files; */
|
||||
0, /* grib_context* context;*/
|
||||
0, /* grib_file* first;*/
|
||||
0, /* grib_file* current; */
|
||||
0, /* size_t size;*/
|
||||
0, /* int number_of_opened_files;*/
|
||||
GRIB_MAX_OPENED_FILES /* int max_opened_files; */
|
||||
};
|
||||
|
||||
void grib_file_pool_clean()
|
||||
{
|
||||
grib_file *file,*next;
|
||||
grib_file *file,*next;
|
||||
|
||||
if (!file_pool.first) return;
|
||||
if (!file_pool.first) return;
|
||||
|
||||
file=file_pool.first;
|
||||
while(file) {
|
||||
next=file->next;
|
||||
grib_file_delete(file);
|
||||
file=next;
|
||||
}
|
||||
file=file_pool.first;
|
||||
while(file) {
|
||||
next=file->next;
|
||||
grib_file_delete(file);
|
||||
file=next;
|
||||
}
|
||||
}
|
||||
|
||||
static void grib_file_pool_change_id()
|
||||
{
|
||||
grib_file *file;
|
||||
grib_file *file;
|
||||
|
||||
if (!file_pool.first) return;
|
||||
if (!file_pool.first) return;
|
||||
|
||||
file=file_pool.first;
|
||||
while(file) {
|
||||
file->id+=1000;
|
||||
file=file->next;
|
||||
}
|
||||
file=file_pool.first;
|
||||
while(file) {
|
||||
file->id+=1000;
|
||||
file=file->next;
|
||||
}
|
||||
}
|
||||
|
||||
static grib_file* grib_read_file(grib_context *c,FILE* fh,int *err)
|
||||
{
|
||||
short marker=0;
|
||||
short id=0;
|
||||
grib_file* file;
|
||||
*err = grib_read_short(fh,&marker);
|
||||
if(!marker) return NULL;
|
||||
short marker=0;
|
||||
short id=0;
|
||||
grib_file* file;
|
||||
*err = grib_read_short(fh,&marker);
|
||||
if(!marker) return NULL;
|
||||
|
||||
file=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
file->buffer=0;
|
||||
file->name=grib_read_string(c,fh,err);
|
||||
if (*err) return NULL;
|
||||
file=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
file->buffer=0;
|
||||
file->name=grib_read_string(c,fh,err);
|
||||
if (*err) return NULL;
|
||||
|
||||
*err=grib_read_short(fh,&id);
|
||||
file->id=id;
|
||||
if (*err) return NULL;
|
||||
*err=grib_read_short(fh,&id);
|
||||
file->id=id;
|
||||
if (*err) return NULL;
|
||||
|
||||
file->next=grib_read_file(c,fh,err);
|
||||
if (*err) return NULL;
|
||||
file->next=grib_read_file(c,fh,err);
|
||||
if (*err) return NULL;
|
||||
|
||||
return file;
|
||||
return file;
|
||||
}
|
||||
|
||||
static int grib_write_file(FILE *fh,grib_file* file)
|
||||
{
|
||||
int err=0;
|
||||
int err=0;
|
||||
|
||||
if (!file)
|
||||
return grib_write_null_marker(fh);
|
||||
if (!file)
|
||||
return grib_write_null_marker(fh);
|
||||
|
||||
err=grib_write_not_null_marker(fh);
|
||||
if (err) return err;
|
||||
err=grib_write_not_null_marker(fh);
|
||||
if (err) return err;
|
||||
|
||||
err=grib_write_string(fh,file->name);
|
||||
if (err) return err;
|
||||
err=grib_write_string(fh,file->name);
|
||||
if (err) return err;
|
||||
|
||||
err=grib_write_short(fh,(short)file->id);
|
||||
if (err) return err;
|
||||
err=grib_write_short(fh,(short)file->id);
|
||||
if (err) return err;
|
||||
|
||||
return grib_write_file(fh,file->next);
|
||||
return grib_write_file(fh,file->next);
|
||||
}
|
||||
|
||||
grib_file* grib_file_pool_get_files()
|
||||
{
|
||||
return file_pool.first;
|
||||
return file_pool.first;
|
||||
}
|
||||
|
||||
int grib_file_pool_read(grib_context* c,FILE* fh)
|
||||
{
|
||||
int err=0;
|
||||
short marker=0;
|
||||
grib_file* file;
|
||||
int err=0;
|
||||
short marker=0;
|
||||
grib_file* file;
|
||||
|
||||
if (!c) c=grib_context_get_default();
|
||||
if (!c) c=grib_context_get_default();
|
||||
|
||||
err = grib_read_short(fh,&marker);
|
||||
if(!marker) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,
|
||||
"Unable to find file information in index file\n");
|
||||
return GRIB_INVALID_FILE;
|
||||
}
|
||||
err = grib_read_short(fh,&marker);
|
||||
if(!marker) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,
|
||||
"Unable to find file information in index file\n");
|
||||
return GRIB_INVALID_FILE;
|
||||
}
|
||||
|
||||
grib_file_pool_change_id();
|
||||
file=file_pool.first;
|
||||
grib_file_pool_change_id();
|
||||
file=file_pool.first;
|
||||
|
||||
while (file->next)
|
||||
file=file->next;
|
||||
while (file->next)
|
||||
file=file->next;
|
||||
|
||||
file->next=grib_read_file(c,fh,&err);
|
||||
if (err) return err;
|
||||
file->next=grib_read_file(c,fh,&err);
|
||||
if (err) return err;
|
||||
|
||||
return GRIB_SUCCESS;
|
||||
return GRIB_SUCCESS;
|
||||
}
|
||||
|
||||
int grib_file_pool_write(FILE* fh)
|
||||
{
|
||||
int err=0;
|
||||
if (!file_pool.first)
|
||||
return grib_write_null_marker(fh);
|
||||
int err=0;
|
||||
if (!file_pool.first)
|
||||
return grib_write_null_marker(fh);
|
||||
|
||||
err=grib_write_not_null_marker(fh);
|
||||
if (err) return err;
|
||||
err=grib_write_not_null_marker(fh);
|
||||
if (err) return err;
|
||||
|
||||
return grib_write_file(fh,file_pool.first);
|
||||
return grib_write_file(fh,file_pool.first);
|
||||
}
|
||||
|
||||
grib_file* grib_file_open(const char* filename, const char* mode,int* err)
|
||||
{
|
||||
grib_file *file=0,*prev=0;
|
||||
int same_mode=0;
|
||||
int is_new=0;
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
grib_file *file=0,*prev=0;
|
||||
int same_mode=0;
|
||||
int is_new=0;
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
|
||||
if (!file_pool.context) file_pool.context=grib_context_get_default();
|
||||
if (!file_pool.context) file_pool.context=grib_context_get_default();
|
||||
|
||||
if (file_pool.current && !grib_inline_strcmp(filename,file_pool.current->name)) {
|
||||
file=file_pool.current;
|
||||
} else {
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (!grib_inline_strcmp(filename,file->name)) break;
|
||||
prev=file;
|
||||
file=file->next;
|
||||
}
|
||||
if (!file) {
|
||||
is_new=1;
|
||||
file=grib_file_new(file_pool.context,filename,err);
|
||||
if (prev) prev->next=file;
|
||||
file_pool.current=file;
|
||||
if (!prev) file_pool.first=file;
|
||||
file_pool.size++;
|
||||
}
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
if (file_pool.current && !grib_inline_strcmp(filename,file_pool.current->name)) {
|
||||
file=file_pool.current;
|
||||
} else {
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (!grib_inline_strcmp(filename,file->name)) break;
|
||||
prev=file;
|
||||
file=file->next;
|
||||
}
|
||||
if (!file) {
|
||||
is_new=1;
|
||||
file=grib_file_new(file_pool.context,filename,err);
|
||||
if (prev) prev->next=file;
|
||||
file_pool.current=file;
|
||||
if (!prev) file_pool.first=file;
|
||||
file_pool.size++;
|
||||
}
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
|
||||
if (file->mode) same_mode=grib_inline_strcmp(mode,file->mode) ? 0 : 1;
|
||||
if (file->handle && same_mode) {
|
||||
*err=0;
|
||||
return file;
|
||||
}
|
||||
if (file->mode) same_mode=grib_inline_strcmp(mode,file->mode) ? 0 : 1;
|
||||
if (file->handle && same_mode) {
|
||||
*err=0;
|
||||
return file;
|
||||
}
|
||||
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
if (!same_mode && file->handle) {
|
||||
/*printf("========== mode=%s file->mode=%s\n",mode,file->mode);*/
|
||||
fclose(file->handle);
|
||||
}
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
if (!same_mode && file->handle) {
|
||||
/*printf("========== mode=%s file->mode=%s\n",mode,file->mode);*/
|
||||
fclose(file->handle);
|
||||
}
|
||||
|
||||
if (!file->handle) {
|
||||
/*printf("-- opening file %s %s\n",file->name,mode);*/
|
||||
if (!is_new && *mode == 'w') {
|
||||
/* fprintf(stderr,"++++ opening %s as append\n",file->name); */
|
||||
file->handle = fopen(file->name,"a");
|
||||
} else {
|
||||
file->handle = fopen(file->name,mode);
|
||||
/* fprintf(stderr,"++++ opening %s as mode\n",file->name); */
|
||||
}
|
||||
if (!file->handle) {
|
||||
/*printf("-- opening file %s %s\n",file->name,mode);*/
|
||||
if (!is_new && *mode == 'w') {
|
||||
/* fprintf(stderr,"++++ opening %s as append\n",file->name); */
|
||||
file->handle = fopen(file->name,"a");
|
||||
} else {
|
||||
file->handle = fopen(file->name,mode);
|
||||
/* fprintf(stderr,"++++ opening %s as mode\n",file->name); */
|
||||
}
|
||||
|
||||
file->mode=strdup(mode);
|
||||
if (!file->handle) {
|
||||
grib_context_log(file->context,GRIB_LOG_PERROR,"grib_file_open: cannot open file %s",file->name);
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
return NULL;
|
||||
}
|
||||
if (file_pool.context->io_buffer_size) {
|
||||
file->mode=strdup(mode);
|
||||
if (!file->handle) {
|
||||
grib_context_log(file->context,GRIB_LOG_PERROR,"grib_file_open: cannot open file %s",file->name);
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
return NULL;
|
||||
}
|
||||
if (file_pool.context->io_buffer_size) {
|
||||
#ifdef POSIX_MEMALIGN
|
||||
if (posix_memalign((void**)&(file->buffer),sysconf(_SC_PAGESIZE),file_pool.context->io_buffer_size) ) {
|
||||
grib_context_log(file->context,GRIB_LOG_FATAL,"posix_memalign unable to allocate io_buffer\n");
|
||||
}
|
||||
if (posix_memalign((void**)&(file->buffer),sysconf(_SC_PAGESIZE),file_pool.context->io_buffer_size) ) {
|
||||
grib_context_log(file->context,GRIB_LOG_FATAL,"posix_memalign unable to allocate io_buffer\n");
|
||||
}
|
||||
#else
|
||||
file->buffer = (void*)malloc(file_pool.context->io_buffer_size);
|
||||
if (!file->buffer) {
|
||||
grib_context_log(file->context,GRIB_LOG_FATAL,"Unable to allocate io_buffer\n");
|
||||
}
|
||||
file->buffer = (void*)malloc(file_pool.context->io_buffer_size);
|
||||
if (!file->buffer) {
|
||||
grib_context_log(file->context,GRIB_LOG_FATAL,"Unable to allocate io_buffer\n");
|
||||
}
|
||||
#endif
|
||||
setvbuf(file->handle,file->buffer,_IOFBF,file_pool.context->io_buffer_size);
|
||||
}
|
||||
setvbuf(file->handle,file->buffer,_IOFBF,file_pool.context->io_buffer_size);
|
||||
}
|
||||
|
||||
file_pool.number_of_opened_files++;
|
||||
file_pool.number_of_opened_files++;
|
||||
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
|
||||
}
|
||||
return file;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
void grib_file_pool_delete_file(grib_file* file) {
|
||||
grib_file* prev=NULL;
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
|
||||
if (file==file_pool.first) {
|
||||
file_pool.first=file->next;
|
||||
file_pool.current=file->next;
|
||||
} else {
|
||||
if (file==file_pool.first) {
|
||||
file_pool.first=file->next;
|
||||
file_pool.current=file->next;
|
||||
} else {
|
||||
|
||||
prev=file_pool.first;
|
||||
file_pool.current=file_pool.first;
|
||||
while (prev) {
|
||||
if (prev->next==file) break;
|
||||
prev=prev->next;
|
||||
}
|
||||
Assert(prev);
|
||||
prev->next=file->next;
|
||||
}
|
||||
prev=file_pool.first;
|
||||
file_pool.current=file_pool.first;
|
||||
while (prev) {
|
||||
if (prev->next==file) break;
|
||||
prev=prev->next;
|
||||
}
|
||||
Assert(prev);
|
||||
prev->next=file->next;
|
||||
}
|
||||
|
||||
if (file->handle) {
|
||||
if (file->handle) {
|
||||
file_pool.number_of_opened_files--;
|
||||
}
|
||||
grib_file_delete(file);
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
grib_file_delete(file);
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
|
||||
}
|
||||
|
||||
void grib_file_close_force(const char* filename,int* err)
|
||||
{
|
||||
grib_file* file=NULL;
|
||||
grib_file* file=NULL;
|
||||
|
||||
/* fprintf(stderr,"++++ closing file %s\n",filename); */
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
file=grib_get_file(filename,err);
|
||||
fclose(file->handle);
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
file->buffer=0;
|
||||
}
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
file->buffer=0;
|
||||
}
|
||||
file->handle=NULL;
|
||||
file_pool.number_of_opened_files--;
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
|
||||
void grib_file_close(const char* filename,int* err)
|
||||
{
|
||||
grib_file* file=NULL;
|
||||
grib_file* file=NULL;
|
||||
|
||||
/* Performance: keep the files open to avoid opening and closing files when writing the output. */
|
||||
/* So only call fclose() when too many files are open */
|
||||
if ( file_pool.number_of_opened_files > GRIB_MAX_OPENED_FILES ) {
|
||||
/*printf("++ closing file %s\n",filename);*/
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
file=grib_get_file(filename,err);
|
||||
if (file->handle) {
|
||||
if (fclose(file->handle) != 0) {
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
}
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
file->buffer=0;
|
||||
}
|
||||
file->handle=NULL;
|
||||
file_pool.number_of_opened_files--;
|
||||
}
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
/* Performance: keep the files open to avoid opening and closing files when writing the output. */
|
||||
/* So only call fclose() when too many files are open */
|
||||
if ( file_pool.number_of_opened_files > GRIB_MAX_OPENED_FILES ) {
|
||||
/*printf("++ closing file %s\n",filename);*/
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
file=grib_get_file(filename,err);
|
||||
if (file->handle) {
|
||||
if (fclose(file->handle) != 0) {
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
}
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
file->buffer=0;
|
||||
}
|
||||
file->handle=NULL;
|
||||
file_pool.number_of_opened_files--;
|
||||
}
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
}
|
||||
|
||||
void grib_file_close_all(int *err)
|
||||
{
|
||||
grib_file* file = NULL;
|
||||
if (!file_pool.first) return;
|
||||
grib_file* file = NULL;
|
||||
if (!file_pool.first) return;
|
||||
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
|
||||
file = file_pool.first;
|
||||
while (file) {
|
||||
if (file->handle) {
|
||||
if (fclose(file->handle) != 0) {
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
}
|
||||
file->handle=NULL;
|
||||
}
|
||||
file = file->next;
|
||||
}
|
||||
file = file_pool.first;
|
||||
while (file) {
|
||||
if (file->handle) {
|
||||
if (fclose(file->handle) != 0) {
|
||||
*err=GRIB_IO_PROBLEM;
|
||||
}
|
||||
file->handle=NULL;
|
||||
}
|
||||
file = file->next;
|
||||
}
|
||||
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
|
||||
grib_file* grib_get_file(const char* filename,int* err)
|
||||
{
|
||||
grib_file* file=NULL;
|
||||
grib_file* file=NULL;
|
||||
|
||||
if (file_pool.current->name && !grib_inline_strcmp(filename,file_pool.current->name)) {
|
||||
return file_pool.current;
|
||||
}
|
||||
if (file_pool.current->name && !grib_inline_strcmp(filename,file_pool.current->name)) {
|
||||
return file_pool.current;
|
||||
}
|
||||
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (!grib_inline_strcmp(filename,file->name)) break;
|
||||
file=file->next;
|
||||
}
|
||||
if (!file) file=grib_file_new(0,filename,err);
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (!grib_inline_strcmp(filename,file->name)) break;
|
||||
file=file->next;
|
||||
}
|
||||
if (!file) file=grib_file_new(0,filename,err);
|
||||
|
||||
return file;
|
||||
return file;
|
||||
}
|
||||
|
||||
grib_file* grib_find_file(short id)
|
||||
{
|
||||
grib_file* file=NULL;
|
||||
grib_file* file=NULL;
|
||||
|
||||
if (file_pool.current->name && id==file_pool.current->id) {
|
||||
return file_pool.current;
|
||||
}
|
||||
if (file_pool.current->name && id==file_pool.current->id) {
|
||||
return file_pool.current;
|
||||
}
|
||||
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (id==file->id) break;
|
||||
file=file->next;
|
||||
}
|
||||
file=file_pool.first;
|
||||
while (file) {
|
||||
if (id==file->id) break;
|
||||
file=file->next;
|
||||
}
|
||||
|
||||
return file;
|
||||
return file;
|
||||
}
|
||||
|
||||
grib_file* grib_file_new(grib_context* c, const char* name, int* err)
|
||||
{
|
||||
grib_file* file;
|
||||
grib_file* file;
|
||||
|
||||
if (!c) c=grib_context_get_default( );
|
||||
if (!c) c=grib_context_get_default( );
|
||||
|
||||
file=(grib_file*)grib_context_malloc_clear( c,sizeof(grib_file));
|
||||
file=(grib_file*)grib_context_malloc_clear( c,sizeof(grib_file));
|
||||
|
||||
if (!file) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,"grib_file_new: unable to allocate memory");
|
||||
*err=GRIB_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
if (!file) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,"grib_file_new: unable to allocate memory");
|
||||
*err=GRIB_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
|
||||
file->name=strdup(name);
|
||||
file->id=next_id;
|
||||
file->name=strdup(name);
|
||||
file->id=next_id;
|
||||
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
next_id++;
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
next_id++;
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
|
||||
file->mode=0;
|
||||
file->handle=0;
|
||||
file->refcount=0;
|
||||
file->context=c;
|
||||
file->next=0;
|
||||
file->buffer=0;
|
||||
return file;
|
||||
file->mode=0;
|
||||
file->handle=0;
|
||||
file->refcount=0;
|
||||
file->context=c;
|
||||
file->next=0;
|
||||
file->buffer=0;
|
||||
return file;
|
||||
}
|
||||
|
||||
void grib_file_delete(grib_file* file)
|
||||
{
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
if (!file) return;
|
||||
if(file->name) free(file->name);
|
||||
if (file->mode) free(file->mode);
|
||||
if (file->handle) {
|
||||
fclose(file->handle);
|
||||
}
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
}
|
||||
grib_context_free(file->context,file);
|
||||
file=NULL;
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
GRIB_PTHREAD_ONCE(&once,&init);
|
||||
GRIB_MUTEX_LOCK(&mutex1);
|
||||
if (!file) return;
|
||||
if (file->handle) {
|
||||
if (fclose(file->handle) != 0) {
|
||||
perror(file->name);
|
||||
}
|
||||
}
|
||||
if (file->name) free(file->name);
|
||||
if (file->mode) free(file->mode);
|
||||
|
||||
if (file->buffer) {
|
||||
free(file->buffer);
|
||||
}
|
||||
grib_context_free(file->context,file);
|
||||
file=NULL;
|
||||
GRIB_MUTEX_UNLOCK(&mutex1);
|
||||
}
|
||||
|
|
428
src/grib_index.c
428
src/grib_index.c
|
@ -907,7 +907,7 @@ int grib_index_search_same(grib_index* index, grib_handle* h)
|
|||
}
|
||||
|
||||
int grib_index_add_file(grib_index* index,const char* filename) {
|
||||
return _codes_index_add_file(index,filename,CODES_GRIB);
|
||||
return _codes_index_add_file(index,filename,CODES_GRIB);
|
||||
}
|
||||
|
||||
grib_handle* new_message_from_file(int message_type, grib_context *c, FILE *f, int *error)
|
||||
|
@ -922,155 +922,157 @@ grib_handle* new_message_from_file(int message_type, grib_context *c, FILE *f, i
|
|||
|
||||
int _codes_index_add_file(grib_index* index,const char* filename,int message_type)
|
||||
{
|
||||
double dval;
|
||||
size_t svallen;
|
||||
long length,lval;
|
||||
char buf[1024]={0,};
|
||||
int err=0;
|
||||
grib_file* indfile;
|
||||
grib_file* newfile;
|
||||
double dval;
|
||||
size_t svallen;
|
||||
long length,lval;
|
||||
char buf[1024]={0,};
|
||||
int err=0;
|
||||
grib_file* indfile;
|
||||
grib_file* newfile;
|
||||
|
||||
grib_index_key* index_key=NULL;
|
||||
grib_handle* h=NULL;
|
||||
grib_field* field;
|
||||
grib_field_tree* field_tree;
|
||||
grib_file* file=NULL;
|
||||
grib_context* c;
|
||||
grib_index_key* index_key=NULL;
|
||||
grib_handle* h=NULL;
|
||||
grib_field* field;
|
||||
grib_field_tree* field_tree;
|
||||
grib_file* file=NULL;
|
||||
grib_context* c;
|
||||
|
||||
if (!index) return GRIB_NULL_INDEX;
|
||||
c=index->context;
|
||||
c=index->context;
|
||||
|
||||
file=grib_file_open(filename,"r",&err);
|
||||
file=grib_file_open(filename,"r",&err);
|
||||
|
||||
if (!file || !file->handle) return err;
|
||||
if (!file || !file->handle) return err;
|
||||
|
||||
if (!index->files) {
|
||||
grib_filesid++;
|
||||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
index->files=newfile;
|
||||
} else {
|
||||
indfile=index->files;
|
||||
while(indfile) {
|
||||
if (!strcmp(indfile->name,file->name)) return 0;
|
||||
indfile=indfile->next;
|
||||
}
|
||||
indfile=index->files;
|
||||
while(indfile->next) indfile=indfile->next;
|
||||
grib_filesid++;
|
||||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
indfile->next=newfile;
|
||||
}
|
||||
if (!index->files) {
|
||||
grib_filesid++;
|
||||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
newfile->handle = file->handle;
|
||||
index->files=newfile;
|
||||
} else {
|
||||
indfile=index->files;
|
||||
while(indfile) {
|
||||
if (!strcmp(indfile->name,file->name)) return 0;
|
||||
indfile=indfile->next;
|
||||
}
|
||||
indfile=index->files;
|
||||
while(indfile->next) indfile=indfile->next;
|
||||
grib_filesid++;
|
||||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
newfile->handle = file->handle;
|
||||
indfile->next=newfile;
|
||||
}
|
||||
|
||||
fseeko(file->handle,0,SEEK_SET);
|
||||
fseeko(file->handle,0,SEEK_SET);
|
||||
|
||||
while ((h=new_message_from_file(message_type, c, file->handle, &err))!=NULL) {
|
||||
grib_string_list* v=0;
|
||||
index_key=index->keys;
|
||||
field_tree=index->fields;
|
||||
index_key->value[0]=0;
|
||||
while ((h=new_message_from_file(message_type, c, file->handle, &err))!=NULL) {
|
||||
grib_string_list* v=0;
|
||||
index_key=index->keys;
|
||||
field_tree=index->fields;
|
||||
index_key->value[0]=0;
|
||||
|
||||
while (index_key) {
|
||||
if (index_key->type==GRIB_TYPE_UNDEFINED) {
|
||||
err=grib_get_native_type(h,index_key->name,&(index_key->type));
|
||||
if (err) index_key->type=GRIB_TYPE_STRING;
|
||||
}
|
||||
svallen=1024;
|
||||
switch (index_key->type) {
|
||||
case GRIB_TYPE_STRING:
|
||||
err=grib_get_string(h,index_key->name,buf,&svallen);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
break;
|
||||
case GRIB_TYPE_LONG:
|
||||
err=grib_get_long(h,index_key->name,&lval);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
else sprintf(buf,"%ld",lval);
|
||||
break;
|
||||
case GRIB_TYPE_DOUBLE:
|
||||
err=grib_get_double(h,index_key->name,&dval);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
else sprintf(buf,"%g",dval);
|
||||
break;
|
||||
default :
|
||||
err=GRIB_WRONG_TYPE;
|
||||
return err;
|
||||
}
|
||||
if (err && err != GRIB_NOT_FOUND) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,"unable to create index. \"%s\": %s",index_key->name,grib_get_error_message(err));
|
||||
return err;
|
||||
}
|
||||
while (index_key) {
|
||||
if (index_key->type==GRIB_TYPE_UNDEFINED) {
|
||||
err=grib_get_native_type(h,index_key->name,&(index_key->type));
|
||||
if (err) index_key->type=GRIB_TYPE_STRING;
|
||||
}
|
||||
svallen=1024;
|
||||
switch (index_key->type) {
|
||||
case GRIB_TYPE_STRING:
|
||||
err=grib_get_string(h,index_key->name,buf,&svallen);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
break;
|
||||
case GRIB_TYPE_LONG:
|
||||
err=grib_get_long(h,index_key->name,&lval);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
else sprintf(buf,"%ld",lval);
|
||||
break;
|
||||
case GRIB_TYPE_DOUBLE:
|
||||
err=grib_get_double(h,index_key->name,&dval);
|
||||
if (err==GRIB_NOT_FOUND) sprintf(buf,GRIB_KEY_UNDEF);
|
||||
else sprintf(buf,"%g",dval);
|
||||
break;
|
||||
default :
|
||||
err=GRIB_WRONG_TYPE;
|
||||
return err;
|
||||
}
|
||||
if (err && err != GRIB_NOT_FOUND) {
|
||||
grib_context_log(c,GRIB_LOG_ERROR,"unable to create index. \"%s\": %s",index_key->name,grib_get_error_message(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!index_key->values->value) {
|
||||
index_key->values->value=grib_context_strdup(c,buf);
|
||||
index_key->values_count++;
|
||||
} else {
|
||||
v=index_key->values;
|
||||
while (v->next && strcmp(v->value,buf)) v=v->next;
|
||||
if (strcmp(v->value,buf)) {
|
||||
index_key->values_count++;
|
||||
if (v->next) v=v->next;
|
||||
v->next=(grib_string_list*)grib_context_malloc_clear(c,sizeof(grib_string_list));
|
||||
v->next->value=grib_context_strdup(c,buf);
|
||||
}
|
||||
}
|
||||
if (!index_key->values->value) {
|
||||
index_key->values->value=grib_context_strdup(c,buf);
|
||||
index_key->values_count++;
|
||||
} else {
|
||||
v=index_key->values;
|
||||
while (v->next && strcmp(v->value,buf)) v=v->next;
|
||||
if (strcmp(v->value,buf)) {
|
||||
index_key->values_count++;
|
||||
if (v->next) v=v->next;
|
||||
v->next=(grib_string_list*)grib_context_malloc_clear(c,sizeof(grib_string_list));
|
||||
v->next->value=grib_context_strdup(c,buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (!field_tree->value) {
|
||||
field_tree->value=grib_context_strdup(c,buf);
|
||||
} else {
|
||||
while (field_tree->next &&
|
||||
(field_tree->value==NULL ||
|
||||
strcmp(field_tree->value,buf)))
|
||||
field_tree=field_tree->next;
|
||||
if (!field_tree->value) {
|
||||
field_tree->value=grib_context_strdup(c,buf);
|
||||
} else {
|
||||
while (field_tree->next &&
|
||||
(field_tree->value==NULL ||
|
||||
strcmp(field_tree->value,buf)))
|
||||
field_tree=field_tree->next;
|
||||
|
||||
if (!field_tree->value || strcmp(field_tree->value,buf)){
|
||||
field_tree->next=
|
||||
(grib_field_tree*)grib_context_malloc_clear(c,
|
||||
sizeof(grib_field_tree));
|
||||
field_tree=field_tree->next;
|
||||
field_tree->value=grib_context_strdup(c,buf);
|
||||
}
|
||||
}
|
||||
if (!field_tree->value || strcmp(field_tree->value,buf)){
|
||||
field_tree->next=
|
||||
(grib_field_tree*)grib_context_malloc_clear(c,
|
||||
sizeof(grib_field_tree));
|
||||
field_tree=field_tree->next;
|
||||
field_tree->value=grib_context_strdup(c,buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (index_key->next) {
|
||||
if (!field_tree->next_level) {
|
||||
field_tree->next_level=
|
||||
(grib_field_tree*)grib_context_malloc_clear(c,sizeof(grib_field_tree));
|
||||
}
|
||||
field_tree=field_tree->next_level;
|
||||
}
|
||||
index_key=index_key->next;
|
||||
}
|
||||
if (index_key->next) {
|
||||
if (!field_tree->next_level) {
|
||||
field_tree->next_level=
|
||||
(grib_field_tree*)grib_context_malloc_clear(c,sizeof(grib_field_tree));
|
||||
}
|
||||
field_tree=field_tree->next_level;
|
||||
}
|
||||
index_key=index_key->next;
|
||||
}
|
||||
|
||||
field=(grib_field*)grib_context_malloc_clear(c,sizeof(grib_field));
|
||||
field->file=file;
|
||||
index->count++;
|
||||
field->offset=h->offset;;
|
||||
field=(grib_field*)grib_context_malloc_clear(c,sizeof(grib_field));
|
||||
field->file=file;
|
||||
index->count++;
|
||||
field->offset=h->offset;;
|
||||
|
||||
err=grib_get_long(h,"totalLength",&length);
|
||||
if (err) return err;
|
||||
field->length=length;
|
||||
err=grib_get_long(h,"totalLength",&length);
|
||||
if (err) return err;
|
||||
field->length=length;
|
||||
|
||||
|
||||
if (field_tree->field) {
|
||||
grib_field* pfield=field_tree->field;
|
||||
while (pfield->next) pfield=pfield->next;
|
||||
pfield->next=field;
|
||||
} else
|
||||
field_tree->field=field;
|
||||
if (field_tree->field) {
|
||||
grib_field* pfield=field_tree->field;
|
||||
while (pfield->next) pfield=pfield->next;
|
||||
pfield->next=field;
|
||||
} else
|
||||
field_tree->field=field;
|
||||
|
||||
if (h) grib_handle_delete(h);
|
||||
if (h) grib_handle_delete(h);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
grib_file_close(file->name,&err);
|
||||
grib_file_close(file->name,&err);
|
||||
|
||||
if (err) return err;
|
||||
index->rewind=1;
|
||||
return GRIB_SUCCESS;
|
||||
|
||||
if (err) return err;
|
||||
index->rewind=1;
|
||||
return GRIB_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -1103,6 +1105,7 @@ int grib_index_add_file(grib_index* index, const char* filename)
|
|||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
newfile->handle = file->handle;
|
||||
index->files=newfile;
|
||||
} else {
|
||||
indfile=index->files;
|
||||
|
@ -1116,6 +1119,7 @@ int grib_index_add_file(grib_index* index, const char* filename)
|
|||
newfile=(grib_file*)grib_context_malloc_clear(c,sizeof(grib_file));
|
||||
newfile->id=grib_filesid;
|
||||
newfile->name=strdup(file->name);
|
||||
newfile->handle = file->handle;
|
||||
indfile->next=newfile;
|
||||
}
|
||||
|
||||
|
@ -1432,36 +1436,36 @@ int grib_index_select_string(grib_index* index,const char* skey,char* value)
|
|||
}
|
||||
|
||||
grib_handle* grib_index_get_handle(grib_field* field,int *err) {
|
||||
return codes_index_get_handle(field,CODES_GRIB,err);
|
||||
return codes_index_get_handle(field,CODES_GRIB,err);
|
||||
}
|
||||
|
||||
grib_handle* codes_index_get_handle(grib_field* field,int message_type,int *err)
|
||||
{
|
||||
grib_handle* h=NULL;
|
||||
typedef grib_handle* (*message_new_proc) ( grib_context*,FILE*,int,int* );
|
||||
message_new_proc message_new=NULL;
|
||||
Assert(field->file);
|
||||
grib_file_open(field->file->name,"r",err);
|
||||
grib_handle* h=NULL;
|
||||
typedef grib_handle* (*message_new_proc) ( grib_context*,FILE*,int,int* );
|
||||
message_new_proc message_new=NULL;
|
||||
Assert(field->file);
|
||||
grib_file_open(field->file->name,"r",err);
|
||||
|
||||
if (*err!=GRIB_SUCCESS) return NULL;
|
||||
switch (message_type) {
|
||||
case CODES_GRIB:
|
||||
message_new=grib_new_from_file;
|
||||
break;
|
||||
case CODES_BUFR:
|
||||
Assert(!"_codes_index_add_file for BUFR: not yet implemented");
|
||||
/* message_new=bufr_new_from_file; */
|
||||
break;
|
||||
default :
|
||||
Assert(0);
|
||||
}
|
||||
if (*err!=GRIB_SUCCESS) return NULL;
|
||||
switch (message_type) {
|
||||
case CODES_GRIB:
|
||||
message_new=grib_new_from_file;
|
||||
break;
|
||||
case CODES_BUFR:
|
||||
Assert(!"_codes_index_add_file for BUFR: not yet implemented");
|
||||
/* message_new=bufr_new_from_file; */
|
||||
break;
|
||||
default :
|
||||
Assert(0);
|
||||
}
|
||||
|
||||
fseeko(field->file->handle,field->offset,SEEK_SET);
|
||||
h=message_new(0,field->file->handle,0,err);
|
||||
if (*err!=GRIB_SUCCESS) return NULL;
|
||||
fseeko(field->file->handle,field->offset,SEEK_SET);
|
||||
h=message_new(0,field->file->handle,0,err);
|
||||
if (*err!=GRIB_SUCCESS) return NULL;
|
||||
|
||||
grib_file_close(field->file->name,err);
|
||||
return h;
|
||||
grib_file_close(field->file->name,err);
|
||||
return h;
|
||||
}
|
||||
|
||||
static int grib_index_execute(grib_index* index)
|
||||
|
@ -1540,9 +1544,9 @@ static void grib_dump_field(FILE* fout, grib_field* field)
|
|||
if (!field) return;
|
||||
fprintf(fout, "field name = %s\n", field->file->name);
|
||||
/*fprintf(fout, "field FID = %d\n", field->file->id);
|
||||
* fprintf(fout, "field offset = %ld\n", field->offset);
|
||||
* fprintf(fout, "field length = %ld\n", field->length);
|
||||
*/
|
||||
* fprintf(fout, "field offset = %ld\n", field->offset);
|
||||
* fprintf(fout, "field length = %ld\n", field->length);
|
||||
*/
|
||||
|
||||
grib_dump_field(fout, field->next);
|
||||
}
|
||||
|
@ -1569,7 +1573,7 @@ int grib_index_dump_file(FILE* fout, const char* filename)
|
|||
Assert(filename);
|
||||
index = grib_index_read(c, filename, &err);
|
||||
if (err) return err;
|
||||
|
||||
|
||||
/* To get the GRIB files referenced we have */
|
||||
/* to resort to low level reading of the index file! */
|
||||
fh=fopen(filename,"r");
|
||||
|
@ -1592,10 +1596,10 @@ int grib_index_dump_file(FILE* fout, const char* filename)
|
|||
}
|
||||
fclose(fh);
|
||||
}
|
||||
|
||||
|
||||
grib_index_dump(fout, index);
|
||||
grib_index_delete(index);
|
||||
|
||||
|
||||
return GRIB_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1612,9 +1616,9 @@ void grib_index_dump(FILE* fout, grib_index* index)
|
|||
grib_dump_index_keys(fout, index->keys);
|
||||
|
||||
/*
|
||||
* fprintf(fout, "Index field tree:\n");
|
||||
* grib_dump_field_tree(fout, index->fields);
|
||||
*/
|
||||
* fprintf(fout, "Index field tree:\n");
|
||||
* grib_dump_field_tree(fout, index->fields);
|
||||
*/
|
||||
|
||||
fprintf(fout, "Index count = %d\n", index->count);
|
||||
}
|
||||
|
@ -1631,68 +1635,68 @@ char* grib_get_field_file(grib_index* index,off_t *offset)
|
|||
|
||||
grib_handle* grib_handle_new_from_index(grib_index* index,int *err)
|
||||
{
|
||||
return codes_new_from_index(index,CODES_GRIB,err);
|
||||
return codes_new_from_index(index,CODES_GRIB,err);
|
||||
}
|
||||
|
||||
grib_handle* codes_new_from_index(grib_index* index,int message_type,int *err)
|
||||
{
|
||||
grib_index_key* keys;
|
||||
grib_field_list *fieldset,*next;
|
||||
grib_handle* h=NULL;
|
||||
grib_context* c=NULL;
|
||||
grib_index_key* keys;
|
||||
grib_field_list *fieldset,*next;
|
||||
grib_handle* h=NULL;
|
||||
grib_context* c=NULL;
|
||||
|
||||
if (!index) return NULL;
|
||||
c=index->context;
|
||||
if (!index->rewind) {
|
||||
if (!index->current) {
|
||||
*err=GRIB_END_OF_INDEX;
|
||||
return NULL;
|
||||
}
|
||||
if (!index) return NULL;
|
||||
c=index->context;
|
||||
if (!index->rewind) {
|
||||
if (!index->current) {
|
||||
*err=GRIB_END_OF_INDEX;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (index->current->field->next)
|
||||
index->current->field=index->current->field->next;
|
||||
else if(index->current->next)
|
||||
index->current=index->current->next;
|
||||
else {*err=GRIB_END_OF_INDEX;return NULL;}
|
||||
if (index->current->field->next)
|
||||
index->current->field=index->current->field->next;
|
||||
else if(index->current->next)
|
||||
index->current=index->current->next;
|
||||
else {*err=GRIB_END_OF_INDEX;return NULL;}
|
||||
|
||||
h=codes_index_get_handle(index->current->field,message_type,err);
|
||||
return h;
|
||||
}
|
||||
h=codes_index_get_handle(index->current->field,message_type,err);
|
||||
return h;
|
||||
}
|
||||
|
||||
if (!index->fieldset) {
|
||||
index->fieldset=(grib_field_list*)grib_context_malloc_clear(index->context,
|
||||
sizeof(grib_field_list));
|
||||
if (!index->fieldset) {
|
||||
grib_context_log(index->context,GRIB_LOG_ERROR,
|
||||
"unable to allocate %d bytes",
|
||||
sizeof(grib_field_list));
|
||||
return NULL;
|
||||
}
|
||||
index->current=index->fieldset;
|
||||
} else {
|
||||
fieldset=index->fieldset;
|
||||
while(fieldset->next) {
|
||||
next=fieldset->next;
|
||||
grib_context_free(c,fieldset);
|
||||
fieldset=next;
|
||||
}
|
||||
fieldset->field=NULL;
|
||||
fieldset->next=NULL;
|
||||
index->fieldset=fieldset;
|
||||
index->current=fieldset;
|
||||
}
|
||||
if (!index->fieldset) {
|
||||
index->fieldset=(grib_field_list*)grib_context_malloc_clear(index->context,
|
||||
sizeof(grib_field_list));
|
||||
if (!index->fieldset) {
|
||||
grib_context_log(index->context,GRIB_LOG_ERROR,
|
||||
"unable to allocate %d bytes",
|
||||
sizeof(grib_field_list));
|
||||
return NULL;
|
||||
}
|
||||
index->current=index->fieldset;
|
||||
} else {
|
||||
fieldset=index->fieldset;
|
||||
while(fieldset->next) {
|
||||
next=fieldset->next;
|
||||
grib_context_free(c,fieldset);
|
||||
fieldset=next;
|
||||
}
|
||||
fieldset->field=NULL;
|
||||
fieldset->next=NULL;
|
||||
index->fieldset=fieldset;
|
||||
index->current=fieldset;
|
||||
}
|
||||
|
||||
*err=GRIB_END_OF_INDEX;
|
||||
h=NULL;
|
||||
keys=index->keys;
|
||||
*err=GRIB_END_OF_INDEX;
|
||||
h=NULL;
|
||||
keys=index->keys;
|
||||
|
||||
if ((*err=grib_index_execute(index))==GRIB_SUCCESS) {
|
||||
if ((*err=grib_index_execute(index))==GRIB_SUCCESS) {
|
||||
|
||||
if (!index->fieldset) {*err=GRIB_END_OF_INDEX;return NULL;}
|
||||
index->current=index->fieldset;
|
||||
h=codes_index_get_handle(index->current->field,message_type,err);
|
||||
}
|
||||
return h;
|
||||
if (!index->fieldset) {*err=GRIB_END_OF_INDEX;return NULL;}
|
||||
index->current=index->fieldset;
|
||||
h=codes_index_get_handle(index->current->field,message_type,err);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
void grib_index_rewind(grib_index* index)
|
||||
|
|
Loading…
Reference in New Issue