/* * Grace - GRaphing, Advanced Computation and Exploration of data * * Home page: http://plasma-gate.weizmann.ac.il/Grace/ * * Copyright (c) 1991-1995 Paul J Turner, Portland, OR * Copyright (c) 1996-2002 Grace Development Team * * Maintained by Evgeny Stambulchik * * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * * read data files * */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_NETCDF # include #endif #include "globals.h" #include "utils.h" #include "files.h" #include "ssdata.h" #include "graphs.h" #include "graphutils.h" #include "parser.h" #include "protos.h" #define MAXERR 5 /* * number of rows to allocate for each call to realloc */ #define BUFSIZE 512 /* * number of bytes in each line chunk * (should be related to system pipe size, typically 4K) */ #ifndef PIPE_BUF # define PIPE_BUF 4096 #endif #define CHUNKSIZE 2*PIPE_BUF char *close_input; /* name of real-time input to close */ struct timeval read_begin = {0l, 0l}; /* used to check too long inputs */ static Input_buffer dummy_ib = {-1, 0, 0, 0, 0, NULL, 0, 0, NULL, 0l}; int nb_rt = 0; /* number of real time file descriptors */ Input_buffer *ib_tbl = 0; /* table for each open input */ int ib_tblsize = 0; /* number of elements in ib_tbl */ static int time_spent(void); static int expand_ib_tbl(void); static int expand_line_buffer(char **adrBuf, int *ptrSize, char **adrPtr); static int reopen_real_time_input(Input_buffer *ib); static int read_real_time_lines(Input_buffer *ib); static int process_complete_lines(Input_buffer *ib); static int read_long_line(FILE *fp, char **linebuf, int *buflen); static int uniread(FILE *fp, int load_type, char *label); /* * part of the time sliced already spent in milliseconds */ static int time_spent(void) { struct timeval now; gettimeofday(&now, NULL); return 1000 * (now.tv_sec - read_begin.tv_sec) + (now.tv_usec - read_begin.tv_usec) / 1000; } /* * expand the table of monitored real time inputs */ static int expand_ib_tbl(void) { int i, new_size; Input_buffer *new_tbl; new_size = (ib_tblsize > 0) ? 2*ib_tblsize : 5; new_tbl = xcalloc(new_size, sizeof(Input_buffer)); if (new_tbl == NULL) { return RETURN_FAILURE; } for (i = 0; i < new_size; i++) { new_tbl[i] = (i < ib_tblsize) ? ib_tbl[i] : dummy_ib; } if (ib_tblsize > 0) { xfree((void *) ib_tbl); } ib_tbl = new_tbl; ib_tblsize = new_size; return RETURN_SUCCESS; } /* * expand a line buffer */ static int expand_line_buffer(char **adrBuf, int *ptrSize, char **adrPtr) { char *newbuf; int newsize; newsize = *ptrSize + CHUNKSIZE; newbuf = xmalloc(newsize); if (newbuf == 0) { return RETURN_FAILURE; } if (*ptrSize == 0) { /* this is the first time through */ if (adrPtr) { *adrPtr = newbuf; } } else { /* we are expanding an existing line */ strncpy(newbuf, *adrBuf, *ptrSize); if (adrPtr) { *adrPtr += newbuf - *adrBuf; } xfree(*adrBuf); } *adrBuf = newbuf; *ptrSize = newsize; return RETURN_SUCCESS; } /* * reopen an Input_buffer (surely a fifo) */ static int reopen_real_time_input(Input_buffer *ib) { int fd; char buf[256]; /* in order to avoid race conditions (broken pipe on the write side), we open a new file descriptor before closing the existing one */ fd = open(ib->name, O_RDONLY | O_NONBLOCK); if (fd < 0) { sprintf(buf, "Can't reopen real time input %s", ib->name); errmsg(buf); unregister_real_time_input(ib->name); return RETURN_FAILURE; } #ifndef NONE_GUI xunregister_rti((XtInputId) ib->id); #endif /* swapping the file descriptors */ close(ib->fd); ib->fd = fd; #ifndef NONE_GUI xregister_rti(ib); #endif return RETURN_SUCCESS; } /* * unregister a file descriptor no longer monitored * (since Input_buffer structures dedicated to static inputs * are not kept in the table, it is not an error to unregister * an input not already registered) */ void unregister_real_time_input(const char *name) { Input_buffer *ib; int l1, l2; l1 = strlen(name); nb_rt = 0; for (ib = ib_tbl; ib < ib_tbl + ib_tblsize; ib++) { l2 = (ib->name == 0) ? -1 : strlen(ib->name); if (l1 == l2 && strcmp (name, ib->name) == 0) { /* name is usually the same pointer as ib->name so we cannot */ /* free the string and output the message using name afterwards */ #ifndef NONE_GUI xunregister_rti((XtInputId) ib->id); #endif close(ib->fd); ib->fd = -1; xfree(ib->name); ib->name = NULL; } else { /* this descriptor is still in use */ nb_rt++; } } } /* * register a file descriptor for monitoring */ int register_real_time_input(int fd, const char *name, int reopen) { Input_buffer *ib; char buf[256]; /* some safety checks */ if (fd < 0) { sprintf(buf, "%s : internal error, wrong file descriptor", name); errmsg(buf); return RETURN_FAILURE; } #ifdef HAVE_FCNTL if (fcntl(fd, F_GETFL) & O_WRONLY) { fprintf(stderr, "Descriptor %d not open for reading\n", fd); return RETURN_FAILURE; } #endif /* remove previous entry for the same set if any */ unregister_real_time_input(name); /* find an empty slot in the table */ for (ib = ib_tbl; ib < ib_tbl + ib_tblsize; ib++) { if (ib->fd == fd) { sprintf(buf, "%s : internal error, file descriptor already in use", name); errmsg(buf); return RETURN_FAILURE; } else if (ib->fd < 0) { break; } } if (ib == ib_tbl + ib_tblsize) { /* the table was full, we expand it */ int old_size = ib_tblsize; if (expand_ib_tbl() != RETURN_SUCCESS) { return RETURN_FAILURE; } ib = ib_tbl + old_size; } /* we keep the current buffer (even if 0), and only say everything is available */ ib->fd = fd; ib->errors = 0; ib->lineno = 0; ib->zeros = 0; ib->reopen = reopen; ib->name = copy_string(ib->name, name); ib->used = 0; #ifndef NONE_GUI xregister_rti (ib); #endif nb_rt++; return RETURN_SUCCESS; } /* * read a real-time line (but do not process it) */ static int read_real_time_lines(Input_buffer *ib) { char *cursor; int available, nbread; char buf[256]; cursor = ib->buf + ib->used; available = ib->size - ib->used; /* have we enough space to store the characters ? */ if (available < 2) { if (expand_line_buffer(&(ib->buf), &(ib->size), &cursor) != RETURN_SUCCESS) { return RETURN_FAILURE; } available = ib->buf + ib->size - cursor; } /* read as much as possible */ nbread = read(ib->fd, (void *) cursor, available - 1); if (nbread < 0) { sprintf(buf, "%s : read error on real time input", ib->name); errmsg(buf); return RETURN_FAILURE; } else { if (nbread == 0) { ib->zeros++; } else { ib->zeros = 0; ib->used += nbread; ib->buf[ib->used] = '\0'; } } return RETURN_SUCCESS; } /* * process complete lines that have already been read */ static int process_complete_lines(Input_buffer *ib) { int line_corrupted; char *begin_of_line, *end_of_line; char buf[256]; if (ib->used <= 0) { return RETURN_SUCCESS; } end_of_line = NULL; do { /* loop over the embedded lines */ begin_of_line = (end_of_line == NULL) ? ib->buf : (end_of_line + 1); end_of_line = begin_of_line; line_corrupted = 0; while (end_of_line != NULL && *end_of_line != '\n') { /* trying to find a complete line */ if (end_of_line == ib->buf + ib->used) { end_of_line = NULL; } else { if (*end_of_line == '\0') { line_corrupted = 1; } ++end_of_line; } } if (end_of_line != NULL) { /* we have a whole line */ ++(ib->lineno); *end_of_line = '\0'; close_input = NULL; if (line_corrupted || scanner(begin_of_line)) { sprintf(buf, "Error at line %d", ib->lineno); errmsg(buf); ++(ib->errors); if (ib->errors > MAXERR) { #ifndef NONE_GUI /* this prevents from being called recursively by the inner X loop of yesno */ xunregister_rti((XtInputId) ib->id); #endif if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) { close_input = copy_string(close_input, ""); } #ifndef NONE_GUI xregister_rti(ib); #endif ib->errors = 0; } } if (close_input != NULL) { /* something should be closed */ if (close_input[0] == '\0') { unregister_real_time_input(ib->name); } else { unregister_real_time_input(close_input); } xfree(close_input); close_input = NULL; if (ib->fd < 0) { /* we have closed ourselves */ return RETURN_SUCCESS; } } } } while (end_of_line != NULL); if (end_of_line != NULL) { /* the line has just been processed */ begin_of_line = end_of_line + 1; } if (begin_of_line > ib->buf) { /* move the remaining data to the beginning */ ib->used -= begin_of_line - ib->buf; memmove(ib->buf, begin_of_line, ib->used); ib->buf[ib->used] = '\0'; } return RETURN_SUCCESS; } int real_time_under_monitoring(void) { return nb_rt > 0; } /* * monitor the set of registered file descriptors for pending input */ int monitor_input(Input_buffer *tbl, int tblsize, int no_wait) { Input_buffer *ib; fd_set rfds; int remaining; struct timeval timeout; int highest, first_time, retsel; /* we don't want to get stuck here, we memorize the start date and will check we do not exceed our allowed time slice */ gettimeofday(&read_begin, NULL); first_time = 1; retsel = 1; while (((time_spent() < timer_delay) || first_time) && retsel > 0) { /* register all the monitored descriptors */ highest = -1; FD_ZERO(&rfds); for (ib = tbl; ib < tbl + tblsize; ib++) { if (ib->fd >= 0) { FD_SET(ib->fd, &rfds); if (ib->fd > highest) { highest = ib->fd; } } } if (highest < 0) { /* there's nothing to do */ return RETURN_SUCCESS; } if (no_wait) { /* just check for available data without waiting */ remaining = 0; } else { /* wait until data or end of time slice arrive */ remaining = timer_delay - time_spent(); if (remaining < 0) { remaining = 0; } } timeout.tv_sec = remaining / 1000; timeout.tv_usec = 1000l * (remaining % 1000); retsel = select(highest + 1, &rfds, NULL, NULL, &timeout); for (ib = tbl; ((time_spent() < timer_delay) || first_time) && ib < tbl + tblsize; ib++) { if (ib->fd >= 0 && FD_ISSET(ib->fd, &rfds)) { /* there is pending input */ if (read_real_time_lines(ib) != RETURN_SUCCESS || process_complete_lines(ib) != RETURN_SUCCESS) { return RETURN_FAILURE; } if (ib->zeros >= 5) { /* we were told five times something happened, but never got any byte : we assume the pipe (or whatever) has been closed by the peer */ if (ib->reopen) { /* we should reset the input buffer, in case the peer also reopens it */ if (reopen_real_time_input(ib) != RETURN_SUCCESS) { return RETURN_FAILURE; } } else { unregister_real_time_input(ib->name); } /* we have changed the table, we should end the loop */ break; } } } /* after one pass, we obey timeout */ first_time = 0; } return RETURN_SUCCESS; } /* replacement for fgets() to fix up reading DOS text files */ char *grace_fgets(char *s, int size, FILE *stream) { int slen; char *endptr; s = fgets(s, size, stream); if (!s) { return NULL; } slen = strlen(s); if (slen >= 2) { endptr = s + slen - 2; /* check for DOS ending "\r\n" */ if (*endptr == '\r') { /* 'move' un*x string tail "\n\0" one char forward */ *endptr = '\n'; *(endptr+1) = '\0'; } } return s; } /* * read a line increasing buffer as necessary */ static int read_long_line(FILE * fp, char **linebuf, int *buflen) { char *cursor; int available; int nbread, retval; cursor = *linebuf; available = *buflen; retval = RETURN_FAILURE; do { /* do we have enough space to store the characters ? */ if (available < 2) { if (expand_line_buffer(linebuf, buflen, &cursor) != RETURN_SUCCESS) { return RETURN_FAILURE; } } available = (int)(*linebuf-cursor) + *buflen; /* read as much as possible */ if (grace_fgets(cursor, available, fp) == NULL) { return retval; } nbread = strlen(cursor); if (nbread < 1) { return retval; } else { retval = RETURN_SUCCESS; } /* prepare next read */ cursor += nbread; available -= nbread; } while (*(cursor - 1) != '\n'); return retval; } /* open a file for write */ FILE *grace_openw(char *fn) { struct stat statb; char buf[GR_MAXPATHLEN + 50]; FILE *retval; if (!fn || !fn[0]) { errmsg("No file name given"); return NULL; } else if (strcmp(fn, "-") == 0 || strcmp(fn, "stdout") == 0) { return stdout; } else { if (stat(fn, &statb) == 0) { /* check to make sure this is a file and not a dir */ if (S_ISREG(statb.st_mode)) { sprintf(buf, "Overwrite %s?", fn); if (!yesno(buf, NULL, NULL, NULL)) { return NULL; } } else { sprintf(buf, "%s is not a regular file!", fn); errmsg(buf); return NULL; } } retval = filter_write(fn); if (!retval) { sprintf(buf, "Can't write to file %s, check permissions!", fn); errmsg(buf); } return retval; } } char *grace_path(char *fn) { static char buf[GR_MAXPATHLEN]; struct stat statb; if (fn == NULL) { return NULL; } else { strcpy(buf, fn); switch (fn[0]) { case '/': case '\0': return buf; break; case '~': expand_tilde(buf); return buf; break; case '.': switch (fn[1]) { case '/': return buf; break; case '.': if (fn[2] == '/') { return buf; } break; } } /* if we arrived here, the path is relative */ if (stat(buf, &statb) == 0) { /* ok, we found it */ return buf; } /* second try: in .grace/ in the current dir */ strcpy(buf, ".grace/"); strcat(buf, fn); if (stat(buf, &statb) == 0) { return buf; } /* third try: in .grace/ in the $HOME dir */ strcpy(buf, get_userhome()); strcat(buf, ".grace/"); strcat(buf, fn); if (stat(buf, &statb) == 0) { return buf; } /* the last attempt: in $GRACE_HOME */ strcpy(buf, get_grace_home()); strcat(buf, "/"); strcat(buf, fn); if (stat(buf, &statb) == 0) { return buf; } /* giving up... */ strcpy(buf, fn); return buf; } } char *grace_exe_path(char *fn) { static char buf[GR_MAXPATHLEN]; char *cp; if (fn == NULL) { return NULL; } else { cp = strchr(fn, ' '); if (cp == NULL) { return exe_path_translate(grace_path(fn)); } else { strcpy(buf, fn); buf[cp - fn] = '\0'; strcpy(buf, grace_path(buf)); strcat(buf, " "); strcat(buf, cp); return exe_path_translate(buf); } } } /* open a file for read */ FILE *grace_openr(char *fn, int src) { struct stat statb; char *tfn; char buf[GR_MAXPATHLEN + 50]; if (!fn || !fn[0]) { errmsg("No file name given"); return NULL; } switch (src) { case SOURCE_DISK: tfn = grace_path(fn); if (strcmp(tfn, "-") == 0 || strcmp(tfn, "stdin") == 0) { return stdin; } else if (stat(tfn, &statb)) { sprintf(buf, "Can't stat file %s", tfn); errmsg(buf); return NULL; /* check to make sure this is a file and not a dir */ } else if (!S_ISREG(statb.st_mode)) { sprintf(buf, "%s is not a regular file", tfn); errmsg(buf); return NULL; } else { return filter_read(tfn); } break; case SOURCE_PIPE: tfn = grace_exe_path(fn); return popen(tfn, "r"); break; default: errmsg("Wrong call to grace_openr()"); return NULL; } } /* * close either a pipe or a file pointer * */ void grace_close(FILE *fp) { if (fp == stdin || fp == stderr || fp == stdout) { return; } if (pclose(fp) == -1) { fclose(fp); } } int getparms(char *plfile) { int linecount = 0, errcnt = 0; char *linebuf=NULL; int linelen=0; FILE *pp; if ((pp = grace_openr(plfile, SOURCE_DISK)) == NULL) { return 0; } else { errcnt = 0; while (read_long_line(pp, &linebuf, &linelen) == RETURN_SUCCESS) { linecount++; if (scanner(linebuf)) { sprintf(linebuf, "Error at line %d", linecount); errmsg(linebuf); errcnt++; if (errcnt > MAXERR) { if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) { grace_close(pp); xfree(linebuf); return 0; } else { errcnt = 0; } } } } if (pp != stdin) { grace_close(pp); } } xfree(linebuf); return 1; } static int uniread(FILE *fp, int load_type, char *label) { int nrows, ncols, nncols, nscols, nncols_req; int *formats = NULL; int breakon, readerror; ss_data ssd; char *s, tbuf[128]; char *linebuf=NULL; int linelen=0; /* a misleading name ... */ int linecount; linecount = 0; readerror = 0; nrows = 0; breakon = TRUE; memset(&ssd, 0, sizeof(ssd)); while (read_long_line(fp, &linebuf, &linelen) == RETURN_SUCCESS) { linecount++; s = linebuf; while (*s == ' ' || *s == '\t' || *s == '\n') { s++; } /* skip comments */ if (*s == '#') { continue; } /* command end-of-set EOL */ if (*s == '@' || *s == '&' || *s == '\0') { /* a data break line */ if (breakon != TRUE) { /* free excessive storage */ realloc_ss_data(&ssd, nrows); /* store accumulated data in set(s) */ if (store_data(&ssd, load_type, label) != RETURN_SUCCESS) { xfree(linebuf); return RETURN_FAILURE; } /* reset state registers */ nrows = 0; readerror = 0; breakon = TRUE; } if (*s == '@') { scanner(s + 1); continue; } } else { if (breakon) { /* parse the data line */ XCFREE(formats); if (parse_ss_row(s, &nncols, &nscols, &formats) != RETURN_SUCCESS) { errmsg("Can't parse data"); xfree(linebuf); return RETURN_FAILURE; } if (load_type == LOAD_SINGLE) { nncols_req = settype_cols(curtype); if (nncols_req <= nncols) { nncols = nncols_req; } else if (nncols_req == nncols + 1) { /* X from index, OK */ ; } else { errmsg("Column count incorrect"); xfree(linebuf); return RETURN_FAILURE; } } ncols = nncols + nscols; /* init the data storage */ if (init_ss_data(&ssd, ncols, formats) != RETURN_SUCCESS) { errmsg("Malloc failed in uniread()"); xfree(linebuf); return RETURN_FAILURE; } breakon = FALSE; } if (nrows % BUFSIZE == 0) { if (realloc_ss_data(&ssd, nrows + BUFSIZE) != RETURN_SUCCESS) { errmsg("Malloc failed in uniread()"); free_ss_data(&ssd); xfree(linebuf); return RETURN_FAILURE; } } if (insert_data_row(&ssd, nrows, s) != RETURN_SUCCESS) { sprintf(tbuf, "Error parsing line %d, skipped", linecount); errmsg(tbuf); readerror++; if (readerror > MAXERR) { if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) { free_ss_data(&ssd); xfree(linebuf); return RETURN_FAILURE; } else { readerror = 0; } } } else { nrows++; } } } if (nrows > 0) { /* free excessive storage */ realloc_ss_data(&ssd, nrows); /* store accumulated data in set(s) */ if (store_data(&ssd, load_type, label) != RETURN_SUCCESS) { xfree(linebuf); return RETURN_FAILURE; } } xfree(linebuf); xfree(formats); return RETURN_SUCCESS; } int getdata(int gno, char *fn, int src, int load_type) { FILE *fp; int retval; int save_version, cur_version; fp = grace_openr(fn, src); if (fp == NULL) { return RETURN_FAILURE; } save_version = get_project_version(); set_project_version(0); set_parser_gno(gno); retval = uniread(fp, load_type, fn); grace_close(fp); cur_version = get_project_version(); if (cur_version != 0) { /* a complete project */ postprocess_project(cur_version); } else if (load_type != LOAD_BLOCK) { /* just a few sets */ autoscale_graph(gno, autoscale_onread); } set_project_version(save_version); return retval; } /* * read data to the set from a file overriding the current contents */ int update_set_from_file(int gno, int setno, char *fn, int src) { int retval; if (set_parser_setno(gno, setno) != RETURN_SUCCESS) { retval = RETURN_FAILURE; } else { FILE *fp; fp = grace_openr(fn, src); killsetdata(gno, setno); curtype = dataset_type(gno, setno); retval = uniread(fp, LOAD_SINGLE, fn); grace_close(fp); } return retval; } void outputset(int gno, int setno, char *fname, char *dformat) { FILE *cp; if ((cp = grace_openw(fname)) == NULL) { return; } else { write_set(gno, setno, cp, dformat, TRUE); grace_close(cp); } } int load_project_file(char *fn, int as_template) { int gno; int retval; if (wipeout()) { return RETURN_FAILURE; } else { if (getdata(0, fn, SOURCE_DISK, LOAD_SINGLE) == RETURN_SUCCESS) { if (as_template == FALSE) { set_docname(fn); } clear_dirtystate(); retval = RETURN_SUCCESS; } else { retval = RETURN_FAILURE; } /* try to switch to the first active graph */ for (gno = 0; gno < number_of_graphs(); gno++) { if (is_graph_hidden(gno) == FALSE) { select_graph(gno); break; } } #ifndef NONE_GUI update_all(); #endif return retval; } } int load_project(char *fn) { return load_project_file(fn, FALSE); } int new_project(char *template) { int retval; char *s; if (template == NULL || template[0] == '\0') { retval = load_project_file("templates/Default.agr", TRUE); } else if (template[0] == '/') { retval = load_project_file(template, TRUE); } else { s = xmalloc(strlen("templates/") + strlen(template) + 1); if (s == NULL) { retval = RETURN_FAILURE; } else { sprintf(s, "templates/%s", template); retval = load_project_file(s, TRUE); xfree(s); } } return retval; } int save_project(char *fn) { FILE *cp; int gno, setno; char *old_fn; int noask_save = noask; old_fn = get_docname(); if (compare_strings(old_fn, fn)) { /* If saving under the same name, don't warn about overwriting */ noask = TRUE; } if ((cp = grace_openw(fn)) == NULL) { noask = noask_save; return RETURN_FAILURE; } putparms(-1, cp, TRUE); for (gno = 0; gno < number_of_graphs(); gno++) { for (setno = 0; setno < number_of_sets(gno); setno++) { write_set(gno, setno, cp, sformat, FALSE); } } grace_close(cp); set_docname(fn); clear_dirtystate(); noask = noask_save; return RETURN_SUCCESS; } /* * write out a set */ int write_set(int gno, int setno, FILE *cp, char *format, int rawdata) { int i, n, col, ncols; double *x[MAX_SET_COLS]; char **s; if (cp == NULL) { return RETURN_FAILURE; } if (is_set_active(gno, setno) == TRUE) { n = getsetlength(gno, setno); ncols = dataset_cols(gno, setno); for (col = 0; col < ncols; col++) { x[col] = getcol(gno, setno, col); } s = get_set_strings(gno, setno); if (format == NULL) { format = sformat; } if (!rawdata) { fprintf(cp, "@target G%d.S%d\n", gno, setno); fprintf(cp, "@type %s\n", set_types(dataset_type(gno, setno))); } for (i = 0; i < n; i++) { for (col = 0; col < ncols; col++) { if (col != 0) { fputs(" ", cp); } fprintf(cp, format, x[col][i]); } if (s != NULL) { fprintf(cp, " \"%s\"", PSTRING(s[i])); } fputs("\n", cp); } if (rawdata) { fprintf(cp, "\n"); } else { fprintf(cp, "&\n"); } } return RETURN_SUCCESS; } #ifdef HAVE_NETCDF /* * read a variable from netcdf file into a set in graph gno * xvar and yvar are the names for x, y in the netcdf file resp. * return 0 on fail, return 1 if success. * * if xvar == NULL, then load the index of the point to x * */ int readnetcdf(int gno, int setno, char *netcdfname, char *xvar, char *yvar, int nstart, int nstop, int nstride) { int cdfid; /* netCDF id */ int i, n; double *x, *y; float *xf, *yf; short *xs, *ys; long *xl, *yl; char buf[256]; /* variable ids */ int x_id = -1, y_id; /* variable shapes */ long start[2]; long count[2]; nc_type xdatatype = 0; nc_type ydatatype = 0; int xndims, xdim[10], xnatts; int yndims, ydim[10], ynatts; long nx, ny; ncopts = 0; /* no crash on error */ /* * get a set if on entry setno == -1, if setno=-1, then fail */ if (setno == -1) { if ((setno = nextset(gno)) == -1) { return 0; } } else { if (is_set_active(gno, setno)) { killset(gno, setno); } } /* * open the netcdf file and locate the variable to read */ if ((cdfid = ncopen(netcdfname, NC_NOWRITE)) == -1) { errmsg("Can't open file."); return 0; } if (xvar != NULL) { if ((x_id = ncvarid(cdfid, xvar)) == -1) { char ebuf[256]; sprintf(ebuf, "readnetcdf(): No such variable %s for X", xvar); errmsg(ebuf); return 0; } ncvarinq(cdfid, x_id, NULL, &xdatatype, &xndims, xdim, &xnatts); ncdiminq(cdfid, xdim[0], NULL, &nx); if (xndims != 1) { errmsg("Number of dimensions for X must be 1."); return 0; } } if ((y_id = ncvarid(cdfid, yvar)) == -1) { char ebuf[256]; sprintf(ebuf, "readnetcdf(): No such variable %s for Y", yvar); errmsg(ebuf); return 0; } ncvarinq(cdfid, y_id, NULL, &ydatatype, &yndims, ydim, &ynatts); ncdiminq(cdfid, ydim[0], NULL, &ny); if (yndims != 1) { errmsg("Number of dimensions for Y must be 1."); return 0; } if (xvar != NULL) { n = nx < ny ? nx : ny; } else { n = ny; } if (n <= 0) { errmsg("Length of dimension == 0."); return 0; } /* * allocate for this set */ x = xcalloc(n, SIZEOF_DOUBLE); y = xcalloc(n, SIZEOF_DOUBLE); if (x == NULL || y == NULL) { XCFREE(x); XCFREE(y); ncclose(cdfid); return 0; } start[0] = 0; count[0] = n; /* This will retrieve whole file, modify * these values to get subset. This will only * work for single-dimension vars. You need * to add dims to start & count for * multi-dimensional. */ /* * read the variables from the netcdf file */ if (xvar != NULL) { /* TODO should check for other data types here */ /* TODO should check for NULL on the xcallocs() */ /* TODO making assumptions about the sizes of shorts and longs */ switch (xdatatype) { case NC_SHORT: xs = xcalloc(n, SIZEOF_SHORT); ncvarget(cdfid, x_id, start, count, (void *) xs); for (i = 0; i < n; i++) { x[i] = xs[i]; } xfree(xs); break; case NC_LONG: xl = xcalloc(n, SIZEOF_LONG); ncvarget(cdfid, x_id, start, count, (void *) xl); for (i = 0; i < n; i++) { x[i] = xl[i]; } xfree(xl); break; case NC_FLOAT: xf = xcalloc(n, SIZEOF_FLOAT); ncvarget(cdfid, x_id, start, count, (void *) xf); for (i = 0; i < n; i++) { x[i] = xf[i]; } xfree(xf); break; case NC_DOUBLE: ncvarget(cdfid, x_id, start, count, (void *) x); break; default: errmsg("Data type not supported"); XCFREE(x); XCFREE(y); ncclose(cdfid); return 0; break; } } else { /* just load index */ for (i = 0; i < n; i++) { x[i] = i + 1; } } switch (ydatatype) { case NC_SHORT: ys = xcalloc(n, SIZEOF_SHORT); ncvarget(cdfid, y_id, start, count, (void *) ys); for (i = 0; i < n; i++) { y[i] = ys[i]; } break; case NC_LONG: yl = xcalloc(n, SIZEOF_LONG); ncvarget(cdfid, y_id, start, count, (void *) yl); for (i = 0; i < n; i++) { y[i] = yl[i]; } break; case NC_FLOAT: /* TODO should check for NULL here */ yf = xcalloc(n, SIZEOF_FLOAT); ncvarget(cdfid, y_id, start, count, (void *) yf); for (i = 0; i < n; i++) { y[i] = yf[i]; } xfree(yf); break; case NC_DOUBLE: ncvarget(cdfid, y_id, start, count, (void *) y); break; default: errmsg("Data type not supported"); XCFREE(x); XCFREE(y); ncclose(cdfid); return 0; break; } ncclose(cdfid); /* * initialize stuff for the newly created set */ activateset(gno, setno); set_dataset_type(gno, setno, SET_XY); setcol(gno, setno, 0, x, n); setcol(gno, setno, 1, y, n); sprintf(buf, "File %s x = %s y = %s", netcdfname, xvar == NULL ? "Index" : xvar, yvar); setcomment(gno, setno, buf); autoscale_graph(gno, autoscale_onread); return 1; } int write_netcdf(char *fname) { char buf[512]; int ncid; /* netCDF id */ int i, j; /* dimension ids */ int n_dim; /* variable ids */ int x_id, y_id; int dims[1]; long len[1]; long it = 0; double *x, *y, x1, x2, y1, y2; ncid = nccreate(fname, NC_CLOBBER); ncattput(ncid, NC_GLOBAL, "Contents", NC_CHAR, 11, (void *) "grace sets"); for (i = 0; i < number_of_graphs(); i++) { if (is_graph_active(i)) { for (j = 0; j < number_of_sets(i); j++) { if (is_set_active(i, j)) { char s[64]; sprintf(buf, "g%d_s%d_comment", i, j); ncattput(ncid, NC_GLOBAL, buf, NC_CHAR, strlen(getcomment(i, j)), (void *) getcomment(i, j)); sprintf(buf, "g%d_s%d_type", i, j); strcpy(s, set_types(dataset_type(i, j))); ncattput(ncid, NC_GLOBAL, buf, NC_CHAR, strlen(s), (void *) s); sprintf(buf, "g%d_s%d_n", i, j); n_dim = ncdimdef(ncid, buf, getsetlength(i, j)); dims[0] = n_dim; getsetminmax(i, j, &x1, &x2, &y1, &y2); sprintf(buf, "g%d_s%d_x", i, j); x_id = ncvardef(ncid, buf, NC_DOUBLE, 1, dims); ncattput(ncid, x_id, "min", NC_DOUBLE, 1, (void *) &x1); ncattput(ncid, x_id, "max", NC_DOUBLE, 1, (void *) &x2); dims[0] = n_dim; sprintf(buf, "g%d_s%d_y", i, j); y_id = ncvardef(ncid, buf, NC_DOUBLE, 1, dims); ncattput(ncid, y_id, "min", NC_DOUBLE, 1, (void *) &y1); ncattput(ncid, y_id, "max", NC_DOUBLE, 1, (void *) &y2); } } } } ncendef(ncid); ncclose(ncid); if ((ncid = ncopen(fname, NC_WRITE)) == -1) { errmsg("Can't open file."); return 1; } for (i = 0; i < number_of_graphs(); i++) { if (is_graph_active(i)) { for (j = 0; j < number_of_sets(i); j++) { if (is_set_active(i, j)) { len[0] = getsetlength(i, j); x = getx(i, j); y = gety(i, j); sprintf(buf, "g%d_s%d_x", i, j); x_id = ncvarid(ncid, buf); sprintf(buf, "g%d_s%d_y", i, j); y_id = ncvarid(ncid, buf); ncvarput(ncid, x_id, &it, len, (void *) x); ncvarput(ncid, y_id, &it, len, (void *) y); } } } } ncclose(ncid); return 0; } #endif /* HAVE_NETCDF */