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  * spreadsheet data stuff
32  *
33  */
34 
35 #include <config.h>
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "defines.h"
42 #include "globals.h"
43 #include "utils.h"
44 #include "graphs.h"
45 #include "graphutils.h"
46 #include "files.h"
47 #include "ssdata.h"
48 #include "parser.h"
49 
50 #include "protos.h"
51 
copy_data_column(double * src,int nrows)52 double *copy_data_column(double *src, int nrows)
53 {
54     double *dest;
55 
56     dest = xmalloc(nrows*SIZEOF_DOUBLE);
57     if (dest != NULL) {
58         memcpy(dest, src, nrows*SIZEOF_DOUBLE);
59     }
60     return dest;
61 }
62 
copy_string_column(char ** src,int nrows)63 char **copy_string_column(char **src, int nrows)
64 {
65     char **dest;
66     int i;
67 
68     dest = xmalloc(nrows*sizeof(char *));
69     if (dest != NULL) {
70         for (i = 0; i < nrows; i++)
71             dest[i] =copy_string(NULL, src[i]);
72     }
73     return dest;
74 }
75 
76 /* TODO: index_shift */
allocate_index_data(int nrows)77 double *allocate_index_data(int nrows)
78 {
79     int i;
80     double *retval;
81 
82     retval = xmalloc(nrows*SIZEOF_DOUBLE);
83     if (retval != NULL) {
84         for (i = 0; i < nrows; i++) {
85             retval[i] = i;
86         }
87     }
88     return retval;
89 }
90 
allocate_mesh(double start,double stop,int len)91 double *allocate_mesh(double start, double stop, int len)
92 {
93     int i;
94     double *retval;
95 
96     retval = xmalloc(len*SIZEOF_DOUBLE);
97     if (retval != NULL) {
98         double s = (start + stop)/2, d = (stop - start)/2;
99         for (i = 0; i < len; i++) {
100             retval[i] = s + d*((double) (2*i + 1 - len)/(len - 1));
101         }
102     }
103     return retval;
104 }
105 
106 static ss_data blockdata = {0, 0, NULL, NULL};
107 
set_blockdata(ss_data * ssd)108 void set_blockdata(ss_data *ssd)
109 {
110     free_ss_data(&blockdata);
111     if (ssd) {
112         memcpy(&blockdata, ssd, sizeof(ss_data));
113     }
114 }
115 
get_blockncols(void)116 int get_blockncols(void)
117 {
118     return blockdata.ncols;
119 }
120 
get_blocknrows(void)121 int get_blocknrows(void)
122 {
123     return blockdata.nrows;
124 }
125 
get_blockformats(void)126 int *get_blockformats(void)
127 {
128     return blockdata.formats;
129 }
130 
realloc_ss_data(ss_data * ssd,int nrows)131 int realloc_ss_data(ss_data *ssd, int nrows)
132 {
133     int i, j;
134     char  **sp;
135 
136     for (i = 0; i < ssd->ncols; i++) {
137         if (ssd->formats[i] == FFORMAT_STRING) {
138             sp = (char **) ssd->data[i];
139             for (j = nrows; j < ssd->nrows; j++) {
140                 XCFREE(sp[j]);
141             }
142             ssd->data[i] = xrealloc(ssd->data[i], nrows*sizeof(char *));
143             sp = (char **) ssd->data[i];
144             for (j = ssd->nrows; j < nrows; j++) {
145                 sp[j] = NULL;
146             }
147         } else {
148             ssd->data[i] = xrealloc(ssd->data[i], nrows*SIZEOF_DOUBLE);
149         }
150     }
151     ssd->nrows = nrows;
152 
153     return RETURN_SUCCESS;
154 }
155 
free_ss_data(ss_data * ssd)156 void free_ss_data(ss_data *ssd)
157 {
158     if (ssd) {
159         int i, j;
160         char  **sp;
161 
162         for (i = 0; i < ssd->ncols; i++) {
163             if (ssd->formats && ssd->formats[i] == FFORMAT_STRING) {
164                 sp = (char **) ssd->data[i];
165                 for (j = 0; j < ssd->nrows; j++) {
166                     XCFREE(sp[j]);
167                 }
168             }
169             XCFREE(ssd->data[i]);
170         }
171         XCFREE(ssd->data);
172         XCFREE(ssd->formats);
173         ssd->nrows = 0;
174         ssd->ncols = 0;
175     }
176 }
177 
init_ss_data(ss_data * ssd,int ncols,int * formats)178 int init_ss_data(ss_data *ssd, int ncols, int *formats)
179 {
180     int i;
181 
182     ssd->data = xmalloc(ncols*SIZEOF_VOID_P);
183     for (i = 0; i < ncols; i++) {
184         ssd->data[i] = NULL;
185     }
186     ssd->formats = xmalloc(ncols*SIZEOF_INT);
187     memcpy(ssd->formats, formats, ncols*SIZEOF_INT);
188     ssd->ncols = ncols;
189     ssd->nrows = 0;
190 
191     return RETURN_SUCCESS;
192 }
193 
next_token(char * s,char ** token,int * quoted)194 static char *next_token(char *s, char **token, int *quoted)
195 {
196     *quoted = FALSE;
197     *token = NULL;
198 
199     if (s == NULL) {
200         return NULL;
201     }
202 
203     while (*s == ' ' || *s == '\t') {
204         s++;
205     }
206     if (*s == '"') {
207         s++;
208         *token = s;
209         while (*s != '\0' && (*s != '"' || (*s == '"' && *(s - 1) == '\\'))) {
210             s++;
211         }
212         if (*s == '"') {
213             /* successfully identified a quoted string */
214             *quoted = TRUE;
215         }
216     } else {
217         *token = s;
218         if (**token == '\n') {
219             /* EOL reached */
220             return NULL;
221         }
222         while (*s != '\n' && *s != '\0' && *s != ' ' && *s != '\t') {
223             s++;
224         }
225     }
226 
227     if (*s != '\0') {
228         *s = '\0';
229         s++;
230         return s;
231     } else {
232         return NULL;
233     }
234 }
235 
parse_ss_row(const char * s,int * nncols,int * nscols,int ** formats)236 int parse_ss_row(const char *s, int *nncols, int *nscols, int **formats)
237 {
238     int ncols;
239     int quoted;
240     char *buf, *s1, *token;
241     double value;
242     Dates_format df_pref, ddummy;
243     const char *sdummy;
244 
245     *nscols = 0;
246     *nncols = 0;
247     *formats = NULL;
248     df_pref = get_date_hint();
249     buf = copy_string(NULL, s);
250     s1 = buf;
251     while ((s1 = next_token(s1, &token, &quoted)) != NULL) {
252         if (token == NULL) {
253             *nscols = 0;
254             *nncols = 0;
255             XCFREE(*formats);
256             xfree(buf);
257             return RETURN_FAILURE;
258         }
259 
260         ncols = *nncols + *nscols;
261         /* reallocate the formats array */
262         if (ncols % 10 == 0) {
263             *formats = xrealloc(*formats, (ncols + 10)*SIZEOF_INT);
264         }
265 
266         if (quoted) {
267             (*formats)[ncols] = FFORMAT_STRING;
268             (*nscols)++;
269         } else if (parse_date(token, df_pref, FALSE, &value, &ddummy) ==
270             RETURN_SUCCESS) {
271             (*formats)[ncols] = FFORMAT_DATE;
272             (*nncols)++;
273         } else if (parse_float(token, &value, &sdummy) == RETURN_SUCCESS) {
274             (*formats)[ncols] = FFORMAT_NUMBER;
275             (*nncols)++;
276         } else {
277             /* last resort - treat the field as string, even if not quoted */
278             (*formats)[ncols] = FFORMAT_STRING;
279             (*nscols)++;
280         }
281     }
282     xfree(buf);
283 
284     return RETURN_SUCCESS;
285 }
286 
287 
288 /* NOTE: the input string will be corrupted! */
insert_data_row(ss_data * ssd,int row,char * s)289 int insert_data_row(ss_data *ssd, int row, char *s)
290 {
291     int i, j;
292     int ncols = ssd->ncols;
293     char *token;
294     int quoted;
295     char  **sp;
296     double *np;
297     Dates_format df_pref, ddummy;
298     const char *sdummy;
299     int res;
300 
301     df_pref = get_date_hint();
302     for (i = 0; i < ncols; i++) {
303         s = next_token(s, &token, &quoted);
304         if (s == NULL || token == NULL) {
305             /* invalid line: free the already allocated string fields */
306             for (j = 0; j < i; j++) {
307                 if (ssd->formats[j] == FFORMAT_STRING) {
308                     sp = (char **) ssd->data[j];
309                     XCFREE(sp[row]);
310                 }
311             }
312             return RETURN_FAILURE;
313         } else {
314             if (ssd->formats[i] == FFORMAT_STRING) {
315                 sp = (char **) ssd->data[i];
316                 sp[row] = copy_string(NULL, token);
317                 if (sp[row] != NULL) {
318                     res = RETURN_SUCCESS;
319                 } else {
320                     res = RETURN_FAILURE;
321                 }
322             } else if (ssd->formats[i] == FFORMAT_DATE) {
323                 np = (double *) ssd->data[i];
324                 res = parse_date(token, df_pref, FALSE, &np[row], &ddummy);
325             } else {
326                 np = (double *) ssd->data[i];
327                 res = parse_float(token, &np[row], &sdummy);
328             }
329             if (res != RETURN_SUCCESS) {
330                 for (j = 0; j < i; j++) {
331                     if (ssd->formats[j] == FFORMAT_STRING) {
332                         sp = (char **) ssd->data[j];
333                         XCFREE(sp[row]);
334                     }
335                 }
336                 return RETURN_FAILURE;
337             }
338         }
339     }
340 
341     return RETURN_SUCCESS;
342 }
343 
344 
store_data(ss_data * ssd,int load_type,char * label)345 int store_data(ss_data *ssd, int load_type, char *label)
346 {
347     int ncols, nncols, nncols_req, nscols, nrows;
348     int i, j;
349     double *xdata;
350     int gno, setno;
351     int x_from_index;
352 
353     if (ssd == NULL) {
354         return RETURN_FAILURE;
355     }
356     ncols = ssd->ncols;
357     nrows = ssd->nrows;
358     if (ncols <= 0 || nrows <= 0) {
359         return RETURN_FAILURE;
360     }
361 
362     nncols = 0;
363     for (j = 0; j < ncols; j++) {
364         if (ssd->formats[j] != FFORMAT_STRING) {
365             nncols++;
366         }
367     }
368     nscols = ncols - nncols;
369 
370     gno = get_parser_gno();
371     if (is_valid_gno(gno) != TRUE) {
372         return RETURN_FAILURE;
373     }
374 
375     switch (load_type) {
376     case LOAD_SINGLE:
377         if (nscols > 1) {
378             errmsg("Can not use more than one column of strings per set");
379             free_ss_data(ssd);
380             return RETURN_FAILURE;
381         }
382 
383         nncols_req = settype_cols(curtype);
384         x_from_index = FALSE;
385         if (nncols_req == nncols + 1) {
386             x_from_index = TRUE;
387         } else if (nncols_req != nncols) {
388 	    errmsg("Column count incorrect");
389 	    return RETURN_FAILURE;
390         }
391 
392         setno = nextset(gno);
393         set_dataset_type(gno, setno, curtype);
394 
395         nncols = 0;
396         if (x_from_index) {
397             xdata = allocate_index_data(nrows);
398             if (xdata == NULL) {
399                 free_ss_data(ssd);
400             }
401             setcol(gno, setno, nncols, xdata, nrows);
402             nncols++;
403         }
404         for (j = 0; j < ncols; j++) {
405             if (ssd->formats[j] == FFORMAT_STRING) {
406                 set_set_strings(gno, setno, nrows, (char **) ssd->data[j]);
407             } else {
408                 setcol(gno, setno, nncols, (double *) ssd->data[j], nrows);
409                 nncols++;
410             }
411         }
412         if (!strlen(getcomment(gno, setno))) {
413             setcomment(gno, setno, label);
414         }
415 
416         XCFREE(ssd->data);
417         XCFREE(ssd->formats);
418         break;
419     case LOAD_NXY:
420         if (nscols != 0) {
421             errmsg("Can not yet use strings when reading in data as NXY");
422             free_ss_data(ssd);
423             return RETURN_FAILURE;
424         }
425 
426         for (i = 0; i < ncols - 1; i++) {
427             setno = nextset(gno);
428             if (setno == -1) {
429                 free_ss_data(ssd);
430                 return RETURN_FAILURE;
431             }
432             if (i > 0) {
433                 xdata = copy_data_column((double *) ssd->data[0], nrows);
434                 if (xdata == NULL) {
435                     free_ss_data(ssd);
436                 }
437             } else {
438                 xdata = (double *) ssd->data[0];
439             }
440             set_dataset_type(gno, setno, SET_XY);
441             setcol(gno, setno, DATA_X, xdata, nrows);
442             setcol(gno, setno, DATA_Y, (double *) ssd->data[i + 1], nrows);
443             setcomment(gno, setno, label);
444         }
445 
446         XCFREE(ssd->data);
447         XCFREE(ssd->formats);
448         break;
449     case LOAD_BLOCK:
450         set_blockdata(ssd);
451         break;
452     default:
453         errmsg("Internal error");
454         free_ss_data(ssd);
455         return RETURN_FAILURE;
456     }
457 
458     return RETURN_SUCCESS;
459 }
460 
field_string_to_cols(const char * fs,int * nc,int ** cols,int * scol)461 int field_string_to_cols(const char *fs, int *nc, int **cols, int *scol)
462 {
463     int col;
464     char *s, *buf;
465 
466     buf = copy_string(NULL, fs);
467     if (buf == NULL) {
468         return RETURN_FAILURE;
469     }
470 
471     s = buf;
472     *nc = 0;
473     while ((s = strtok(s, ":")) != NULL) {
474 	(*nc)++;
475 	s = NULL;
476     }
477     *cols = xmalloc((*nc)*SIZEOF_INT);
478     if (*cols == NULL) {
479         xfree(buf);
480         return RETURN_FAILURE;
481     }
482 
483     strcpy(buf, fs);
484     s = buf;
485     *nc = 0;
486     *scol = -1;
487     while ((s = strtok(s, ":")) != NULL) {
488         int strcol;
489         if (*s == '{') {
490             char *s1;
491             strcol = TRUE;
492             s++;
493             if ((s1 = strchr(s, '}')) != NULL) {
494                 *s1 = '\0';
495             }
496         } else {
497             strcol = FALSE;
498         }
499         col = atoi(s);
500         col--;
501         if (strcol) {
502             *scol = col;
503         } else {
504             (*cols)[*nc] = col;
505 	    (*nc)++;
506         }
507 	s = NULL;
508     }
509 
510     xfree(buf);
511 
512     return RETURN_SUCCESS;
513 }
514 
cols_to_field_string(int nc,int * cols,int scol)515 char *cols_to_field_string(int nc, int *cols, int scol)
516 {
517     int i;
518     char *s, buf[32];
519 
520     s = NULL;
521     for (i = 0; i < nc; i++) {
522         sprintf(buf, "%d", cols[i] + 1);
523         if (i != 0) {
524             s = concat_strings(s, ":");
525         }
526         s = concat_strings(s, buf);
527     }
528     if (scol >= 0) {
529         sprintf(buf, ":{%d}", scol + 1);
530         s = concat_strings(s, buf);
531     }
532 
533     return s;
534 }
535 
create_set_fromblock(int gno,int setno,int type,int nc,int * coli,int scol,int autoscale)536 int create_set_fromblock(int gno, int setno,
537     int type, int nc, int *coli, int scol, int autoscale)
538 {
539     int i, ncols, blockncols, blocklen, column;
540     double *cdata;
541     char buf[256], *s;
542 
543     blockncols = get_blockncols();
544     if (blockncols <= 0) {
545         errmsg("No block data read");
546         return RETURN_FAILURE;
547     }
548 
549     blocklen = get_blocknrows();
550 
551     ncols = settype_cols(type);
552     if (nc > ncols) {
553         errmsg("Too many columns scanned in column string");
554         return RETURN_FAILURE;
555     }
556     if (nc < ncols) {
557 	errmsg("Too few columns scanned in column string");
558 	return RETURN_FAILURE;
559     }
560 
561     for (i = 0; i < nc; i++) {
562 	if (coli[i] < -1 || coli[i] >= blockncols) {
563 	    errmsg("Column index out of range");
564 	    return RETURN_FAILURE;
565 	}
566     }
567 
568     if (scol >= blockncols) {
569 	errmsg("String column index out of range");
570 	return RETURN_FAILURE;
571     }
572 
573     if (setno == NEW_SET) {
574         setno = nextset(gno);
575         if (setno == -1) {
576             return RETURN_FAILURE;
577         }
578     }
579 
580     /* clear data stored in the set, if any */
581     killsetdata(gno, setno);
582 
583     if (activateset(gno, setno) != RETURN_SUCCESS) {
584         return RETURN_FAILURE;
585     }
586 
587     set_dataset_type(gno, setno, type);
588 
589     for (i = 0; i < nc; i++) {
590         column = coli[i];
591         if (column == -1) {
592             cdata = allocate_index_data(blocklen);
593         } else {
594             if (blockdata.formats[column] != FFORMAT_STRING) {
595                 cdata = copy_data_column((double *) blockdata.data[column], blocklen);
596             } else {
597                 errmsg("Tried to read doubles from strings!");
598                 killsetdata(gno, setno);
599                 return RETURN_FAILURE;
600             }
601         }
602         if (cdata == NULL) {
603             killsetdata(gno, setno);
604             return RETURN_FAILURE;
605         }
606         setcol(gno, setno, i, cdata, blocklen);
607     }
608 
609     /* strings, if any */
610     if (scol >= 0) {
611         if (blockdata.formats[scol] != FFORMAT_STRING) {
612             errmsg("Tried to read strings from doubles!");
613             killsetdata(gno, setno);
614             return RETURN_FAILURE;
615         } else {
616             set_set_strings(gno, setno, blocklen,
617                 copy_string_column((char **) blockdata.data[scol], blocklen));
618         }
619     }
620 
621     s = cols_to_field_string(nc, coli, scol);
622     sprintf(buf, "Cols %s", s);
623     xfree(s);
624     setcomment(gno, setno, buf);
625 
626     autoscale_graph(gno, autoscale);
627 
628     return RETURN_SUCCESS;
629 }
630