Blame src/datablock.c

Packit 0986c0
/*
Packit 0986c0
 * $Id: datablock.c,v 1.8.2.1 2017/06/05 20:40:28 sfeam Exp $
Packit 0986c0
 */
Packit 0986c0
/* GNUPLOT - datablock.c */
Packit 0986c0
Packit 0986c0
/*[
Packit 0986c0
 * Copyright Ethan A Merritt 2012
Packit 0986c0
 *
Packit 0986c0
 * Gnuplot license:
Packit 0986c0
 *
Packit 0986c0
 * Permission to use, copy, and distribute this software and its
Packit 0986c0
 * documentation for any purpose with or without fee is hereby granted,
Packit 0986c0
 * provided that the above copyright notice appear in all copies and
Packit 0986c0
 * that both that copyright notice and this permission notice appear
Packit 0986c0
 * in supporting documentation.
Packit 0986c0
 *
Packit 0986c0
 * Permission to modify the software is granted, but not the right to
Packit 0986c0
 * distribute the complete modified source code.  Modifications are to
Packit 0986c0
 * be distributed as patches to the released version.  Permission to
Packit 0986c0
 * distribute binaries produced by compiling modified sources is granted,
Packit 0986c0
 * provided you
Packit 0986c0
 *   1. distribute the corresponding source modifications from the
Packit 0986c0
 *    released version in the form of a patch file along with the binaries,
Packit 0986c0
 *   2. add special version identification to distinguish your version
Packit 0986c0
 *    in addition to the base release version number,
Packit 0986c0
 *   3. provide your name and address as the primary contact for the
Packit 0986c0
 *    support of your modified version, and
Packit 0986c0
 *   4. retain our contact information in regard to use of the base
Packit 0986c0
 *    software.
Packit 0986c0
 * Permission to distribute the released version of the source code along
Packit 0986c0
 * with corresponding source modifications in the form of a patch file is
Packit 0986c0
 * granted with same provisions 2 through 4 for binary distributions.
Packit 0986c0
 *
Packit 0986c0
 * This software is provided "as is" without express or implied warranty
Packit 0986c0
 * to the extent permitted by applicable law.
Packit 0986c0
 *
Packit 0986c0
 * Alternative license:
Packit 0986c0
 *
Packit 0986c0
 * As an alternative to distributing code in this file under the gnuplot license,
Packit 0986c0
 * you may instead comply with the terms below. In this case, redistribution and
Packit 0986c0
 * use in source and binary forms, with or without modification, are permitted
Packit 0986c0
 * provided that the following conditions are met:
Packit 0986c0
 *
Packit 0986c0
 * Redistributions of source code must retain the above copyright notice, this
Packit 0986c0
 * list of conditions and the following disclaimer.  Redistributions in binary
Packit 0986c0
 * form must reproduce the above copyright notice, this list of conditions and
Packit 0986c0
 * the following disclaimer in the documentation and/or other materials provided
Packit 0986c0
 * with the distribution.
Packit 0986c0
 *
Packit 0986c0
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit 0986c0
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
Packit 0986c0
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit 0986c0
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
Packit 0986c0
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
Packit 0986c0
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
Packit 0986c0
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
Packit 0986c0
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
Packit 0986c0
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
Packit 0986c0
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
Packit 0986c0
 * POSSIBILITY OF SUCH DAMAGE.
Packit 0986c0
 *
Packit 0986c0
]*/
Packit 0986c0
Packit 0986c0
#include "gp_types.h"
Packit 0986c0
#include "alloc.h"
Packit 0986c0
#include "command.h"
Packit 0986c0
#include "datablock.h"
Packit 0986c0
#include "datafile.h"
Packit 0986c0
#include "eval.h"
Packit 0986c0
#include "misc.h"
Packit 0986c0
#include "util.h"
Packit 0986c0
Packit 0986c0
static int enlarge_datablock(struct value *datablock_value, int extra);
Packit 0986c0
Packit 0986c0
Packit 0986c0
/*
Packit 0986c0
 * In-line data blocks are implemented as a here-document:
Packit 0986c0
 * $FOO << EOD
Packit 0986c0
 *  data line 1
Packit 0986c0
 *  data line 2
Packit 0986c0
 *  ...
Packit 0986c0
 * EOD
Packit 0986c0
 *
Packit 0986c0
 * The data block name must begin with $ followed by a letter.
Packit 0986c0
 * The string EOD is arbitrary; lines of data will be read from the input stream
Packit 0986c0
 * until the leading characters on the line match the given character string.
Packit 0986c0
 * No attempt is made to parse the data at the time it is read in.
Packit 0986c0
 */
