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