#include #include #include #include #include "wgrib2.h" /* * a simple extension to fopen * * if file is already open, just return the pointer to the file handler * * public domain 2/2008 Wesley Ebisuzaki * v1.1 WNE: upgrade: add read/write detection * - can be stdin and stdout (in previous version) * if pipe - set flush_mode, error checking * v1.2 10/2014 John Howard .. add ffclose * needs to keep track of number of ffopens of a file * when number ffopens and ffclose are the same .. close file * * adding ffcloses is needed to make wgrib2 a callable subroutine * otherwise run out of file handles * * v1.3 4/2015 WNE added @tmp:XXXX temporary files, added for callable wgrib2 * */ extern int flush_mode; struct opened_file { char *name; FILE *handle; int is_read_file; int usage_count; int do_not_close_flag; struct opened_file *next; }; /* memory buffers */ extern size_t mem_buffer_pos[N_mem_buffers]; /* do not initialize opened_file_start in init_globals */ static struct opened_file *opened_file_start = NULL; /* do not initialize opened_file_start in init_globals */ FILE *ffopen(const char *filename, const char *mode) { struct opened_file *ptr; struct stat stat_buf; /* test for pipes */ int is_read_file, i; const char *p; /* see if is a read/write file */ is_read_file = 0; p = mode; while (*p) { if (*p++ == 'r') is_read_file = 1; } if (strcmp(filename,"-") == 0) { if (is_read_file) return stdin; flush_mode = 1; return stdout; } if (strncmp(filename,"@mem:",5) == 0) { fatal_error("option does not suport memory files %s", filename); } /* see if file has already been opened */ ptr = opened_file_start; while (ptr != NULL) { if (strcmp(filename,ptr->name) == 0) { if (ptr->usage_count == 0) { /* reuse of opened file */ if (is_read_file && !ptr->is_read_file) { /* was write, now read */ i = fflush(ptr->handle); if (i) fprintf(stderr,"WARNING: wgrib2 could not flush %s in write->read\n", ptr->name); fseek(ptr->handle, 0L, SEEK_SET); /* rewind file */ } ptr->is_read_file = is_read_file; } if (is_read_file != ptr->is_read_file) { fatal_error("ffopen: file can only read or write not both: %s", ptr->name); } (ptr->usage_count)++; return ptr->handle; } ptr = ptr->next; } ptr = (struct opened_file *) malloc( sizeof(struct opened_file) ); if (ptr == NULL) fatal_error("ffopen: memory allocation problem (%s)", filename); ptr->name = (char *) malloc(strlen(filename) + 1); if (ptr->name == NULL) fatal_error("ffopen: memory allocation problem (%s)", filename); strncpy(ptr->name, filename, strlen(filename)+1); ptr->is_read_file = is_read_file; ptr->usage_count = 1; ptr->do_not_close_flag = 1; // default is not to close file file /* check if filename is @tmp:XXX or just XXX */ if (strlen(filename) > 5 && strncmp(filename, "@tmp:", 5) == 0) { if (is_read_file) { /* remove ptr */ free(ptr->name); free(ptr); fatal_error("ffopen: creating temporary file for read is not a good idea (%s)", filename); } ptr->handle = tmpfile(); /* ISO C to generate temporary file */ if (ptr->handle == NULL) { /* remove ptr */ free(ptr->name); free(ptr); fatal_error("ffopen: could not open tmpfile %s", filename); } /* add ptr to linked list */ ptr->next = opened_file_start; opened_file_start = ptr; return ptr->handle; } /* disk file or pipe */ ptr->handle = fopen(filename,mode); if (ptr->handle == NULL) { free(ptr->name); free(ptr); // fatal_error("ffopen: could not open %s", filename); return NULL; } /* check if output is to a pipe */ if (is_read_file == 0) { if (stat(filename, &stat_buf) == -1) { /* remove ptr */ free(ptr->name); free(ptr); fatal_error("ffopen: could not stat file: %s", filename); } if (S_ISFIFO(stat_buf.st_mode)) { flush_mode = 1; } } /* add ptr to linked list */ ptr->next = opened_file_start; opened_file_start = ptr; return ptr->handle; } int ffclose(FILE *flocal) { struct opened_file *ptr, *last_ptr; if (flocal == stdin || flocal == stdout) return 0; // do not close stdin or stdout last_ptr = NULL; ptr = opened_file_start; while (ptr != NULL) { if (ptr->handle == flocal) { if (ptr->usage_count <= 0) fatal_error_i("ffclose: usage_count = %d <= 0, %d", ptr->usage_count); if (--ptr->usage_count == 0) { // usage_count is zero, can close file name if (ptr->do_not_close_flag == 1) return 0; if (last_ptr == NULL) opened_file_start = ptr->next; else { last_ptr->next = ptr->next; } fclose(ptr->handle); free(ptr->name); free(ptr); } return 0; } last_ptr = ptr; ptr = ptr->next; } return 1; /* not found */ } /* * make a file that has been opened with ffopen persistent * * with a callable wgrib2, may not want to close files for performance * */ int mk_file_persistent(const char *filename) { struct opened_file *ptr; ptr = opened_file_start; while (ptr != NULL) { if (strcmp(filename,ptr->name) == 0) { ptr->do_not_close_flag = 1; return 0; } ptr = ptr->next; } fprintf(stderr,"Warning persistant flag not set %s\n", filename); return 1; } /* * make a persistent file transient (remove persistant flag) */ int mk_file_transient(const char *filename) { struct opened_file *ptr; ptr = opened_file_start; while (ptr != NULL) { if (strcmp(filename,ptr->name) == 0) { ptr->do_not_close_flag = 0; return 0; } ptr = ptr->next; } fprintf(stderr,"Warning persistant not cleared %s\n", filename); return 1; } /* * rewind filename */ int rewind_file(const char *filename) { struct opened_file *ptr; int i; if (strncmp(filename,"@mem:",5) == 0) { i = atoi(filename+5); if (i < 0 || i >= N_mem_buffers || (i == 0 && filename[5] != '0')) { fatal_error_i("rewind_file: @mem:N N from 0..%d", N_mem_buffers-1); } mem_buffer_pos[i] = 0; return 0; } ptr = opened_file_start; while (ptr != NULL) { if (strcmp(filename,ptr->name) == 0) { if (fseek(ptr->handle, 0L, SEEK_SET)) fatal_error("rewind_file: failed (%s)", filename); return 0; } ptr = ptr->next; } return 1; } /* * status of ffopen/ffclose */ void status_ffopen(void) { struct opened_file *ptr; ptr = opened_file_start; while (ptr != NULL) { fprintf(stderr, "file: %s %c:%s file_offset=%ld\n", ptr->name, ptr->is_read_file ? 'r' : 'w', ptr->do_not_close_flag ? "perm":"tran", ftell(ptr->handle) ); ptr = ptr->next; } return; }