2 * Grace - GRaphing, Advanced Computation and Exploration of data
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2003 Grace Development Team
9 * Maintained by Evgeny Stambulchik
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
41 #include <sys/types.h>
45 #ifdef HAVE_SYS_SELECT_H
46 # include <sys/select.h>
61 #include "core_utils.h"
69 * number of rows to allocate for each call to realloc
74 * number of bytes in each line chunk
75 * (should be related to system pipe size, typically 4K)
78 # define PIPE_BUF 4096
80 #define CHUNKSIZE 2*PIPE_BUF
82 char *close_input
; /* name of real-time input to close */
84 struct timeval read_begin
= {0l, 0l}; /* used to check too long inputs */
86 static Input_buffer dummy_ib
= {-1, 0, 0, 0, 0, 0, NULL
, 0, 0, NULL
, 0l};
88 int nb_rt
= 0; /* number of real time file descriptors */
89 Input_buffer
*ib_tbl
= 0; /* table for each open input */
90 int ib_tblsize
= 0; /* number of elements in ib_tbl */
92 static int time_spent(void);
93 static int expand_ib_tbl(int delay
);
94 static int expand_line_buffer(char **adrBuf
, int *ptrSize
, char **adrPtr
);
95 static int reopen_real_time_input(Grace
*gr
, Input_buffer
*ib
);
96 static int read_real_time_lines(Input_buffer
*ib
);
97 static int process_complete_lines(Input_buffer
*ib
);
99 static int read_long_line(FILE *fp
, char **linebuf
, int *buflen
);
101 static int uniread(Quark
*pr
, FILE *fp
, int load_type
, char *label
);
104 * part of the time sliced already spent in milliseconds
106 static int time_spent(void)
110 gettimeofday(&now
, NULL
);
112 return 1000 * (now
.tv_sec
- read_begin
.tv_sec
)
113 + (now
.tv_usec
- read_begin
.tv_usec
) / 1000;
119 * expand the table of monitored real time inputs
121 static int expand_ib_tbl(int delay
)
124 Input_buffer
*new_tbl
;
126 new_size
= (ib_tblsize
> 0) ? 2*ib_tblsize
: 5;
127 new_tbl
= xcalloc(new_size
, sizeof(Input_buffer
));
128 if (new_tbl
== NULL
) {
129 return RETURN_FAILURE
;
132 for (i
= 0; i
< new_size
; i
++) {
133 new_tbl
[i
] = (i
< ib_tblsize
) ? ib_tbl
[i
] : dummy_ib
;
134 new_tbl
[i
].delay
= delay
;
137 if (ib_tblsize
> 0) {
138 xfree((void *) ib_tbl
);
141 ib_tblsize
= new_size
;
143 return RETURN_SUCCESS
;
148 * expand a line buffer
150 static int expand_line_buffer(char **adrBuf
, int *ptrSize
, char **adrPtr
)
155 newsize
= *ptrSize
+ CHUNKSIZE
;
156 newbuf
= xmalloc(newsize
);
158 return RETURN_FAILURE
;
162 /* this is the first time through */
167 /* we are expanding an existing line */
168 strncpy(newbuf
, *adrBuf
, *ptrSize
);
170 *adrPtr
+= newbuf
- *adrBuf
;
178 return RETURN_SUCCESS
;
183 * reopen an Input_buffer (surely a fifo)
185 static int reopen_real_time_input(Grace
*grace
, Input_buffer
*ib
)
190 /* in order to avoid race conditions (broken pipe on the write
191 side), we open a new file descriptor before closing the
193 fd
= open(ib
->name
, O_RDONLY
| O_NONBLOCK
);
195 sprintf(buf
, "Can't reopen real time input %s", ib
->name
);
197 unregister_real_time_input(ib
->name
);
198 return RETURN_FAILURE
;
205 /* swapping the file descriptors */
213 return RETURN_SUCCESS
;
219 * unregister a file descriptor no longer monitored
220 * (since Input_buffer structures dedicated to static inputs
221 * are not kept in the table, it is not an error to unregister
222 * an input not already registered)
224 void unregister_real_time_input(const char *name
)
232 for (ib
= ib_tbl
; ib
< ib_tbl
+ ib_tblsize
; ib
++) {
233 l2
= (ib
->name
== 0) ? -1 : strlen(ib
->name
);
234 if (l1
== l2
&& strcmp (name
, ib
->name
) == 0) {
235 /* name is usually the same pointer as ib->name so we cannot */
236 /* free the string and output the message using name afterwards */
245 /* this descriptor is still in use */
252 * register a file descriptor for monitoring
254 int register_real_time_input(Grace
*grace
, int fd
, const char *name
, int reopen
)
259 /* some safety checks */
261 sprintf(buf
, "%s : internal error, wrong file descriptor", name
);
263 return RETURN_FAILURE
;
267 if (fcntl(fd
, F_GETFL
) & O_WRONLY
) {
269 "Descriptor %d not open for reading\n",
271 return RETURN_FAILURE
;
275 /* remove previous entry for the same set if any */
276 unregister_real_time_input(name
);
278 /* find an empty slot in the table */
279 for (ib
= ib_tbl
; ib
< ib_tbl
+ ib_tblsize
; ib
++) {
281 sprintf(buf
, "%s : internal error, file descriptor already in use",
284 return RETURN_FAILURE
;
285 } else if (ib
->fd
< 0) {
290 if (ib
== ib_tbl
+ ib_tblsize
) {
291 /* the table was full, we expand it */
292 int old_size
= ib_tblsize
;
293 if (expand_ib_tbl(grace
->rt
->timer_delay
) != RETURN_SUCCESS
) {
294 return RETURN_FAILURE
;
296 ib
= ib_tbl
+ old_size
;
299 /* we keep the current buffer (even if 0),
300 and only say everything is available */
306 ib
->name
= copy_string(ib
->name
, name
);
314 return RETURN_SUCCESS
;
318 * read a real-time line (but do not process it)
320 static int read_real_time_lines(Input_buffer
*ib
)
323 int available
, nbread
;
326 cursor
= ib
->buf
+ ib
->used
;
327 available
= ib
->size
- ib
->used
;
329 /* have we enough space to store the characters ? */
331 if (expand_line_buffer(&(ib
->buf
), &(ib
->size
), &cursor
)
333 return RETURN_FAILURE
;
335 available
= ib
->buf
+ ib
->size
- cursor
;
338 /* read as much as possible */
339 nbread
= read(ib
->fd
, (void *) cursor
, available
- 1);
342 sprintf(buf
, "%s : read error on real time input",
345 return RETURN_FAILURE
;
352 ib
->buf
[ib
->used
] = '\0';
356 return RETURN_SUCCESS
;
361 * process complete lines that have already been read
363 static int process_complete_lines(Input_buffer
*ib
)
366 char *begin_of_line
, *end_of_line
;
370 return RETURN_SUCCESS
;
375 /* loop over the embedded lines */
376 begin_of_line
= (end_of_line
== NULL
) ? ib
->buf
: (end_of_line
+ 1);
377 end_of_line
= begin_of_line
;
379 while (end_of_line
!= NULL
&& *end_of_line
!= '\n') {
380 /* trying to find a complete line */
381 if (end_of_line
== ib
->buf
+ ib
->used
) {
384 if (*end_of_line
== '\0') {
391 if (end_of_line
!= NULL
) {
392 /* we have a whole line */
398 if (line_corrupted
|| scanner(begin_of_line
)) {
399 sprintf(buf
, "Error at line %d", ib
->lineno
);
402 if (ib
->errors
> MAXERR
) {
405 /* this prevents from being called recursively by
406 the inner X loop of yesno */
409 if (yesno("Lots of errors, abort?", NULL
, NULL
, NULL
)) {
410 close_input
= copy_string(close_input
, "");
420 if (close_input
!= NULL
) {
421 /* something should be closed */
422 if (close_input
[0] == '\0') {
423 unregister_real_time_input(ib
->name
);
425 unregister_real_time_input(close_input
);
432 /* we have closed ourselves */
433 return RETURN_SUCCESS
;
440 } while (end_of_line
!= NULL
);
442 if (end_of_line
!= NULL
) {
443 /* the line has just been processed */
444 begin_of_line
= end_of_line
+ 1;
447 if (begin_of_line
> ib
->buf
) {
448 /* move the remaining data to the beginning */
449 ib
->used
-= begin_of_line
- ib
->buf
;
450 memmove(ib
->buf
, begin_of_line
, ib
->used
);
451 ib
->buf
[ib
->used
] = '\0';
455 return RETURN_SUCCESS
;
459 int real_time_under_monitoring(void)
465 * monitor the set of registered file descriptors for pending input
467 int monitor_input(Grace
*grace
, Input_buffer
*tbl
, int tblsize
, int no_wait
)
473 struct timeval timeout
;
474 int highest
, first_time
, retsel
;
476 /* we don't want to get stuck here, we memorize the start date
477 and will check we do not exceed our allowed time slice */
478 gettimeofday(&read_begin
, NULL
);
481 while (((time_spent() < tbl
->delay
) || first_time
) && retsel
> 0) {
483 /* register all the monitored descriptors */
486 for (ib
= tbl
; ib
< tbl
+ tblsize
; ib
++) {
488 FD_SET(ib
->fd
, &rfds
);
489 if (ib
->fd
> highest
) {
496 /* there's nothing to do */
497 return RETURN_SUCCESS
;
501 /* just check for available data without waiting */
504 /* wait until data or end of time slice arrive */
505 remaining
= tbl
->delay
- time_spent();
510 timeout
.tv_sec
= remaining
/ 1000;
511 timeout
.tv_usec
= 1000l * (remaining
% 1000);
512 retsel
= select(highest
+ 1, &rfds
, NULL
, NULL
, &timeout
);
515 ((time_spent() < tbl
->delay
) || first_time
) && ib
< tbl
+ tblsize
;
517 if (ib
->fd
>= 0 && FD_ISSET(ib
->fd
, &rfds
)) {
518 /* there is pending input */
519 if (read_real_time_lines(ib
) != RETURN_SUCCESS
520 || process_complete_lines(ib
) != RETURN_SUCCESS
) {
521 return RETURN_FAILURE
;
524 if (ib
->zeros
>= 5) {
525 /* we were told five times something happened, but
526 never got any byte : we assume the pipe (or
527 whatever) has been closed by the peer */
529 /* we should reset the input buffer, in case
530 the peer also reopens it */
531 if (reopen_real_time_input(grace
, ib
) != RETURN_SUCCESS
) {
532 return RETURN_FAILURE
;
535 unregister_real_time_input(ib
->name
);
538 /* we have changed the table, we should end the loop */
544 /* after one pass, we obey timeout */
548 return RETURN_SUCCESS
;
552 * read a line increasing buffer as necessary
554 static int read_long_line(FILE * fp
, char **linebuf
, int *buflen
)
562 retval
= RETURN_FAILURE
;
564 /* do we have enough space to store the characters ? */
566 if (expand_line_buffer(linebuf
, buflen
, &cursor
)
568 return RETURN_FAILURE
;
571 available
= (int)(*linebuf
-cursor
) + *buflen
;
573 /* read as much as possible */
574 if (grace_fgets(cursor
, available
, fp
) == NULL
) {
577 nbread
= strlen(cursor
);
581 retval
= RETURN_SUCCESS
;
584 /* prepare next read */
588 } while (*(cursor
- 1) != '\n');
594 /* open a file for write */
595 FILE *grace_openw(Grace
*grace
, char *fn
)
598 char buf
[GR_MAXPATHLEN
+ 50];
602 errmsg("No file name given");
604 } else if (strcmp(fn
, "-") == 0 || strcmp(fn
, "stdout") == 0) {
607 if (stat(fn
, &statb
) == 0) {
608 /* check to make sure this is a file and not a dir */
609 if (S_ISREG(statb
.st_mode
)) {
610 sprintf(buf
, "Overwrite %s?", fn
);
611 if (!yesno(buf
, NULL
, NULL
, NULL
)) {
615 sprintf(buf
, "%s is not a regular file!", fn
);
620 retval
= filter_write(grace
, fn
);
622 sprintf(buf
, "Can't write to file %s, check permissions!", fn
);
629 char *grace_path(Grace
*grace
, char *fn
)
631 static char buf
[GR_MAXPATHLEN
];
645 expand_tilde(grace
, buf
);
660 /* if we arrived here, the path is relative */
661 if (stat(buf
, &statb
) == 0) {
662 /* ok, we found it */
666 /* second try: in .grace/ in the current dir */
667 strcpy(buf
, ".grace/");
669 if (stat(buf
, &statb
) == 0) {
673 /* third try: in .grace/ in the $HOME dir */
674 strcpy(buf
, get_userhome(grace
));
675 strcat(buf
, ".grace/");
677 if (stat(buf
, &statb
) == 0) {
681 /* the last attempt: in $GRACE_HOME */
682 strcpy(buf
, get_grace_home(grace
));
685 if (stat(buf
, &statb
) == 0) {
695 char *grace_path2(Grace
*grace
, const char *prefix
, char *fn
)
699 s
= copy_string(NULL
, prefix
);
704 if (s
[strlen(s
) - 1] != '/') {
705 s
= concat_strings(s
, "/");
708 s
= concat_strings(s
, fn
);
710 bufp
= grace_path(grace
, s
);
717 char *grace_exe_path(Grace
*grace
, char *fn
)
719 static char buf
[GR_MAXPATHLEN
];
725 cp
= strchr(fn
, ' ');
727 return exe_path_translate(grace_path(grace
, fn
));
731 strcpy(buf
, grace_path(grace
, buf
));
734 return exe_path_translate(buf
);
739 /* open a file for read */
740 FILE *grace_openr(Grace
*grace
, char *fn
, int src
)
744 char buf
[GR_MAXPATHLEN
+ 50];
747 errmsg("No file name given");
752 tfn
= grace_path(grace
, fn
);
753 if (strcmp(tfn
, "-") == 0 || strcmp(tfn
, "stdin") == 0) {
755 } else if (stat(tfn
, &statb
)) {
756 sprintf(buf
, "Can't stat file %s", tfn
);
759 /* check to make sure this is a file and not a dir */
760 } else if (!S_ISREG(statb
.st_mode
)) {
761 sprintf(buf
, "%s is not a regular file", tfn
);
765 return filter_read(grace
, tfn
);
769 tfn
= grace_exe_path(grace
, fn
);
770 return popen(tfn
, "r");
773 errmsg("Wrong call to grace_openr()");
779 * close either a pipe or a file pointer
782 void grace_close(FILE *fp
)
784 if (fp
== stdin
|| fp
== stderr
|| fp
== stdout
) {
787 if (pclose(fp
) == -1) {
792 static int uniread(Quark
*pr
, FILE *fp
, int load_type
, char *label
)
794 int nrows
, ncols
, nncols
, nscols
, nncols_req
;
796 int breakon
, readerror
;
800 int linelen
=0; /* a misleading name ... */
809 memset(&ssd
, 0, sizeof(ssd
));
811 while (read_long_line(fp
, &linebuf
, &linelen
) == RETURN_SUCCESS
) {
814 while (*s
== ' ' || *s
== '\t' || *s
== '\n') {
821 /* command end-of-set EOL */
822 if (*s
== '@' || *s
== '&' || *s
== '\0') {
823 /* a data break line */
824 if (breakon
!= TRUE
) {
825 /* free excessive storage */
826 realloc_ss_data(&ssd
, nrows
);
828 /* store accumulated data in set(s) */
829 if (store_data(pr
, &ssd
, load_type
) != RETURN_SUCCESS
) {
831 return RETURN_FAILURE
;
834 /* reset state registers */
845 /* parse the data line */
847 if (parse_ss_row(pr
, s
, &nncols
, &nscols
, &formats
) != RETURN_SUCCESS
) {
848 errmsg("Can't parse data");
850 return RETURN_FAILURE
;
853 if (load_type
== LOAD_SINGLE
) {
854 RunTime
*rt
= rt_from_quark(pr
);
855 nncols_req
= settype_cols(rt
->curtype
);
856 if (nncols_req
<= nncols
) {
858 } else if (nncols_req
== nncols
+ 1) {
859 /* X from index, OK */
862 errmsg("Column count incorrect");
864 return RETURN_FAILURE
;
868 ncols
= nncols
+ nscols
;
870 /* init the data storage */
871 if (init_ss_data(&ssd
, ncols
, formats
, label
)
873 errmsg("Malloc failed in uniread()");
875 return RETURN_FAILURE
;
880 if (nrows
% BUFSIZE
== 0) {
881 if (realloc_ss_data(&ssd
, nrows
+ BUFSIZE
) != RETURN_SUCCESS
) {
882 errmsg("Malloc failed in uniread()");
885 return RETURN_FAILURE
;
889 if (insert_data_row(pr
, &ssd
, nrows
, s
) != RETURN_SUCCESS
) {
890 sprintf(tbuf
, "Error parsing line %d, skipped", linecount
);
893 if (readerror
> MAXERR
) {
894 if (yesno("Lots of errors, abort?", NULL
, NULL
, NULL
)) {
897 return RETURN_FAILURE
;
909 /* free excessive storage */
910 realloc_ss_data(&ssd
, nrows
);
912 /* store accumulated data in set(s) */
913 if (store_data(pr
, &ssd
, load_type
) != RETURN_SUCCESS
) {
915 return RETURN_FAILURE
;
921 return RETURN_SUCCESS
;
925 int getdata(Quark
*pr
, char *fn
, int src
, int load_type
)
929 int save_version
, cur_version
;
930 Grace
*grace
= grace_from_quark(pr
);
932 fp
= grace_openr(grace
, fn
, src
);
934 return RETURN_FAILURE
;
937 save_version
= project_get_version_id(pr
);
938 project_set_version_id(pr
, 0);
940 parser_state_reset(pr
);
942 retval
= uniread(pr
, fp
, load_type
, fn
);
946 cur_version
= project_get_version_id(pr
);
947 if (cur_version
!= 0) {
948 /* a complete project */
949 project_postprocess(pr
);
950 } else if (load_type
!= LOAD_BLOCK
) {
951 /* just a few sets */
952 autoscale_graph(graph_get_current(pr
), grace
->rt
->autoscale_onread
);
954 project_set_version_id(pr
, save_version
);
961 * read data to the set from a file overriding the current contents
963 int update_set_from_file(Quark
*pset
)
968 dsp
= set_get_dataset(pset
);
971 retval
= RETURN_FAILURE
;
974 RunTime
*rt
= rt_from_quark(pset
);
976 fp
= grace_openr(grace_from_quark(pset
), dsp
->hotfile
, dsp
->hotsrc
);
979 rt
->curtype
= set_get_type(pset
);
980 retval
= uniread(get_parent_project(pset
), fp
, LOAD_SINGLE
, dsp
->hotfile
);
989 void outputset(Quark
*pset
, char *fname
, char *dformat
)
993 if ((cp
= grace_openw(grace_from_quark(pset
), fname
)) == NULL
) {
996 write_set(pset
, cp
, dformat
);
1004 int write_set(Quark
*pset
, FILE *cp
, char *format
)
1006 int i
, n
, col
, ncols
;
1007 double *x
[MAX_SET_COLS
];
1011 return RETURN_FAILURE
;
1014 if (is_set_dataless(pset
) == FALSE
) {
1015 n
= set_get_length(pset
);
1016 ncols
= set_get_ncols(pset
);
1017 for (col
= 0; col
< ncols
; col
++) {
1018 x
[col
] = set_get_col(pset
, col
);
1020 s
= set_get_strings(pset
);
1022 if (format
== NULL
) {
1023 format
= project_get_sformat(get_parent_project(pset
));
1026 for (i
= 0; i
< n
; i
++) {
1027 for (col
= 0; col
< ncols
; col
++) {
1031 fprintf(cp
, format
, x
[col
][i
]);
1034 fprintf(cp
, " \"%s\"", escapequotes(s
[i
]));
1041 return RETURN_SUCCESS
;
1044 extern int load_xgr_project(Grace
*grace
, char *fn
);
1046 int load_agr_project(Grace
*grace
, char *fn
)
1048 Quark
*project
= project_new(grace
->rt
->qfactory
);
1050 grace
->rt
->print_file
[0] = '\0';
1051 grace
->rt
->curtype
= SET_XY
;
1053 if (getdata(project
, fn
, SOURCE_DISK
, LOAD_SINGLE
) == RETURN_SUCCESS
) {
1054 return grace_set_project(grace
, project
);
1056 parser_state_reset(grace
->project
);
1057 quark_free(project
);
1059 return RETURN_FAILURE
;
1063 int load_project_file(Grace
*grace
, char *fn
, int as_template
)
1070 if (grace
->project
&&
1071 quark_dirtystate_get(grace
->project
) &&
1072 !yesno("Abandon unsaved changes?", NULL
, NULL
, NULL
)) {
1073 return RETURN_FAILURE
;
1076 /* A temporary hack */
1077 if (fn
&& strstr(fn
, ".xgr")) {
1078 retval
= load_xgr_project(grace
, fn
);
1080 retval
= load_agr_project(grace
, fn
);
1083 if (retval
== RETURN_SUCCESS
) {
1087 static char buf
[GR_MAXPATHLEN
];
1090 if (as_template
== FALSE
) {
1091 project_set_docname(grace
->project
, fn
);
1094 /* Set idstr = basename */
1095 strcpy(buf
, mybasename(fn
));
1096 bufp
= strrchr(buf
, '.');
1100 quark_idstr_set(grace
->project
, buf
);
1103 tfn
= grace_path(grace
, fn
);
1104 if (tfn
&& !stat(tfn
, &statb
)) {
1105 mtime
= statb
.st_mtime
;
1107 /* Probably, piped */
1110 project_update_timestamp(grace
->project
, &mtime
);
1112 /* Clear dirtystate */
1113 quark_dirtystate_set(grace
->project
, FALSE
);
1116 /* try to switch to the first active graph */
1117 ngraphs
= project_get_graphs(grace
->project
, &graphs
);
1118 for (i
= 0; i
< ngraphs
; i
++) {
1120 if (select_graph(gr
) == RETURN_SUCCESS
) {
1132 int load_project(Grace
*grace
, char *fn
)
1134 return load_project_file(grace
, fn
, FALSE
);
1137 int new_project(Grace
*grace
, char *template)
1142 if (is_empty_string(template)) {
1143 retval
= load_project_file(grace
, "templates/Default.xgr", TRUE
);
1144 } else if (template[0] == '/') {
1145 retval
= load_project_file(grace
, template, TRUE
);
1147 s
= xmalloc(strlen("templates/") + strlen(template) + 1);
1149 retval
= RETURN_FAILURE
;
1151 sprintf(s
, "templates/%s", template);
1152 retval
= load_project_file(grace
, s
, TRUE
);
1163 * Read scaling attribute such ass add_offset or scale_factor
1164 * with default value
1167 double read_scale_attr(int cdfid
, int varid
, char *attrname
, double default_value
) {
1172 status
= nc_inq_att(cdfid
, varid
, attrname
, &attr_type
, &length
);
1173 if((status
!=NC_NOERR
) || (length
!= 1)) {
1174 return default_value
;
1176 status
= nc_get_att_double (cdfid
, varid
, attrname
, &attribute
);
1177 if(status
!=NC_NOERR
) {
1178 return default_value
;
1185 * read a variable from netcdf file into a set in graph gr
1186 * xvar and yvar are the names for x, y in the netcdf file resp.
1187 * return 0 on fail, return 1 if success.
1189 * if xvar == NULL, then load the index of the point to x
1192 int readnetcdf(Quark
*pset
,
1200 Quark
*gr
= get_parent_graph(pset
);
1201 int cdfid
; /* netCDF id */
1211 int x_id
= -1, y_id
;
1213 /* variable shapes */
1216 /* scaling attributes */
1217 double scale_factor
, add_offset
;
1219 nc_type xdatatype
= 0;
1220 nc_type ydatatype
= 0;
1221 int xndims
, xdim
[10], xnatts
;
1222 int yndims
, ydim
[10], ynatts
;
1225 RunTime
*rt
= rt_from_quark(pset
);
1227 ncopts
= 0; /* no crash on error */
1230 * open the netcdf file and locate the variable to read
1232 if ((cdfid
= ncopen(netcdfname
, NC_NOWRITE
)) == -1) {
1233 errmsg("Can't open file.");
1237 if ((x_id
= ncvarid(cdfid
, xvar
)) == -1) {
1239 sprintf(ebuf
, "readnetcdf(): No such variable %s for X", xvar
);
1243 ncvarinq(cdfid
, x_id
, NULL
, &xdatatype
, &xndims
, xdim
, &xnatts
);
1244 ncdiminq(cdfid
, xdim
[0], NULL
, &nx
);
1246 errmsg("Number of dimensions for X must be 1.");
1250 if ((y_id
= ncvarid(cdfid
, yvar
)) == -1) {
1252 sprintf(ebuf
, "readnetcdf(): No such variable %s for Y", yvar
);
1256 ncvarinq(cdfid
, y_id
, NULL
, &ydatatype
, &yndims
, ydim
, &ynatts
);
1257 ncdiminq(cdfid
, ydim
[0], NULL
, &ny
);
1259 errmsg("Number of dimensions for Y must be 1.");
1263 n
= nx
< ny
? nx
: ny
;
1268 errmsg("Length of dimension == 0.");
1272 * allocate for this set
1274 x
= xcalloc(n
, SIZEOF_DOUBLE
);
1275 y
= xcalloc(n
, SIZEOF_DOUBLE
);
1276 if (x
== NULL
|| y
== NULL
) {
1283 count
[0] = n
; /* This will retrieve whole file, modify
1284 * these values to get subset. This will only
1285 * work for single-dimension vars. You need
1286 * to add dims to start & count for
1287 * multi-dimensional. */
1290 * read the variables from the netcdf file
1293 scale_factor
= read_scale_attr(cdfid
,x_id
, "scale_factor", 1.0);
1294 add_offset
= read_scale_attr(cdfid
,x_id
, "add_offset", 0.0);
1295 /* TODO should check for other data types here */
1296 /* TODO should check for NULL on the xcallocs() */
1297 /* TODO making assumptions about the sizes of shorts and longs */
1298 switch (xdatatype
) {
1301 /* Theoretically I should add macro to autoconf
1302 file to figure out sizeof(byte)
1304 ncvarget(cdfid
, x_id
, start
, count
, (void *) xb
);
1305 for (i
= 0; i
< n
; i
++) {
1306 x
[i
] = scale_factor
*xb
[i
]+add_offset
;
1311 xs
= xcalloc(n
, SIZEOF_SHORT
);
1312 ncvarget(cdfid
, x_id
, start
, count
, (void *) xs
);
1313 for (i
= 0; i
< n
; i
++) {
1314 x
[i
] = x
[i
] = scale_factor
*xs
[i
]+add_offset
;
1319 xl
= xcalloc(n
, SIZEOF_LONG
);
1320 ncvarget(cdfid
, x_id
, start
, count
, (void *) xl
);
1321 for (i
= 0; i
< n
; i
++) {
1322 x
[i
] = scale_factor
*xl
[i
]+add_offset
;
1327 xf
= xcalloc(n
, SIZEOF_FLOAT
);
1328 ncvarget(cdfid
, x_id
, start
, count
, (void *) xf
);
1329 for (i
= 0; i
< n
; i
++) {
1330 x
[i
] = scale_factor
*xf
[i
]+add_offset
;
1335 ncvarget(cdfid
, x_id
, start
, count
, (void *) x
);
1336 for (i
= 0; i
< n
; i
++) {
1337 x
[i
] = scale_factor
*x
[i
]+add_offset
;
1341 errmsg("Data type not supported");
1348 } else { /* just load index */
1349 for (i
= 0; i
< n
; i
++) {
1353 scale_factor
= read_scale_attr(cdfid
,y_id
, "scale_factor", 1.0);
1354 add_offset
= read_scale_attr(cdfid
,y_id
, "add_offset", 0.0);
1355 switch (ydatatype
) {
1358 ncvarget(cdfid
, y_id
, start
, count
, (void *) yb
);
1359 for (i
= 0; i
< n
; i
++) {
1360 y
[i
] = scale_factor
*yb
[i
]+add_offset
;
1366 ys
= xcalloc(n
, SIZEOF_SHORT
);
1367 ncvarget(cdfid
, y_id
, start
, count
, (void *) ys
);
1368 for (i
= 0; i
< n
; i
++) {
1369 y
[i
] = scale_factor
*ys
[i
]+add_offset
;
1373 yl
= xcalloc(n
, SIZEOF_LONG
);
1374 ncvarget(cdfid
, y_id
, start
, count
, (void *) yl
);
1375 for (i
= 0; i
< n
; i
++) {
1376 y
[i
] = scale_factor
*yl
[i
]+add_offset
;
1380 /* TODO should check for NULL here */
1381 yf
= xcalloc(n
, SIZEOF_FLOAT
);
1382 ncvarget(cdfid
, y_id
, start
, count
, (void *) yf
);
1383 for (i
= 0; i
< n
; i
++) {
1384 y
[i
] = scale_factor
*yf
[i
]+add_offset
;
1389 ncvarget(cdfid
, y_id
, start
, count
, (void *) y
);
1390 for (i
= 0; i
< n
; i
++) {
1391 y
[i
] = scale_factor
*y
[i
]+add_offset
;
1395 errmsg("Data type not supported");
1405 * initialize stuff for the newly created set
1407 set_set_type(pset
, SET_XY
);
1408 set_set_col(pset
, 0, x
, n
);
1409 set_set_col(pset
, 1, y
, n
);
1411 sprintf(buf
, "File %s x = %s y = %s", netcdfname
, xvar
== NULL
? "Index" : xvar
, yvar
);
1412 set_set_comment(pset
, buf
);
1414 autoscale_graph(gr
, rt
->autoscale_onread
);
1419 int write_netcdf(Quark
*pr
, char *fname
)
1425 int ncid
; /* netCDF id */
1434 double *x
, *y
, x1
, x2
, y1
, y2
;
1435 RunTime
*rt
= rt_from_quark(pr
);
1436 ncid
= nccreate(fname
, NC_CLOBBER
);
1437 ncattput(ncid
, NC_GLOBAL
, "Contents", NC_CHAR
, 11, (void *) "grace sets");
1438 ngraphs
= project_get_graphs(pr
, &graphs
);
1439 for (i
= 0; i
< ngraphs
; i
++) {
1443 int nsets
= get_descendant_sets(gr
, &psets
);
1444 for (j
= 0; j
< nsets
; j
++) {
1445 Quark
*pset
= psets
[j
];
1446 if (!is_set_dataless(pset
)) {
1449 sprintf(buf
, "comment");
1450 ncattput(ncid
, NC_GLOBAL
, buf
, NC_CHAR
,
1451 strlen(set_get_comment(pset
)), (void *) set_get_comment(pset
));
1453 sprintf(buf
, "type");
1454 strcpy(s
, set_types(rt
, set_get_type(pset
)));
1455 ncattput(ncid
, NC_GLOBAL
, buf
, NC_CHAR
, strlen(s
), (void *) s
);
1458 n_dim
= ncdimdef(ncid
, buf
, set_get_length(pset
));
1460 getsetminmax(&pset
, 1, &x1
, &x2
, &y1
, &y2
);
1462 x_id
= ncvardef(ncid
, buf
, NC_DOUBLE
, 1, dims
);
1463 ncattput(ncid
, x_id
, "min", NC_DOUBLE
, 1, (void *) &x1
);
1464 ncattput(ncid
, x_id
, "max", NC_DOUBLE
, 1, (void *) &x2
);
1467 y_id
= ncvardef(ncid
, buf
, NC_DOUBLE
, 1, dims
);
1468 ncattput(ncid
, y_id
, "min", NC_DOUBLE
, 1, (void *) &y1
);
1469 ncattput(ncid
, y_id
, "max", NC_DOUBLE
, 1, (void *) &y2
);
1477 if ((ncid
= ncopen(fname
, NC_WRITE
)) == -1) {
1478 errmsg("Can't open file.");
1482 for (i
= 0; i
< ngraphs
; i
++) {
1486 int nsets
= get_descendant_sets(gr
, &psets
);
1487 for (j
= 0; j
< nsets
; j
++) {
1488 Quark
*pset
= psets
[j
];
1489 if (!is_set_dataless(pset
)) {
1490 len
[0] = set_get_length(pset
);
1494 x_id
= ncvarid(ncid
, buf
);
1496 y_id
= ncvarid(ncid
, buf
);
1497 ncvarput(ncid
, x_id
, &it
, len
, (void *) x
);
1498 ncvarput(ncid
, y_id
, &it
, len
, (void *) y
);
1510 #endif /* HAVE_NETCDF */