Packit 0986c0
void
Packit 0986c0
datablock_command()
Packit 0986c0
{
Packit 0986c0
    FILE *fin;
Packit 0986c0
    char *name, *eod;
Packit 0986c0
    int nlines;
Packit 0986c0
    int nsize = 4;
Packit 0986c0
    struct udvt_entry *datablock;
Packit 0986c0
    char *dataline = NULL;
Packit 0986c0
Packit 0986c0
    if (!isletter(c_token+1))
Packit 0986c0
	int_error(c_token, "illegal datablock name");
Packit 0986c0
Packit 0986c0
    /* Create or recycle a datablock with the requested name */
Packit 0986c0
    name = parse_datablock_name();
Packit 0986c0
    datablock = add_udv_by_name(name);
Packit 0986c0
Packit 0986c0
    if (!equals(c_token, "<<") || !isletter(c_token+1))
Packit 0986c0
	int_error(c_token, "data block name must be followed by << EODmarker");
Packit 0986c0
Packit 0986c0
    if (datablock->udv_value.type != NOTDEFINED)
Packit 0986c0
	gpfree_datablock(&datablock->udv_value);
Packit 0986c0
    datablock->udv_value.type = DATABLOCK;
Packit 0986c0
    datablock->udv_value.v.data_array = NULL;
Packit 0986c0
Packit 0986c0
    c_token++;
Packit 0986c0
    eod = (char *) gp_alloc(token[c_token].length +2, "datablock");
Packit 0986c0
    copy_str(&eod[0], c_token, token[c_token].length + 2);
Packit 0986c0
    c_token++;
Packit 0986c0
Packit 0986c0
    /* Read in and store data lines until EOD */
Packit 0986c0
    fin = (lf_head == NULL) ? stdin : lf_head->fp;
Packit 0986c0
    if (!fin)
Packit 0986c0
	int_error(NO_CARET,"attempt to define data block from invalid context");
Packit 0986c0
    for (nlines = 0; (dataline = df_fgets(fin)); nlines++) {
Packit 0986c0
	int n;
Packit 0986c0
Packit 0986c0
	if (!strncmp(eod, dataline, strlen(eod)))
Packit 0986c0
	    break;
Packit 0986c0
	/* Allocate space for data lines plus at least 2 empty lines at the end. */
Packit 0986c0
	if (nlines >= nsize-4) {
Packit 0986c0
	    nsize *= 2;
Packit 0986c0
	    datablock->udv_value.v.data_array =
Packit 0986c0
		(char **) gp_realloc(datablock->udv_value.v.data_array,
Packit 0986c0
			nsize * sizeof(char *), "datablock");
Packit 0986c0
	    memset(&datablock->udv_value.v.data_array[nlines], 0,
Packit 0986c0
		    (nsize - nlines) * sizeof(char *));
Packit 0986c0
	}
Packit 0986c0
	/* Strip trailing newline character */
Packit 0986c0
	n = strlen(dataline);
Packit 0986c0
	if (n > 0 && dataline[n - 1] == '\n')
Packit 0986c0
	    dataline[n - 1] = NUL;
Packit 0986c0
	datablock->udv_value.v.data_array[nlines] = gp_strdup(dataline);
Packit 0986c0
    }
Packit 0986c0
    inline_num += nlines + 1;	/* Update position in input file */
Packit 0986c0
Packit 0986c0
    /* make sure that we can safely add lines to this datablock later on */
Packit 0986c0
    enlarge_datablock(&datablock->udv_value, 0);
Packit 0986c0
Packit 0986c0
    free(eod);
Packit 0986c0
    return;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
Packit 0986c0
char *
Packit 0986c0
parse_datablock_name()
Packit 0986c0
{
Packit 0986c0
    /* Datablock names begin with $, but the scanner puts  */
Packit 0986c0
    /* the $ in a separate token.  Merge it with the next. */
Packit 0986c0
    /* Caller must _not_ free the string that is returned. */
Packit 0986c0
    static char *name = NULL;
Packit 0986c0
Packit 0986c0
    free(name);
Packit 0986c0
    c_token++;
Packit 0986c0
    name = (char *) gp_alloc(token[c_token].length + 2, "datablock");
Packit 0986c0
    name[0] = '$';
Packit 0986c0
    copy_str(&name[1], c_token, token[c_token].length + 2);
Packit 0986c0
    c_token++;
Packit 0986c0
Packit 0986c0
    return name;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
Packit 0986c0
char **
Packit 0986c0
get_datablock(char *name)
Packit 0986c0
{
Packit 0986c0
    struct udvt_entry *datablock;
Packit 0986c0
Packit 0986c0
    datablock = get_udv_by_name(name);
Packit 0986c0
    if (!datablock || datablock->udv_value.type == NOTDEFINED
Packit 0986c0
    ||  datablock->udv_value.v.data_array == NULL)
Packit 0986c0
	int_error(NO_CARET,"no datablock named %s",name);
Packit 0986c0
Packit 0986c0
    return datablock->udv_value.v.data_array;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
Packit 0986c0
void
Packit 0986c0
gpfree_datablock(struct value *datablock_value)
Packit 0986c0
{
Packit 0986c0
    int i;
Packit 0986c0
    char **stored_data = datablock_value->v.data_array;
Packit 0986c0
Packit 0986c0
    if (datablock_value->type != DATABLOCK)
Packit 0986c0
	return;
Packit 0986c0
    if (stored_data)
Packit 0986c0
	for (i=0; stored_data[i] != NULL; i++)
Packit 0986c0
	    free(stored_data[i]);
Packit 0986c0
    free(stored_data);
Packit 0986c0
    datablock_value->v.data_array = NULL;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
/* count number of lines in a datablock */
Packit 0986c0
int
Packit 0986c0
datablock_size(struct value *datablock_value)
Packit 0986c0
{
Packit 0986c0
    char **dataline;
Packit 0986c0
    int nlines = 0;
Packit 0986c0
Packit 0986c0
    dataline = datablock_value->v.data_array;
Packit 0986c0
    if (dataline) {
Packit 0986c0
	while (*dataline++)
Packit 0986c0
	    nlines++;
Packit 0986c0
    }
Packit 0986c0
    return nlines;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
/* resize or allocate a datablock; allocate memory in chuncks */
Packit 0986c0
static int
Packit 0986c0
enlarge_datablock(struct value *datablock_value, int extra)
Packit 0986c0
{
Packit 0986c0
    int osize, nsize;
Packit 0986c0
    const int blocksize = 512;
Packit 0986c0
    int nlines = datablock_size(datablock_value);
Packit 0986c0
Packit 0986c0
    /* reserve space in multiples of blocksize */
Packit 0986c0
    osize = ((nlines+1 + blocksize-1) / blocksize) * blocksize; 
Packit 0986c0
    nsize = ((nlines+1 + extra + blocksize-1) / blocksize) * blocksize;
Packit 0986c0
Packit 0986c0
    /* only resize if necessary */
Packit 0986c0
    if ((osize != nsize) || (extra == 0) || (nlines == 0)) {
Packit 0986c0
	datablock_value->v.data_array =
Packit 0986c0
	    (char **) gp_realloc(datablock_value->v.data_array,  nsize * sizeof(char *), "resize_datablock");
Packit 0986c0
	datablock_value->v.data_array[nlines] = NULL;
Packit 0986c0
    }
Packit 0986c0
Packit 0986c0
    return nlines;
Packit 0986c0
}
Packit 0986c0
Packit 0986c0
Packit 0986c0
/* append a single line to a datablock */
Packit 0986c0
void
Packit 0986c0
append_to_datablock(struct value *datablock_value, const char *line)
Packit 0986c0
{
Packit 0986c0
    int nlines = enlarge_datablock(datablock_value, 1);
Packit 0986c0
    datablock_value->v.data_array[nlines] = (char *) line;
Packit 0986c0
    datablock_value->v.data_array[nlines + 1] = NULL;
Packit 0986c0
}