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