1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2002 Grace Development Team
8 *
9 * Maintained by Evgeny Stambulchik
10 *
11 *
12 * All Rights Reserved
13 *
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.
18 *
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.
23 *
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.
27 */
28
29 /*
30 *
31 * read data files
32 *
33 */
34
35 #include <config.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <errno.h>
45 #ifdef HAVE_SYS_SELECT_H
46 # include <sys/select.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 # include <fcntl.h>
50 #endif
51
52 #ifdef HAVE_NETCDF
53 # include <netcdf.h>
54 #endif
55
56 #include "globals.h"
57 #include "utils.h"
58 #include "files.h"
59 #include "ssdata.h"
60 #include "graphs.h"
61 #include "graphutils.h"
62 #include "parser.h"
63
64 #include "protos.h"
65
66 #define MAXERR 5
67
68 /*
69 * number of rows to allocate for each call to realloc
70 */
71 #define BUFSIZE 512
72
73 /*
74 * number of bytes in each line chunk
75 * (should be related to system pipe size, typically 4K)
76 */
77 #ifndef PIPE_BUF
78 # define PIPE_BUF 4096
79 #endif
80 #define CHUNKSIZE 2*PIPE_BUF
81
82 char *close_input; /* name of real-time input to close */
83
84 struct timeval read_begin = {0l, 0l}; /* used to check too long inputs */
85
86 static Input_buffer dummy_ib = {-1, 0, 0, 0, 0, NULL, 0, 0, NULL, 0l};
87
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 */
91
92 static int time_spent(void);
93 static int expand_ib_tbl(void);
94 static int expand_line_buffer(char **adrBuf, int *ptrSize, char **adrPtr);
95 static int reopen_real_time_input(Input_buffer *ib);
96 static int read_real_time_lines(Input_buffer *ib);
97 static int process_complete_lines(Input_buffer *ib);
98
99 static int read_long_line(FILE *fp, char **linebuf, int *buflen);
100
101 static int uniread(FILE *fp, int load_type, char *label);
102
103 /*
104 * part of the time sliced already spent in milliseconds
105 */
time_spent(void)106 static int time_spent(void)
107 {
108 struct timeval now;
109
110 gettimeofday(&now, NULL);
111
112 return 1000 * (now.tv_sec - read_begin.tv_sec)
113 + (now.tv_usec - read_begin.tv_usec) / 1000;
114
115 }
116
117
118 /*
119 * expand the table of monitored real time inputs
120 */
expand_ib_tbl(void)121 static int expand_ib_tbl(void)
122 {
123 int i, new_size;
124 Input_buffer *new_tbl;
125
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;
130 }
131
132 for (i = 0; i < new_size; i++) {
133 new_tbl[i] = (i < ib_tblsize) ? ib_tbl[i] : dummy_ib;
134 }
135
136 if (ib_tblsize > 0) {
137 xfree((void *) ib_tbl);
138 }
139 ib_tbl = new_tbl;
140 ib_tblsize = new_size;
141
142 return RETURN_SUCCESS;
143
144 }
145
146
147 /*
148 * expand a line buffer
149 */
expand_line_buffer(char ** adrBuf,int * ptrSize,char ** adrPtr)150 static int expand_line_buffer(char **adrBuf, int *ptrSize, char **adrPtr)
151 {
152 char *newbuf;
153 int newsize;
154
155 newsize = *ptrSize + CHUNKSIZE;
156 newbuf = xmalloc(newsize);
157 if (newbuf == 0) {
158 return RETURN_FAILURE;
159 }
160
161 if (*ptrSize == 0) {
162 /* this is the first time through */
163 if (adrPtr) {
164 *adrPtr = newbuf;
165 }
166 } else {
167 /* we are expanding an existing line */
168 strncpy(newbuf, *adrBuf, *ptrSize);
169 if (adrPtr) {
170 *adrPtr += newbuf - *adrBuf;
171 }
172 xfree(*adrBuf);
173 }
174
175 *adrBuf = newbuf;
176 *ptrSize = newsize;
177
178 return RETURN_SUCCESS;
179 }
180
181
182 /*
183 * reopen an Input_buffer (surely a fifo)
184 */
reopen_real_time_input(Input_buffer * ib)185 static int reopen_real_time_input(Input_buffer *ib)
186 {
187 int fd;
188 char buf[256];
189
190 /* in order to avoid race conditions (broken pipe on the write
191 side), we open a new file descriptor before closing the
192 existing one */
193 fd = open(ib->name, O_RDONLY | O_NONBLOCK);
194 if (fd < 0) {
195 sprintf(buf, "Can't reopen real time input %s", ib->name);
196 errmsg(buf);
197 unregister_real_time_input(ib->name);
198 return RETURN_FAILURE;
199 }
200
201 #ifndef NONE_GUI
202 xunregister_rti((XtInputId) ib->id);
203 #endif
204
205 /* swapping the file descriptors */
206 close(ib->fd);
207 ib->fd = fd;
208
209 #ifndef NONE_GUI
210 xregister_rti(ib);
211 #endif
212
213 return RETURN_SUCCESS;
214
215 }
216
217
218 /*
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)
223 */
unregister_real_time_input(const char * name)224 void unregister_real_time_input(const char *name)
225 {
226 Input_buffer *ib;
227 int l1, l2;
228
229 l1 = strlen(name);
230
231 nb_rt = 0;
232 for (ib = ib_tbl; ib < ib_tbl + ib_tblsize; ib++) {
233 l2 = (ib->name == NULL) ? -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 */
237 #ifndef NONE_GUI
238 xunregister_rti((XtInputId) ib->id);
239 #endif
240 close(ib->fd);
241 ib->fd = -1;
242 xfree(ib->name);
243 ib->name = NULL;
244 } else
245 if (l2 > 0) {
246 /* this descriptor (if not dummy!) is still in use */
247 nb_rt++;
248 }
249 }
250 }
251
252 /*
253 * register a file descriptor for monitoring
254 */
register_real_time_input(int fd,const char * name,int reopen)255 int register_real_time_input(int fd, const char *name, int reopen)
256 {
257 Input_buffer *ib;
258 char buf[256];
259
260 /* some safety checks */
261 if (fd < 0) {
262 sprintf(buf, "%s : internal error, wrong file descriptor", name);
263 errmsg(buf);
264 return RETURN_FAILURE;
265 }
266
267 #ifdef HAVE_FCNTL
268 if (fcntl(fd, F_GETFL) & O_WRONLY) {
269 fprintf(stderr,
270 "Descriptor %d not open for reading\n",
271 fd);
272 return RETURN_FAILURE;
273 }
274 #endif
275
276 /* remove previous entry for the same set if any */
277 unregister_real_time_input(name);
278
279 /* find an empty slot in the table */
280 for (ib = ib_tbl; ib < ib_tbl + ib_tblsize; ib++) {
281 if (ib->fd == fd) {
282 sprintf(buf, "%s : internal error, file descriptor already in use",
283 name);
284 errmsg(buf);
285 return RETURN_FAILURE;
286 } else if (ib->fd < 0) {
287 break;
288 }
289 }
290
291 if (ib == ib_tbl + ib_tblsize) {
292 /* the table was full, we expand it */
293 int old_size = ib_tblsize;
294 if (expand_ib_tbl() != RETURN_SUCCESS) {
295 return RETURN_FAILURE;
296 }
297 ib = ib_tbl + old_size;
298 }
299
300 /* we keep the current buffer (even if 0),
301 and only say everything is available */
302 ib->fd = fd;
303 ib->errors = 0;
304 ib->lineno = 0;
305 ib->zeros = 0;
306 ib->reopen = reopen;
307 ib->name = copy_string(ib->name, name);
308 ib->used = 0;
309 #ifndef NONE_GUI
310 xregister_rti (ib);
311 #endif
312
313 nb_rt++;
314
315 return RETURN_SUCCESS;
316 }
317
318 /*
319 * read a real-time line (but do not process it)
320 */
read_real_time_lines(Input_buffer * ib)321 static int read_real_time_lines(Input_buffer *ib)
322 {
323 char *cursor;
324 int available, nbread;
325 char buf[256];
326
327 cursor = ib->buf + ib->used;
328 available = ib->size - ib->used;
329
330 /* have we enough space to store the characters ? */
331 if (available < 2) {
332 if (expand_line_buffer(&(ib->buf), &(ib->size), &cursor)
333 != RETURN_SUCCESS) {
334 return RETURN_FAILURE;
335 }
336 available = ib->buf + ib->size - cursor;
337 }
338
339 /* read as much as possible */
340 nbread = read(ib->fd, (void *) cursor, available - 1);
341
342 if (nbread < 0) {
343 sprintf(buf, "%s : read error on real time input",
344 ib->name);
345 errmsg(buf);
346 return RETURN_FAILURE;
347 } else {
348 if (nbread == 0) {
349 ib->zeros++;
350 } else {
351 ib->zeros = 0;
352 ib->used += nbread;
353 ib->buf[ib->used] = '\0';
354 }
355 }
356
357 return RETURN_SUCCESS;
358 }
359
360
361 /*
362 * process complete lines that have already been read
363 */
process_complete_lines(Input_buffer * ib)364 static int process_complete_lines(Input_buffer *ib)
365 {
366 int line_corrupted;
367 char *begin_of_line, *end_of_line;
368 char buf[256];
369
370 if (ib->used <= 0) {
371 return RETURN_SUCCESS;
372 }
373
374 end_of_line = NULL;
375 do {
376 /* loop over the embedded lines */
377 begin_of_line = (end_of_line == NULL) ? ib->buf : (end_of_line + 1);
378 end_of_line = begin_of_line;
379 line_corrupted = 0;
380 while (end_of_line != NULL && *end_of_line != '\n') {
381 /* trying to find a complete line */
382 if (end_of_line == ib->buf + ib->used) {
383 end_of_line = NULL;
384 } else {
385 if (*end_of_line == '\0') {
386 line_corrupted = 1;
387 }
388 ++end_of_line;
389 }
390 }
391
392 if (end_of_line != NULL) {
393 /* we have a whole line */
394
395 ++(ib->lineno);
396 *end_of_line = '\0';
397 close_input = NULL;
398
399 if (line_corrupted || scanner(begin_of_line)) {
400 sprintf(buf, "Error at line %d", ib->lineno);
401 errmsg(buf);
402 ++(ib->errors);
403 if (ib->errors > MAXERR) {
404
405 #ifndef NONE_GUI
406 /* this prevents from being called recursively by
407 the inner X loop of yesno */
408 xunregister_rti((XtInputId) ib->id);
409 #endif
410 if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) {
411 close_input = copy_string(close_input, "");
412 }
413 #ifndef NONE_GUI
414 xregister_rti(ib);
415 #endif
416 ib->errors = 0;
417
418 }
419 }
420
421 if (close_input != NULL) {
422 /* something should be closed */
423 if (close_input[0] == '\0') {
424 unregister_real_time_input(ib->name);
425 } else {
426 unregister_real_time_input(close_input);
427 }
428
429 xfree(close_input);
430 close_input = NULL;
431
432 if (ib->fd < 0) {
433 /* we have closed ourselves */
434 return RETURN_SUCCESS;
435 }
436
437 }
438
439 }
440
441 } while (end_of_line != NULL);
442
443 if (end_of_line != NULL) {
444 /* the line has just been processed */
445 begin_of_line = end_of_line + 1;
446 }
447
448 if (begin_of_line > ib->buf) {
449 /* move the remaining data to the beginning */
450 ib->used -= begin_of_line - ib->buf;
451 memmove(ib->buf, begin_of_line, ib->used);
452 ib->buf[ib->used] = '\0';
453
454 }
455
456 return RETURN_SUCCESS;
457
458 }
459
real_time_under_monitoring(void)460 int real_time_under_monitoring(void)
461 {
462 return nb_rt > 0;
463 }
464
465 /*
466 * monitor the set of registered file descriptors for pending input
467 */
monitor_input(Input_buffer * tbl,int tblsize,int no_wait)468 int monitor_input(Input_buffer *tbl, int tblsize, int no_wait)
469 {
470
471 Input_buffer *ib;
472 fd_set rfds;
473 int remaining;
474 struct timeval timeout;
475 int highest, first_time, retsel;
476
477 /* we don't want to get stuck here, we memorize the start date
478 and will check we do not exceed our allowed time slice */
479 gettimeofday(&read_begin, NULL);
480 first_time = 1;
481 retsel = 1;
482 while (((time_spent() < timer_delay) || first_time) && retsel > 0) {
483
484 /* register all the monitored descriptors */
485 highest = -1;
486 FD_ZERO(&rfds);
487 for (ib = tbl; ib < tbl + tblsize; ib++) {
488 if (ib->fd >= 0) {
489 FD_SET(ib->fd, &rfds);
490 if (ib->fd > highest) {
491 highest = ib->fd;
492 }
493 }
494 }
495
496 if (highest < 0) {
497 /* there's nothing to do */
498 return RETURN_SUCCESS;
499 }
500
501 if (no_wait) {
502 /* just check for available data without waiting */
503 remaining = 0;
504 } else {
505 /* wait until data or end of time slice arrive */
506 remaining = timer_delay - time_spent();
507 if (remaining < 0) {
508 remaining = 0;
509 }
510 }
511 timeout.tv_sec = remaining / 1000;
512 timeout.tv_usec = 1000l * (remaining % 1000);
513 retsel = select(highest + 1, &rfds, NULL, NULL, &timeout);
514
515 for (ib = tbl;
516 ((time_spent() < timer_delay) || first_time) && ib < tbl + tblsize;
517 ib++) {
518 if (ib->fd >= 0 && FD_ISSET(ib->fd, &rfds)) {
519 /* there is pending input */
520 if (read_real_time_lines(ib) != RETURN_SUCCESS
521 || process_complete_lines(ib) != RETURN_SUCCESS) {
522 return RETURN_FAILURE;
523 }
524
525 if (ib->zeros >= 5) {
526 /* we were told five times something happened, but
527 never got any byte : we assume the pipe (or
528 whatever) has been closed by the peer */
529 if (ib->reopen) {
530 /* we should reset the input buffer, in case
531 the peer also reopens it */
532 if (reopen_real_time_input(ib) != RETURN_SUCCESS) {
533 return RETURN_FAILURE;
534 }
535 } else {
536 unregister_real_time_input(ib->name);
537 }
538
539 /* we have changed the table, we should end the loop */
540 break;
541 }
542 }
543 }
544
545 /* after one pass, we obey timeout */
546 first_time = 0;
547 }
548
549 return RETURN_SUCCESS;
550 }
551
552 /* replacement for fgets() to fix up reading DOS text files */
grace_fgets(char * s,int size,FILE * stream)553 char *grace_fgets(char *s, int size, FILE *stream) {
554 int slen;
555 char *endptr;
556
557 s = fgets(s, size, stream);
558 if (!s) {
559 return NULL;
560 }
561
562 slen = strlen(s);
563 if (slen >= 2) {
564 endptr = s + slen - 2;
565 /* check for DOS ending "\r\n" */
566 if (*endptr == '\r') {
567 /* 'move' un*x string tail "\n\0" one char forward */
568 *endptr = '\n';
569 *(endptr+1) = '\0';
570 }
571 }
572 return s;
573 }
574
575 /*
576 * read a line increasing buffer as necessary
577 */
read_long_line(FILE * fp,char ** linebuf,int * buflen)578 static int read_long_line(FILE * fp, char **linebuf, int *buflen)
579 {
580 char *cursor;
581 int available;
582 int nbread, retval;
583
584 cursor = *linebuf;
585 available = *buflen;
586 retval = RETURN_FAILURE;
587 do {
588 /* do we have enough space to store the characters ? */
589 if (available < 2) {
590 if (expand_line_buffer(linebuf, buflen, &cursor)
591 != RETURN_SUCCESS) {
592 return RETURN_FAILURE;
593 }
594 }
595 available = (int)(*linebuf-cursor) + *buflen;
596
597 /* read as much as possible */
598 if (grace_fgets(cursor, available, fp) == NULL) {
599 return retval;
600 }
601 nbread = strlen(cursor);
602 if (nbread < 1) {
603 return retval;
604 } else {
605 retval = RETURN_SUCCESS;
606 }
607
608 /* prepare next read */
609 cursor += nbread;
610 available -= nbread;
611
612 } while (*(cursor - 1) != '\n');
613
614 return retval;
615 }
616
617
618 /* open a file for write */
grace_openw(char * fn)619 FILE *grace_openw(char *fn)
620 {
621 struct stat statb;
622 char buf[GR_MAXPATHLEN + 50];
623 FILE *retval;
624
625 if (!fn || !fn[0]) {
626 errmsg("No file name given");
627 return NULL;
628 } else if (strcmp(fn, "-") == 0 || strcmp(fn, "stdout") == 0) {
629 return stdout;
630 } else {
631 if (stat(fn, &statb) == 0) {
632 /* check to make sure this is a file and not a dir */
633 if (S_ISREG(statb.st_mode)) {
634 sprintf(buf, "Overwrite %s?", fn);
635 if (!yesno(buf, NULL, NULL, NULL)) {
636 return NULL;
637 }
638 } else {
639 sprintf(buf, "%s is not a regular file!", fn);
640 errmsg(buf);
641 return NULL;
642 }
643 }
644 retval = filter_write(fn);
645 if (!retval) {
646 sprintf(buf, "Can't write to file %s, check permissions!", fn);
647 errmsg(buf);
648 }
649 return retval;
650 }
651 }
652
grace_path(char * fn)653 char *grace_path(char *fn)
654 {
655 static char buf[GR_MAXPATHLEN];
656 struct stat statb;
657
658 if (fn == NULL) {
659 return NULL;
660 } else {
661 strcpy(buf, fn);
662
663 switch (fn[0]) {
664 case '/':
665 case '\0':
666 return buf;
667 break;
668 case '~':
669 expand_tilde(buf);
670 return buf;
671 break;
672 case '.':
673 switch (fn[1]) {
674 case '/':
675 return buf;
676 break;
677 case '.':
678 if (fn[2] == '/') {
679 return buf;
680 }
681 break;
682 }
683 }
684 /* if we arrived here, the path is relative */
685 if (stat(buf, &statb) == 0) {
686 /* ok, we found it */
687 return buf;
688 }
689
690 /* second try: in .grace/ in the current dir */
691 strcpy(buf, ".grace/");
692 strcat(buf, fn);
693 if (stat(buf, &statb) == 0) {
694 return buf;
695 }
696
697 /* third try: in .grace/ in the $HOME dir */
698 strcpy(buf, get_userhome());
699 strcat(buf, ".grace/");
700 strcat(buf, fn);
701 if (stat(buf, &statb) == 0) {
702 return buf;
703 }
704
705 /* the last attempt: in $GRACE_HOME */
706 strcpy(buf, get_grace_home());
707 strcat(buf, "/");
708 strcat(buf, fn);
709 if (stat(buf, &statb) == 0) {
710 return buf;
711 }
712
713 /* giving up... */
714 strcpy(buf, fn);
715 return buf;
716 }
717 }
718
grace_exe_path(char * fn)719 char *grace_exe_path(char *fn)
720 {
721 static char buf[GR_MAXPATHLEN];
722 char *cp;
723
724 if (fn == NULL) {
725 return NULL;
726 } else {
727 cp = strchr(fn, ' ');
728 if (cp == NULL) {
729 return exe_path_translate(grace_path(fn));
730 } else {
731 strcpy(buf, fn);
732 buf[cp - fn] = '\0';
733 strcpy(buf, grace_path(buf));
734 strcat(buf, " ");
735 strcat(buf, cp);
736 return exe_path_translate(buf);
737 }
738 }
739 }
740
741 /* open a file for read */
grace_openr(char * fn,int src)742 FILE *grace_openr(char *fn, int src)
743 {
744 struct stat statb;
745 char *tfn;
746 char buf[GR_MAXPATHLEN + 50];
747
748 if (!fn || !fn[0]) {
749 errmsg("No file name given");
750 return NULL;
751 }
752 switch (src) {
753 case SOURCE_DISK:
754 tfn = grace_path(fn);
755 if (strcmp(tfn, "-") == 0 || strcmp(tfn, "stdin") == 0) {
756 return stdin;
757 } else if (stat(tfn, &statb)) {
758 sprintf(buf, "Can't stat file %s", tfn);
759 errmsg(buf);
760 return NULL;
761 /* check to make sure this is a file and not a dir */
762 } else if (!S_ISREG(statb.st_mode)) {
763 sprintf(buf, "%s is not a regular file", tfn);
764 errmsg(buf);
765 return NULL;
766 } else {
767 return filter_read(tfn);
768 }
769 break;
770 case SOURCE_PIPE:
771 tfn = grace_exe_path(fn);
772 return popen(tfn, "r");
773 break;
774 default:
775 errmsg("Wrong call to grace_openr()");
776 return NULL;
777 }
778 }
779
780 /*
781 * close either a pipe or a file pointer
782 *
783 */
grace_close(FILE * fp)784 void grace_close(FILE *fp)
785 {
786 if (fp == stdin || fp == stderr || fp == stdout) {
787 return;
788 }
789 if (pclose(fp) == -1) {
790 fclose(fp);
791 }
792 }
793
getparms(char * plfile)794 int getparms(char *plfile)
795 {
796 int linecount = 0, errcnt = 0;
797 char *linebuf=NULL;
798 int linelen=0;
799 FILE *pp;
800
801 if ((pp = grace_openr(plfile, SOURCE_DISK)) == NULL) {
802 return 0;
803 } else {
804 errcnt = 0;
805 while (read_long_line(pp, &linebuf, &linelen) == RETURN_SUCCESS) {
806 linecount++;
807 if (scanner(linebuf)) {
808 sprintf(linebuf, "Error at line %d", linecount);
809 errmsg(linebuf);
810 errcnt++;
811 if (errcnt > MAXERR) {
812 if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) {
813 grace_close(pp);
814 xfree(linebuf);
815 return 0;
816 } else {
817 errcnt = 0;
818 }
819 }
820 }
821 }
822 if (pp != stdin) {
823 grace_close(pp);
824 }
825 }
826 xfree(linebuf);
827 return 1;
828 }
829
uniread(FILE * fp,int load_type,char * label)830 static int uniread(FILE *fp, int load_type, char *label)
831 {
832 int nrows, ncols, nncols, nscols, nncols_req;
833 int *formats = NULL;
834 int breakon, readerror;
835 ss_data ssd;
836 char *s, tbuf[128];
837 char *linebuf=NULL;
838 int linelen=0; /* a misleading name ... */
839 int linecount;
840
841 linecount = 0;
842 readerror = 0;
843 nrows = 0;
844
845 breakon = TRUE;
846
847 memset(&ssd, 0, sizeof(ssd));
848
849 while (read_long_line(fp, &linebuf, &linelen) == RETURN_SUCCESS) {
850 linecount++;
851 s = linebuf;
852 while (*s == ' ' || *s == '\t' || *s == '\n') {
853 s++;
854 }
855 /* skip comments */
856 if (*s == '#') {
857 continue;
858 }
859 /* command end-of-set EOL */
860 if (*s == '@' || *s == '&' || *s == '\0') {
861 /* a data break line */
862 if (breakon != TRUE) {
863 /* free excessive storage */
864 realloc_ss_data(&ssd, nrows);
865
866 /* store accumulated data in set(s) */
867 if (store_data(&ssd, load_type, label) != RETURN_SUCCESS) {
868 xfree(linebuf);
869 return RETURN_FAILURE;
870 }
871
872 /* reset state registers */
873 nrows = 0;
874 readerror = 0;
875 breakon = TRUE;
876 }
877 if (*s == '@') {
878 scanner(s + 1);
879 continue;
880 }
881 } else {
882 if (breakon) {
883 /* parse the data line */
884 XCFREE(formats);
885 if (parse_ss_row(s, &nncols, &nscols, &formats) != RETURN_SUCCESS) {
886 errmsg("Can't parse data");
887 xfree(linebuf);
888 return RETURN_FAILURE;
889 }
890
891 if (load_type == LOAD_SINGLE) {
892 nncols_req = settype_cols(curtype);
893 if (nncols_req <= nncols) {
894 nncols = nncols_req;
895 } else if (nncols_req == nncols + 1) {
896 /* X from index, OK */
897 ;
898 } else {
899 errmsg("Column count incorrect");
900 xfree(linebuf);
901 return RETURN_FAILURE;
902 }
903 }
904
905 ncols = nncols + nscols;
906
907 /* init the data storage */
908 if (init_ss_data(&ssd, ncols, formats) != RETURN_SUCCESS) {
909 errmsg("Malloc failed in uniread()");
910 xfree(linebuf);
911 return RETURN_FAILURE;
912 }
913
914 breakon = FALSE;
915 }
916 if (nrows % BUFSIZE == 0) {
917 if (realloc_ss_data(&ssd, nrows + BUFSIZE) != RETURN_SUCCESS) {
918 errmsg("Malloc failed in uniread()");
919 free_ss_data(&ssd);
920 xfree(linebuf);
921 return RETURN_FAILURE;
922 }
923 }
924
925 if (insert_data_row(&ssd, nrows, s) != RETURN_SUCCESS) {
926 sprintf(tbuf, "Error parsing line %d, skipped", linecount);
927 errmsg(tbuf);
928 readerror++;
929 if (readerror > MAXERR) {
930 if (yesno("Lots of errors, abort?", NULL, NULL, NULL)) {
931 free_ss_data(&ssd);
932 xfree(linebuf);
933 return RETURN_FAILURE;
934 } else {
935 readerror = 0;
936 }
937 }
938 } else {
939 nrows++;
940 }
941 }
942 }
943
944 if (nrows > 0) {
945 /* free excessive storage */
946 realloc_ss_data(&ssd, nrows);
947
948 /* store accumulated data in set(s) */
949 if (store_data(&ssd, load_type, label) != RETURN_SUCCESS) {
950 xfree(linebuf);
951 return RETURN_FAILURE;
952 }
953 }
954
955 xfree(linebuf);
956 xfree(formats);
957 return RETURN_SUCCESS;
958 }
959
960
getdata(int gno,char * fn,int src,int load_type)961 int getdata(int gno, char *fn, int src, int load_type)
962 {
963 FILE *fp;
964 int retval;
965 int save_version, cur_version;
966
967 fp = grace_openr(fn, src);
968 if (fp == NULL) {
969 return RETURN_FAILURE;
970 }
971
972 save_version = get_project_version();
973 set_project_version(0);
974
975 set_parser_gno(gno);
976
977 retval = uniread(fp, load_type, fn);
978
979 grace_close(fp);
980
981 cur_version = get_project_version();
982 if (cur_version != 0) {
983 /* a complete project */
984 postprocess_project(cur_version);
985 } else if (load_type != LOAD_BLOCK) {
986 /* just a few sets */
987 autoscale_graph(gno, autoscale_onread);
988 }
989 set_project_version(save_version);
990
991 return retval;
992 }
993
994
995 /*
996 * read data to the set from a file overriding the current contents
997 */
update_set_from_file(int gno,int setno,char * fn,int src)998 int update_set_from_file(int gno, int setno, char *fn, int src)
999 {
1000 int retval;
1001
1002 if (set_parser_setno(gno, setno) != RETURN_SUCCESS) {
1003 retval = RETURN_FAILURE;
1004 } else {
1005 FILE *fp;
1006
1007 fp = grace_openr(fn, src);
1008
1009 killsetdata(gno, setno);
1010 curtype = dataset_type(gno, setno);
1011 retval = uniread(fp, LOAD_SINGLE, fn);
1012
1013 grace_close(fp);
1014 }
1015
1016 return retval;
1017 }
1018
1019
outputset(int gno,int setno,char * fname,char * dformat)1020 void outputset(int gno, int setno, char *fname, char *dformat)
1021 {
1022 FILE *cp;
1023
1024 if ((cp = grace_openw(fname)) == NULL) {
1025 return;
1026 } else {
1027 write_set(gno, setno, cp, dformat, TRUE);
1028 grace_close(cp);
1029 }
1030 }
1031
load_project_file(char * fn,int as_template)1032 int load_project_file(char *fn, int as_template)
1033 {
1034 int gno;
1035 int retval;
1036
1037 if (wipeout()) {
1038 return RETURN_FAILURE;
1039 } else {
1040 if (getdata(0, fn, SOURCE_DISK, LOAD_SINGLE) == RETURN_SUCCESS) {
1041 if (as_template == FALSE) {
1042 set_docname(fn);
1043 }
1044 clear_dirtystate();
1045 retval = RETURN_SUCCESS;
1046 } else {
1047 retval = RETURN_FAILURE;
1048 }
1049
1050 /* try to switch to the first active graph */
1051 for (gno = 0; gno < number_of_graphs(); gno++) {
1052 if (is_graph_hidden(gno) == FALSE) {
1053 select_graph(gno);
1054 break;
1055 }
1056 }
1057
1058 #ifndef NONE_GUI
1059 update_all();
1060 #endif
1061 return retval;
1062 }
1063 }
1064
load_project(char * fn)1065 int load_project(char *fn)
1066 {
1067 return load_project_file(fn, FALSE);
1068 }
1069
new_project(char * template)1070 int new_project(char *template)
1071 {
1072 int retval;
1073 char *s;
1074
1075 if (template == NULL || template[0] == '\0') {
1076 retval = load_project_file("templates/Default.agr", TRUE);
1077 } else if (template[0] == '/') {
1078 retval = load_project_file(template, TRUE);
1079 } else {
1080 s = xmalloc(strlen("templates/") + strlen(template) + 1);
1081 if (s == NULL) {
1082 retval = RETURN_FAILURE;
1083 } else {
1084 sprintf(s, "templates/%s", template);
1085 retval = load_project_file(s, TRUE);
1086 xfree(s);
1087 }
1088 }
1089
1090 return retval;
1091 }
1092
save_project(char * fn)1093 int save_project(char *fn)
1094 {
1095 FILE *cp;
1096 int gno, setno;
1097 char *old_fn;
1098 int noask_save = noask;
1099
1100 old_fn = get_docname();
1101 if (compare_strings(old_fn, fn)) {
1102 /* If saving under the same name, don't warn about overwriting */
1103 noask = TRUE;
1104 }
1105
1106 if ((cp = grace_openw(fn)) == NULL) {
1107 noask = noask_save;
1108 return RETURN_FAILURE;
1109 }
1110
1111 putparms(-1, cp, TRUE);
1112
1113 for (gno = 0; gno < number_of_graphs(); gno++) {
1114 for (setno = 0; setno < number_of_sets(gno); setno++) {
1115 write_set(gno, setno, cp, sformat, FALSE);
1116 }
1117 }
1118
1119 grace_close(cp);
1120
1121 set_docname(fn);
1122 clear_dirtystate();
1123
1124 noask = noask_save;
1125 return RETURN_SUCCESS;
1126 }
1127
1128 /*
1129 * write out a set
1130 */
write_set(int gno,int setno,FILE * cp,char * format,int rawdata)1131 int write_set(int gno, int setno, FILE *cp, char *format, int rawdata)
1132 {
1133 int i, n, col, ncols;
1134 double *x[MAX_SET_COLS];
1135 char **s;
1136
1137 if (cp == NULL) {
1138 return RETURN_FAILURE;
1139 }
1140
1141 if (is_set_active(gno, setno) == TRUE) {
1142 n = getsetlength(gno, setno);
1143 ncols = dataset_cols(gno, setno);
1144 for (col = 0; col < ncols; col++) {
1145 x[col] = getcol(gno, setno, col);
1146 }
1147 s = get_set_strings(gno, setno);
1148
1149 if (format == NULL) {
1150 format = sformat;
1151 }
1152
1153 if (!rawdata) {
1154 fprintf(cp, "@target G%d.S%d\n", gno, setno);
1155 fprintf(cp, "@type %s\n", set_types(dataset_type(gno, setno)));
1156 }
1157
1158 for (i = 0; i < n; i++) {
1159 for (col = 0; col < ncols; col++) {
1160 if (col != 0) {
1161 fputs(" ", cp);
1162 }
1163 fprintf(cp, format, x[col][i]);
1164 }
1165 if (s != NULL) {
1166 fprintf(cp, " \"%s\"", PSTRING(s[i]));
1167 }
1168 fputs("\n", cp);
1169 }
1170 if (rawdata) {
1171 fprintf(cp, "\n");
1172 } else {
1173 fprintf(cp, "&\n");
1174 }
1175 }
1176
1177 return RETURN_SUCCESS;
1178 }
1179
1180
1181 #ifdef HAVE_NETCDF
1182
1183 /*
1184 * read a variable from netcdf file into a set in graph gno
1185 * xvar and yvar are the names for x, y in the netcdf file resp.
1186 * return 0 on fail, return 1 if success.
1187 *
1188 * if xvar == NULL, then load the index of the point to x
1189 *
1190 */
readnetcdf(int gno,int setno,char * netcdfname,char * xvar,char * yvar,int nstart,int nstop,int nstride)1191 int readnetcdf(int gno,
1192 int setno,
1193 char *netcdfname,
1194 char *xvar,
1195 char *yvar,
1196 int nstart,
1197 int nstop,
1198 int nstride)
1199 {
1200 int cdfid; /* netCDF id */
1201 int i, n;
1202 double *x, *y;
1203 float *xf, *yf;
1204 short *xs, *ys;
1205 long *xl, *yl;
1206 char buf[256];
1207
1208 /* variable ids */
1209 int x_id = -1, y_id;
1210
1211 /* variable shapes */
1212 long start[2];
1213 long count[2];
1214
1215 nc_type xdatatype = 0;
1216 nc_type ydatatype = 0;
1217 int xndims, xdim[10], xnatts;
1218 int yndims, ydim[10], ynatts;
1219 long nx, ny;
1220
1221 ncopts = 0; /* no crash on error */
1222
1223 /*
1224 * get a set if on entry setno == -1, if setno=-1, then fail
1225 */
1226 if (setno == -1) {
1227 if ((setno = nextset(gno)) == -1) {
1228 return 0;
1229 }
1230 } else {
1231 if (is_set_active(gno, setno)) {
1232 killset(gno, setno);
1233 }
1234 }
1235 /*
1236 * open the netcdf file and locate the variable to read
1237 */
1238 if ((cdfid = ncopen(netcdfname, NC_NOWRITE)) == -1) {
1239 errmsg("Can't open file.");
1240 return 0;
1241 }
1242 if (xvar != NULL) {
1243 if ((x_id = ncvarid(cdfid, xvar)) == -1) {
1244 char ebuf[256];
1245 sprintf(ebuf, "readnetcdf(): No such variable %s for X", xvar);
1246 errmsg(ebuf);
1247 return 0;
1248 }
1249 ncvarinq(cdfid, x_id, NULL, &xdatatype, &xndims, xdim, &xnatts);
1250 ncdiminq(cdfid, xdim[0], NULL, &nx);
1251 if (xndims != 1) {
1252 errmsg("Number of dimensions for X must be 1.");
1253 return 0;
1254 }
1255 }
1256 if ((y_id = ncvarid(cdfid, yvar)) == -1) {
1257 char ebuf[256];
1258 sprintf(ebuf, "readnetcdf(): No such variable %s for Y", yvar);
1259 errmsg(ebuf);
1260 return 0;
1261 }
1262 ncvarinq(cdfid, y_id, NULL, &ydatatype, &yndims, ydim, &ynatts);
1263 ncdiminq(cdfid, ydim[0], NULL, &ny);
1264 if (yndims != 1) {
1265 errmsg("Number of dimensions for Y must be 1.");
1266 return 0;
1267 }
1268 if (xvar != NULL) {
1269 n = nx < ny ? nx : ny;
1270 } else {
1271 n = ny;
1272 }
1273 if (n <= 0) {
1274 errmsg("Length of dimension == 0.");
1275 return 0;
1276 }
1277 /*
1278 * allocate for this set
1279 */
1280 x = xcalloc(n, SIZEOF_DOUBLE);
1281 y = xcalloc(n, SIZEOF_DOUBLE);
1282 if (x == NULL || y == NULL) {
1283 XCFREE(x);
1284 XCFREE(y);
1285 ncclose(cdfid);
1286 return 0;
1287 }
1288 start[0] = 0;
1289 count[0] = n; /* This will retrieve whole file, modify
1290 * these values to get subset. This will only
1291 * work for single-dimension vars. You need
1292 * to add dims to start & count for
1293 * multi-dimensional. */
1294
1295 /*
1296 * read the variables from the netcdf file
1297 */
1298 if (xvar != NULL) {
1299 /* TODO should check for other data types here */
1300 /* TODO should check for NULL on the xcallocs() */
1301 /* TODO making assumptions about the sizes of shorts and longs */
1302 switch (xdatatype) {
1303 case NC_SHORT:
1304 xs = xcalloc(n, SIZEOF_SHORT);
1305 ncvarget(cdfid, x_id, start, count, (void *) xs);
1306 for (i = 0; i < n; i++) {
1307 x[i] = xs[i];
1308 }
1309 xfree(xs);
1310 break;
1311 case NC_LONG:
1312 xl = xcalloc(n, SIZEOF_LONG);
1313 ncvarget(cdfid, x_id, start, count, (void *) xl);
1314 for (i = 0; i < n; i++) {
1315 x[i] = xl[i];
1316 }
1317 xfree(xl);
1318 break;
1319 case NC_FLOAT:
1320 xf = xcalloc(n, SIZEOF_FLOAT);
1321 ncvarget(cdfid, x_id, start, count, (void *) xf);
1322 for (i = 0; i < n; i++) {
1323 x[i] = xf[i];
1324 }
1325 xfree(xf);
1326 break;
1327 case NC_DOUBLE:
1328 ncvarget(cdfid, x_id, start, count, (void *) x);
1329 break;
1330 default:
1331 errmsg("Data type not supported");
1332 XCFREE(x);
1333 XCFREE(y);
1334 ncclose(cdfid);
1335 return 0;
1336 break;
1337 }
1338 } else { /* just load index */
1339 for (i = 0; i < n; i++) {
1340 x[i] = i + 1;
1341 }
1342 }
1343 switch (ydatatype) {
1344 case NC_SHORT:
1345 ys = xcalloc(n, SIZEOF_SHORT);
1346 ncvarget(cdfid, y_id, start, count, (void *) ys);
1347 for (i = 0; i < n; i++) {
1348 y[i] = ys[i];
1349 }
1350 break;
1351 case NC_LONG:
1352 yl = xcalloc(n, SIZEOF_LONG);
1353 ncvarget(cdfid, y_id, start, count, (void *) yl);
1354 for (i = 0; i < n; i++) {
1355 y[i] = yl[i];
1356 }
1357 break;
1358 case NC_FLOAT:
1359 /* TODO should check for NULL here */
1360 yf = xcalloc(n, SIZEOF_FLOAT);
1361 ncvarget(cdfid, y_id, start, count, (void *) yf);
1362 for (i = 0; i < n; i++) {
1363 y[i] = yf[i];
1364 }
1365 xfree(yf);
1366 break;
1367 case NC_DOUBLE:
1368 ncvarget(cdfid, y_id, start, count, (void *) y);
1369 break;
1370 default:
1371 errmsg("Data type not supported");
1372 XCFREE(x);
1373 XCFREE(y);
1374 ncclose(cdfid);
1375 return 0;
1376 break;
1377 }
1378 ncclose(cdfid);
1379
1380 /*
1381 * initialize stuff for the newly created set
1382 */
1383 activateset(gno, setno);
1384 set_dataset_type(gno, setno, SET_XY);
1385 setcol(gno, setno, 0, x, n);
1386 setcol(gno, setno, 1, y, n);
1387
1388 sprintf(buf, "File %s x = %s y = %s", netcdfname, xvar == NULL ? "Index" : xvar, yvar);
1389 setcomment(gno, setno, buf);
1390
1391 autoscale_graph(gno, autoscale_onread);
1392
1393 return 1;
1394 }
1395
write_netcdf(char * fname)1396 int write_netcdf(char *fname)
1397 {
1398 char buf[512];
1399 int ncid; /* netCDF id */
1400 int i, j;
1401 /* dimension ids */
1402 int n_dim;
1403 /* variable ids */
1404 int x_id, y_id;
1405 int dims[1];
1406 long len[1];
1407 long it = 0;
1408 double *x, *y, x1, x2, y1, y2;
1409 ncid = nccreate(fname, NC_CLOBBER);
1410 ncattput(ncid, NC_GLOBAL, "Contents", NC_CHAR, 11, (void *) "grace sets");
1411 for (i = 0; i < number_of_graphs(); i++) {
1412 if (is_graph_active(i)) {
1413 for (j = 0; j < number_of_sets(i); j++) {
1414 if (is_set_active(i, j)) {
1415 char s[64];
1416
1417 sprintf(buf, "g%d_s%d_comment", i, j);
1418 ncattput(ncid, NC_GLOBAL, buf, NC_CHAR,
1419 strlen(getcomment(i, j)), (void *) getcomment(i, j));
1420
1421 sprintf(buf, "g%d_s%d_type", i, j);
1422 strcpy(s, set_types(dataset_type(i, j)));
1423 ncattput(ncid, NC_GLOBAL, buf, NC_CHAR, strlen(s), (void *) s);
1424
1425 sprintf(buf, "g%d_s%d_n", i, j);
1426 n_dim = ncdimdef(ncid, buf, getsetlength(i, j));
1427 dims[0] = n_dim;
1428 getsetminmax(i, j, &x1, &x2, &y1, &y2);
1429 sprintf(buf, "g%d_s%d_x", i, j);
1430 x_id = ncvardef(ncid, buf, NC_DOUBLE, 1, dims);
1431 ncattput(ncid, x_id, "min", NC_DOUBLE, 1, (void *) &x1);
1432 ncattput(ncid, x_id, "max", NC_DOUBLE, 1, (void *) &x2);
1433 dims[0] = n_dim;
1434 sprintf(buf, "g%d_s%d_y", i, j);
1435 y_id = ncvardef(ncid, buf, NC_DOUBLE, 1, dims);
1436 ncattput(ncid, y_id, "min", NC_DOUBLE, 1, (void *) &y1);
1437 ncattput(ncid, y_id, "max", NC_DOUBLE, 1, (void *) &y2);
1438 }
1439 }
1440 }
1441 }
1442 ncendef(ncid);
1443 ncclose(ncid);
1444 if ((ncid = ncopen(fname, NC_WRITE)) == -1) {
1445 errmsg("Can't open file.");
1446 return 1;
1447 }
1448 for (i = 0; i < number_of_graphs(); i++) {
1449 if (is_graph_active(i)) {
1450 for (j = 0; j < number_of_sets(i); j++) {
1451 if (is_set_active(i, j)) {
1452 len[0] = getsetlength(i, j);
1453 x = getx(i, j);
1454 y = gety(i, j);
1455 sprintf(buf, "g%d_s%d_x", i, j);
1456 x_id = ncvarid(ncid, buf);
1457 sprintf(buf, "g%d_s%d_y", i, j);
1458 y_id = ncvarid(ncid, buf);
1459 ncvarput(ncid, x_id, &it, len, (void *) x);
1460 ncvarput(ncid, y_id, &it, len, (void *) y);
1461 }
1462 }
1463 }
1464 }
1465
1466 ncclose(ncid);
1467 return 0;
1468 }
1469
1470 #endif /* HAVE_NETCDF */
1471