1 /* GNUPLOT - datablock.c */
2 
3 /*[
4  * Copyright Ethan A Merritt 2012
5  *
6  * Gnuplot license:
7  *
8  * Permission to use, copy, and distribute this software and its
9  * documentation for any purpose with or without fee is hereby granted,
10  * provided that the above copyright notice appear in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation.
13  *
14  * Permission to modify the software is granted, but not the right to
15  * distribute the complete modified source code.  Modifications are to
16  * be distributed as patches to the released version.  Permission to
17  * distribute binaries produced by compiling modified sources is granted,
18  * provided you
19  *   1. distribute the corresponding source modifications from the
20  *    released version in the form of a patch file along with the binaries,
21  *   2. add special version identification to distinguish your version
22  *    in addition to the base release version number,
23  *   3. provide your name and address as the primary contact for the
24  *    support of your modified version, and
25  *   4. retain our contact information in regard to use of the base
26  *    software.
27  * Permission to distribute the released version of the source code along
28  * with corresponding source modifications in the form of a patch file is
29  * granted with same provisions 2 through 4 for binary distributions.
30  *
31  * This software is provided "as is" without express or implied warranty
32  * to the extent permitted by applicable law.
33  *
34  * Alternative license:
35  *
36  * As an alternative to distributing code in this file under the gnuplot license,
37  * you may instead comply with the terms below. In this case, redistribution and
38  * use in source and binary forms, with or without modification, are permitted
39  * provided that the following conditions are met:
40  *
41  * Redistributions of source code must retain the above copyright notice, this
42  * list of conditions and the following disclaimer.  Redistributions in binary
43  * form must reproduce the above copyright notice, this list of conditions and
44  * the following disclaimer in the documentation and/or other materials provided
45  * with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57  * POSSIBILITY OF SUCH DAMAGE.
58  *
59 ]*/
60 
61 #include "gp_types.h"
62 #include "alloc.h"
63 #include "command.h"
64 #include "datablock.h"
65 #include "datafile.h"
66 #include "eval.h"
67 #include "misc.h"
68 #include "util.h"
69 
70 static int enlarge_datablock(struct value *datablock_value, int extra);
71 
72 
73 /*
74  * In-line data blocks are implemented as a here-document:
75  * $FOO << EOD
76  *  data line 1
77  *  data line 2
78  *  ...
79  * EOD
80  *
81  * The data block name must begin with $ followed by a letter.
82  * The string EOD is arbitrary; lines of data will be read from the input stream
83  * until the leading characters on the line match the given character string.
84  * No attempt is made to parse the data at the time it is read in.
85  */
86 void
datablock_command()87 datablock_command()
88 {
89     FILE *fin;
90     char *name, *eod;
91     int nlines;
92     int nsize = 4;
93     struct udvt_entry *datablock;
94     char *dataline = NULL;
95 
96     if (!isletter(c_token+1))
97 	int_error(c_token, "illegal datablock name");
98 
99     /* Create or recycle a datablock with the requested name */
100     name = parse_datablock_name();
101     datablock = add_udv_by_name(name);
102 
103     if (!equals(c_token, "<<") || !isletter(c_token+1))
104 	int_error(c_token, "data block name must be followed by << EODmarker");
105 
106     if (datablock->udv_value.type != NOTDEFINED)
107 	gpfree_datablock(&datablock->udv_value);
108     datablock->udv_value.type = DATABLOCK;
109     datablock->udv_value.v.data_array = NULL;
110 
111     c_token++;
112     eod = (char *) gp_alloc(token[c_token].length +2, "datablock");
113     copy_str(&eod[0], c_token, token[c_token].length + 2);
114     c_token++;
115 
116     /* Read in and store data lines until EOD */
117     fin = (lf_head == NULL) ? stdin : lf_head->fp;
118     if (!fin)
119 	int_error(NO_CARET,"attempt to define data block from invalid context");
120     for (nlines = 0; (dataline = df_fgets(fin)); nlines++) {
121 	int n;
122 
123 	if (!strncmp(eod, dataline, strlen(eod)))
124 	    break;
125 	/* Allocate space for data lines plus at least 2 empty lines at the end. */
126 	if (nlines >= nsize-4) {
127 	    nsize *= 2;
128 	    datablock->udv_value.v.data_array =
129 		(char **) gp_realloc(datablock->udv_value.v.data_array,
130 			nsize * sizeof(char *), "datablock");
131 	    memset(&datablock->udv_value.v.data_array[nlines], 0,
132 		    (nsize - nlines) * sizeof(char *));
133 	}
134 	/* Strip trailing newline character */
135 	n = strlen(dataline);
136 	if (n > 0 && dataline[n - 1] == '\n')
137 	    dataline[n - 1] = NUL;
138 	datablock->udv_value.v.data_array[nlines] = gp_strdup(dataline);
139     }
140     inline_num += nlines + 1;	/* Update position in input file */
141 
142     /* make sure that we can safely add lines to this datablock later on */
143     enlarge_datablock(&datablock->udv_value, 0);
144 
145     free(eod);
146     return;
147 }
148 
149 
150 char *
parse_datablock_name()151 parse_datablock_name()
152 {
153     /* Datablock names begin with $, but the scanner puts  */
154     /* the $ in a separate token.  Merge it with the next. */
155     /* Caller must _not_ free the string that is returned. */
156     static char *name = NULL;
157 
158     free(name);
159     c_token++;
160     name = (char *) gp_alloc(token[c_token].length + 2, "datablock");
161     name[0] = '$';
162     copy_str(&name[1], c_token, token[c_token].length + 2);
163     c_token++;
164 
165     return name;
166 }
167 
168 
169 char **
get_datablock(char * name)170 get_datablock(char *name)
171 {
172     struct udvt_entry *datablock;
173 
174     datablock = get_udv_by_name(name);
175     if (!datablock || datablock->udv_value.type != DATABLOCK
176     ||  datablock->udv_value.v.data_array == NULL)
177 	int_error(NO_CARET,"no datablock named %s",name);
178 
179     return datablock->udv_value.v.data_array;
180 }
181 
182 
183 void
gpfree_datablock(struct value * datablock_value)184 gpfree_datablock(struct value *datablock_value)
185 {
186     int i;
187     char **stored_data = datablock_value->v.data_array;
188 
189     if (datablock_value->type != DATABLOCK)
190 	return;
191     if (stored_data)
192 	for (i=0; stored_data[i] != NULL; i++)
193 	    free(stored_data[i]);
194     free(stored_data);
195     datablock_value->v.data_array = NULL;
196 }
197 
198 /* count number of lines in a datablock */
199 int
datablock_size(struct value * datablock_value)200 datablock_size(struct value *datablock_value)
201 {
202     char **dataline;
203     int nlines = 0;
204 
205     dataline = datablock_value->v.data_array;
206     if (dataline) {
207 	while (*dataline++)
208 	    nlines++;
209     }
210     return nlines;
211 }
212 
213 /* resize or allocate a datablock; allocate memory in chunks */
214 static int
enlarge_datablock(struct value * datablock_value,int extra)215 enlarge_datablock(struct value *datablock_value, int extra)
216 {
217     int osize, nsize;
218     const int blocksize = 512;
219     int nlines = datablock_size(datablock_value);
220 
221     /* reserve space in multiples of blocksize */
222     osize = ((nlines+1 + blocksize-1) / blocksize) * blocksize;
223     nsize = ((nlines+1 + extra + blocksize-1) / blocksize) * blocksize;
224 
225     /* only resize if necessary */
226     if ((osize != nsize) || (extra == 0) || (nlines == 0)) {
227 	datablock_value->v.data_array =
228 	    (char **) gp_realloc(datablock_value->v.data_array,  nsize * sizeof(char *), "resize_datablock");
229 	datablock_value->v.data_array[nlines] = NULL;
230     }
231 
232     return nlines;
233 }
234 
235 
236 /* append a single line to a datablock */
237 void
append_to_datablock(struct value * datablock_value,const char * line)238 append_to_datablock(struct value *datablock_value, const char *line)
239 {
240     int nlines = enlarge_datablock(datablock_value, 1);
241     datablock_value->v.data_array[nlines] = (char *) line;
242     datablock_value->v.data_array[nlines + 1] = NULL;
243 }
244 
245 
246 /* append multiple lines which are separated by linebreaks to a datablock */
247 void
append_multiline_to_datablock(struct value * datablock_value,const char * lines)248 append_multiline_to_datablock(struct value *datablock_value, const char *lines)
249 {
250     char * l = (char *) lines;
251     char * p;
252     TBOOLEAN inquote = FALSE;
253     TBOOLEAN escaped = FALSE;
254 
255     /* handle lines with line-breaks, one at a time;
256        take care of quoted strings
257      */
258     p = l;
259     while (*p != NUL) {
260 	if (*p == '\'' && !escaped)
261 	    inquote = !inquote;
262 	else if (*p == '\\' && !escaped)
263 	    escaped = TRUE;
264 	else if (*p == '\n' && !inquote) {
265 	    *p = NUL;
266 	    append_to_datablock(datablock_value, strdup(l));
267 	    l = p + 1;
268 	} else
269 	    escaped = FALSE;
270         p++;
271     }
272     if (l == lines) {
273 	/* no line-breaks, just a single line */
274 	append_to_datablock(datablock_value, l);
275     } else {
276 	if (strlen(l) > 0) /* remainder after last line-break */
277 	    append_to_datablock(datablock_value, strdup(l));
278 	/* we allocated new sub-strings, free the original */
279 	free((char *) lines);
280     }
281 }
282 
283