ftp.nice.ch/pub/next/tools/frontends/Gnuplot.I.bs.tar.gz#/Gnuplot/GnuplotSource/Status.m

This is Status.m in view mode; [Download] [Up]

/*
 *  Copyright (C) 1993  Robert Davis
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of Version 2, or any later version, of 
 *  the GNU General Public License as published by the Free Software 
 *  Foundation.
 */

static char RCSId[] = "$Id: Status.m,v 1.18 1993/05/30 09:10:15 davis Exp $";


#import <appkit/Font.h>
#import <appkit/nextstd.h>	/* NX_FREE			*/

#import <objc/hashtable.h>	/* NXCopyStringBufferFromZone()	*/
#import <objc/List.h>
#import <objc/zone.h>

#import <ctype.h>		/* isspace()			*/
#import <setjmp.h>		/* setjmp() 			*/
#import <stdio.h>
#import <strings.h>		/* strcat(), strcpy(), index()	*/

#import "FunctionObject.h"
#import "GnuplotPlot.h"
#import "Status.h"

#import "StatusContour.h"	/* Status categories		*/
#import "StatusTics.h"


@interface Status (Private)
- _grabFunctionsFrom: (char *)aString isThreeD:(BOOL)aCond;
- _buildPlotargs;
- _buildUsing:(char *)aString forFunction:(FunctionObject *)aFunction;
- (const char *) _copyString:(const char *)source to:(char **)dest;
- _doText:(const char *)theText;
- _setText:(const char *) aString;
@end


/* 
 *  The class remembers which Status object was the last to issue a 
 *  plot command to Gnuplot.  We need to know this when we set our 
 *  instance variables to the corresponding Gnuplot values -- see 
 *  -grabCurrent.
 */
static id	lastplot = nil;


#define MAX_LINE_LEN 1024	/* Same as in plot.h	*/
#define DEFAULT_FONT		"Helvetica"

#define POLAR		0	/* Two sets of range information	*/
#define CARTESIAN	1


/*
 *  All instance variables begin with s_ to avoid confusion with the 
 *  variables defined in gnuplot .c files.
 *
 *  gnuplot Variable (setshow.c)	   Status Instance Variable
 *  ----------------------------	   ------------------------
 */
extern char nextfe_font[];		/* s_font			*/
extern int nextfe_fontsize;		/* s_fontsize			*/
extern int nextfe_solid;		// unused
extern char title[];			/* s_title			*/
extern char xlabel[];			/* s_labelCoord[]		*/
extern char ylabel[];
extern char zlabel[];
extern char dummy_var[][51];		/* s_dummy_var[]		*/
extern int parametric;			/* s_parametric			*/
extern int polar;			/* s_polar			*/
extern int draw_border;			/* s_border			*/
extern int draw_surface;		/* s_draw_surface		*/
extern int grid;			/* s_grid			*/
extern int xzeroaxis;			/* s_zeroaxis[]			*/
extern int yzeroaxis;
extern int is_log_x, is_log_y, is_log_z;/* s_isLogCoord[],s_isLogPol...	*/

extern double base_log_x, base_log_y,	/* Currently unused by Status	*/
	      base_log_z;		//
extern double log_base_log_x,
	      log_base_log_y,
	      log_base_log_z;

extern int key;				/* s_key			*/
extern double key_x, key_y, key_z;	/* s_keyCoord[]			*/
extern int timedate;			/* s_time			*/
extern int time_xoffset, time_yoffset;	/* s_timeCoord[]		*/
extern float xsize, ysize;		/* s_xsize, s_ysize		*/
extern float surface_rot_x;		/* s_rot_x			*/
extern float surface_rot_z;		/* s_rot_z			*/
extern int hidden3d;			/* s_hidden3d			*/
extern int autoscale_x;			/* s_autoscaleCoord[][]		*/
extern int autoscale_y;
extern int autoscale_z;
extern int autoscale_r;
extern double xmin, xmax;		/* s_minCoord[][],s_maxCoord[][]*/
extern double ymin, ymax;
extern double zmin, zmax;
extern double rmin, rmax;
extern int angles_format;		/* degrees			*/
extern int samples;			/* s_samples			*/
extern int samples_1;			/* s_samplesCoord		*/
extern int samples_2;
extern int iso_samples_1;		/* s_iso_samplesCoord		*/
extern int iso_samples_2;
extern char replot_line[];		/* s_plotargs			*/
extern NXStream *EPSStream;		/* s_epsStream (terminal)	*/
extern int nextfe_halve;		/* (GnuplotPlot preference)	*/
extern NEXTFE_reset();			/* Appends PS Trailer to stream	*/


/*
 *  Removes leading blanks and tabs.
 */
static char *_removeLeadingBlanks (char *aString)
{
    if (aString && *aString) {
	char *cur = aString;

	while (*cur && (isspace(*cur)))
	    cur++;

	strcpy (aString, cur);
    }

    return aString;
}


/*
 *  Stores the first line, up to max characters,  from the multi-line 
 *  string source in dest and returns a pointer to it.  It replaces 
 *  the newline with a null character.  If the line was the last in 
 *  the string, it returns NULL.
 */
static char *_get_line (char *dest, int max, const char *source)
{
    char *returnVal = dest;

    if (source && dest)  {
	while ((*source != '\n') && (*source != '\0') && (max > 0)) {
	    *dest = *source;
	    dest++;
	    source++;
	    max--;
	}

	*dest = '\0';

	if (*source == '\n')
	    return returnVal;
    }					/*  NULL is returned if line is	*/
					/*  too long or end of source	*/
    return NULL;			/*  or dest string is reached.	*/
}
	

/*  
 *  Finds the end of the first function in string containing arguments 
 *  to gnuplot's plot command.
 */
static char *_endFunction(char *aString)
{
    char delim[MAX_LINE_LEN];
    char *cur = aString;
    int number = 0;

    while (cur && *cur) {

	switch (*cur) {
	case '\'':
	case '"':
	    if (number && delim[number - 1] == *cur)
		number--;
	    else
		delim[number++] = *cur;
	    break;

	case '(':
	case ')':
	    if (number && delim[number - 1] == ((*cur == '(')? ')' : '('))
		number --;
	    else
		delim[number++] = *cur;
	    break;

	case ',':
	    if (!number)
		return cur;
	}

	cur++;
    }

    return NULL;
}


const char *makeOneQuoteType (char *aString)
{
    char	*c, quote = '\0';

    /*  
     *  Make sure at most one kind of quotation marks is used in the 
     *  string -- change all quotation marks to match the first one.
     */
    for (c = aString; c && *c; c++)
	if ((*c == '\'') || (*c == '"')) {
	    if (quote && (*c != quote))
		*c = quote;
	    else
		quote = *c;
	}

    return aString;
}
    


@implementation Status

+ lastplot
{
    return lastplot;
}


+ setHalvePlot:(BOOL)condition
{
    nextfe_halve = condition;
    return self;
}


- init
{
    int counter;

    [super init];
    zone = [self zone];

    report = YES;

    /* 
     *  Set all pointers to NULL or nil so -resetCurrent
     *  doesn't try to free them.
     */
    s_epsStream = NULL;

    text = s_title = s_plotargs = s_font = appendix =
	s_dummy_var[0] = s_dummy_var[1] = NULL;

    for (counter = 0 ; counter < 3 ; counter++)
	s_labelCoord[counter] = NULL;

    [self initTics];
    [self initContour];

    [self resetCurrent];

    return self;
}


- free
{
    int counter;

    if (s_epsStream)
	NXCloseMemory (s_epsStream, NX_FREEBUFFER);

    /*  
     *  NXZoneFree() doesn't bomb if you give it a NULL pointer, so we 
     *  don't bother to check the pointer.
     */
    NXZoneFree (zone, text);
    NXZoneFree (zone, appendix);
    NXZoneFree (zone, s_title);
    NXZoneFree (zone, s_font);
    NXZoneFree (zone, s_dummy_var[X_TAG]);
    NXZoneFree (zone, s_dummy_var[Y_TAG]);

    for (counter = 0 ; counter < 3 ; counter++)
	NXZoneFree (zone, s_labelCoord[counter]);

    NXZoneFree (zone, s_plotargs);

    if (functions)
	[[functions freeObjects] free];

    [self freeTics];
    [self freeContour];

    if (lastplot == self)
	lastplot = nil;

    return [super free];
}


- setReport:(BOOL)cond
{
    report = cond;
    return self;
}


- (BOOL)report
{
    return report;
}


- setDelegate: anObject
{
    if ([anObject respondsTo:@selector(settingsDidChange:)]) {
	delegate = anObject;
	return self;
    } else
	return nil;
}


- delegate
{
    return delegate;
}


- (NXStream *)stream
{
    return s_epsStream;
}



- (BOOL)canPlot
{
    int count = 0, index;
    int total = [functions count];

    /* Count the number of functions */
    index = total;
    while (index--)
	count += ([[functions objectAt:index] isDataFile]) ? 0 : 1;

    /* 
     *  We can plot iff
     * 
     * 		1) there is an appendix
     * 		
     * 			or
     * 			
     * 		2) there is at least one FunctionObject and
     * 		3) if the plot is parametric then
     * 			a) we have pairs of functions (two-dimensional),
     * 			b) or we have triplets of functions (3-dimens.)
     *
     */
    
    return ((appendix && *appendix) ||
	    (total && 
	     (s_parametric? (isThreeD? !(count % 3)
				     : (!(count % 2) && (total == count)))
			  :YES)));

}


- plot
{
    [self applyCurrent];
    lastplot = self;

    if (areSettingsEdited && !appendix)  {
	[self _buildPlotargs];
	[self _setText:s_plotargs];

	areSettingsEdited = NO;
    }

    if (appendix && *appendix)
	[self _setText:appendix];	/* Appendix overrides current plot */

    return [self _doText:text];
}



- saveToFile:(const char *)filename
{
    char	*copy, *line, *cur;
    id		returnVal;

    /* 
     *  Gnuplot sometimes drops characters off the end of long 
     *  pathnames given to the "save" command, a bug that seems to 
     *  have been introduced between 3.1 and 3.2.  We work around it 
     *  by cd'ing to the directory first and then saving the file.
     */
    copy = NXCopyStringBufferFromZone (filename, zone);

    if (cur = rindex (copy, '/')) {
	*(cur++) = '\0';
	line = NXZoneMalloc (zone, 15 + strlen (filename));
	sprintf (line, "cd '%s'\nsave '%s'\n", copy, cur);
    } else {
	line = NXZoneMalloc (zone, 9 + strlen (filename));
	sprintf (line, "save '%s'\n", filename);
    }

    returnVal = [self _doText:line]? self : nil;
    NXZoneFree (zone, copy);
    NXZoneFree (zone, line);

    return returnVal;
}



- reportSettingsChange:sender
{
    areSettingsEdited = YES;
    if (lastplot == self)
	lastplot = nil;		/* Last valid plot is no longer valid */

    if (delegate && report)
	[delegate settingsDidChange:self];

    return self;
}



/* 
 *  Resets all the instance variables to their default values.
 */
- resetCurrent
{
    int counter;

    if (functions)
	[functions freeObjects];
    else
	functions = [[List allocFromZone:zone] init];

    NXZoneFree (zone, text);
    text = NULL;

    NXZoneFree (zone, s_title);
    s_title = NULL;

    NXZoneFree (zone, s_dummy_var[0]);
    s_dummy_var[0] = NULL;

    NXZoneFree (zone, s_dummy_var[1]);
    s_dummy_var[1] = NULL;

    for (counter = 0 ; counter < 3 ; counter++)  {
	NXZoneFree (zone, s_labelCoord[counter]);
	s_labelCoord[counter] = NULL;
	s_minCoord[counter][CARTESIAN] = -10.0;
	s_maxCoord[counter][CARTESIAN] = 10.0;
	s_minCoord[counter][POLAR] = -10.0;
	s_isLogCoord[counter][CARTESIAN] = NO;
	s_isLogCoord[counter][POLAR] = NO;
    }

    degrees = NO;
					/* These not set in for loop above */
    s_minCoord[X_TAG][POLAR] = s_minCoord[R_TAG][POLAR] = 0.0;
    s_maxCoord[X_TAG][POLAR] = degrees? 360 :6.283186;
    s_maxCoord[Y_TAG][POLAR] = s_maxCoord[Z_TAG][POLAR]
	= s_maxCoord[R_TAG][POLAR] = 10.0;

    NXZoneFree (zone, s_plotargs);
    s_plotargs = NULL;

    NXZoneFree (zone, s_font);
    s_font = NULL;

    NXZoneFree (zone, appendix);
    appendix = NULL;

    s_fontsize = 16;

    isThreeD = NO;
    s_parametric = NO;
    s_polar = NO;
    s_border = YES;
    s_draw_surface = YES;

    [self resetCurrentContour];
    [self resetCurrentTics];

    s_hidden3d = NO;
    s_grid = NO;
    s_zeroaxis[X_TAG] = s_zeroaxis[Y_TAG] = YES;

    s_key = YES;		/* Plot has a key, in default location	*/
    s_key_default = YES;

    s_time = NO;		/* Don't show time/date			*/
    s_time_default = YES;

    for (counter = 0 ; counter < 4 ; counter++) {
	s_autoscaleCoord[counter][CARTESIAN] = 1;
	s_autoscaleCoord[counter][POLAR] = 1;
    }

    sizeProp = YES;	/* Keep x, y, z proportional */
    s_xsize = s_ysize = 1.0;
    s_samples = s_samplesCoord[X_TAG] = s_samplesCoord[Y_TAG] = 100;
    s_iso_samplesCoord[X_TAG] = s_iso_samplesCoord[Y_TAG] = 10;

    s_rotCoord[X_TAG] = 60.0;
    s_rotCoord[Z_TAG] = 30.0;

    areSettingsEdited = NO;

    return self;
}





/*  Sets the current gnuplot settings to the Status instance variables.  */
- applyCurrent
{
    int		type;

    nextfe_solid = NO;//

    if (s_font)
	strcpy (nextfe_font, s_font);
    else
	strcpy (nextfe_font, DEFAULT_FONT);

    nextfe_fontsize = s_fontsize;

    if (s_title)
	strcpy (title, s_title);
    else
	strcpy (title, "");

    if (s_dummy_var[0])
	strcpy (dummy_var[0], s_dummy_var[0]);
    else
	strcpy (dummy_var[0], "x");

    if (s_dummy_var[1])
	strcpy (dummy_var[1], s_dummy_var[1]);
    else
	strcpy (dummy_var[1], "y");

    if (s_labelCoord[X_TAG])
	strcpy (xlabel, s_labelCoord[X_TAG]);
    else
	strcpy (xlabel, "");

    if (s_labelCoord[Y_TAG])
	strcpy (ylabel, s_labelCoord[Y_TAG]);
    else
	strcpy (ylabel, "");

    if (s_labelCoord[Z_TAG])
	strcpy (zlabel, s_labelCoord[Z_TAG]);
    else
	strcpy (zlabel, "");

    parametric = s_parametric;
    polar = s_polar;
    draw_border = s_border;
    draw_surface = s_draw_surface;

    [self applyCurrentContour];
    [self applyCurrentTics];

    hidden3d = s_hidden3d;
    grid = s_grid;
    xzeroaxis = s_zeroaxis[X_TAG];
    yzeroaxis = s_zeroaxis[Y_TAG];

    /*  
     *  We have to sets of ranges, depending on whether the plot is 
     *  cartesian or polar.  Set the correct ones.
     */
    type = s_polar? POLAR:CARTESIAN;

    xmin = s_minCoord[X_TAG][type];
    xmax = s_maxCoord[X_TAG][type];
    ymin = s_minCoord[Y_TAG][type];
    ymax = s_maxCoord[Y_TAG][type];
    zmin = s_minCoord[Z_TAG][type];
    zmax = s_maxCoord[Z_TAG][type];
    is_log_x = s_isLogCoord[X_TAG][type];
    is_log_y = s_isLogCoord[Y_TAG][type];
    is_log_z = s_isLogCoord[Z_TAG][type];
    autoscale_x = s_autoscaleCoord[X_TAG][type];
    autoscale_y = s_autoscaleCoord[Y_TAG][type];
    autoscale_z = s_autoscaleCoord[Z_TAG][type];
    autoscale_r = s_autoscaleCoord[R_TAG][type];

    base_log_x = base_log_y = base_log_z = 10.0;
    log_base_log_x = log_base_log_y = log_base_log_z = log(10.0);

    angles_format = degrees? ANGLES_DEGREES :ANGLES_RADIANS;

    key = s_key? (s_key_default? -1 :1) :0;
    key_x = s_keyCoord[X_TAG];
    key_y = s_keyCoord[Y_TAG];
    key_z = s_keyCoord[Z_TAG];

    timedate = s_time? 1 :0;
    time_xoffset = (s_time_default? 0 :s_timeCoord[X_TAG]);
    time_yoffset = (s_time_default? 0 :s_timeCoord[Y_TAG]);

    xsize = s_xsize;
    ysize = s_ysize;

    surface_rot_x = s_rotCoord[X_TAG];
    surface_rot_z = s_rotCoord[Z_TAG];

    samples = s_samples;
    samples_1 = s_samplesCoord[X_TAG];
    samples_2 = s_samplesCoord[Y_TAG];
    iso_samples_1 = s_iso_samplesCoord[X_TAG];
    iso_samples_2 = s_iso_samplesCoord[Y_TAG];

    return self;
}



/* 
 *  Resets all the instance variables to the current gnuplot settings, 
 *  all of which pertain to the most recent plot:
 */
- grabCurrent
{
    char *cur;
    int type;

    NXZoneFree (zone, s_font);
    s_font = NXCopyStringBufferFromZone (nextfe_font, zone);

    s_fontsize = nextfe_fontsize;

    NXZoneFree (zone, s_title);
    s_title = NXCopyStringBufferFromZone (title, zone);

    NXZoneFree (zone, s_dummy_var[0]);
    s_dummy_var[0] = NXCopyStringBufferFromZone (dummy_var[0], zone);

    NXZoneFree (zone, s_dummy_var[1]);
    s_dummy_var[1] = NXCopyStringBufferFromZone (dummy_var[1], zone);

    NXZoneFree (zone, s_labelCoord[X_TAG]);
    s_labelCoord[X_TAG] = NXCopyStringBufferFromZone (xlabel, zone);

    NXZoneFree (zone, s_labelCoord[Y_TAG]);
    s_labelCoord[Y_TAG] = NXCopyStringBufferFromZone (ylabel, zone);

    NXZoneFree (zone, s_labelCoord[Z_TAG]);
    s_labelCoord[Z_TAG] = NXCopyStringBufferFromZone (zlabel, zone);

    /*  
     *  If the last plot command was "splot," as opposed to "plot,"  
     *  it is three dimensional.
     */
    isThreeD = (*(_removeLeadingBlanks(replot_line)) == 's');

    s_parametric = parametric;
    s_polar = polar;
    s_border = draw_border;
    s_draw_surface = draw_surface;

    [self grabCurrentContour];
    [self grabCurrentTics];

    s_hidden3d = hidden3d;
    s_grid = grid;
    s_zeroaxis[X_TAG] = xzeroaxis;
    s_zeroaxis[Y_TAG] = yzeroaxis;

    type = polar? POLAR:CARTESIAN;
    s_minCoord[X_TAG][type] = xmin;
    s_maxCoord[X_TAG][type] = xmax;
    s_minCoord[Y_TAG][type] = ymin;
    s_maxCoord[Y_TAG][type] = ymax;
    s_minCoord[Z_TAG][type] = zmin;
    s_maxCoord[Z_TAG][type] = zmax;
    s_isLogCoord[X_TAG][type] = is_log_x;
    s_isLogCoord[Y_TAG][type] = is_log_y;
    s_isLogCoord[Z_TAG][type] = is_log_z;
    s_autoscaleCoord[X_TAG][type] = autoscale_x;
    s_autoscaleCoord[Y_TAG][type] = autoscale_y;
    s_autoscaleCoord[Z_TAG][type] = autoscale_z;
    s_autoscaleCoord[R_TAG][type] = autoscale_r;

    degrees = (angles_format == ANGLES_DEGREES);

    s_key = ((key == -1) || (key == 1));
    s_key_default = (key == -1);
    s_keyCoord[X_TAG] = key_x;
    s_keyCoord[Y_TAG] = key_y;
    s_keyCoord[Z_TAG] = key_z;

    s_time = timedate;
    s_time_default = (time_xoffset == 0.0) && (time_yoffset == 0.0);
    s_timeCoord[X_TAG] = time_xoffset;
    s_timeCoord[Y_TAG] = time_yoffset;

    s_xsize = xsize;
    s_ysize = ysize;

    s_samples = samples;
    s_samplesCoord[X_TAG] = samples_1;
    s_samplesCoord[Y_TAG] = samples_2;
    s_iso_samplesCoord[X_TAG] = iso_samples_1;
    s_iso_samplesCoord[Y_TAG] = iso_samples_2;

    s_rotCoord[X_TAG] = surface_rot_x;
    s_rotCoord[Z_TAG] = surface_rot_z;

    NXZoneFree (zone, s_plotargs);
    if (cur = index (replot_line, ' ')) {	/* todo What about tabs? */
	s_plotargs = NXZoneMalloc (zone, strlen (replot_line) + 1);
	strcpy (s_plotargs, cur + 1);
    } else
	s_plotargs = NULL;

    /* Break plotargs into separate functions */
    [self _grabFunctionsFrom:s_plotargs isThreeD:isThreeD];
    [self _buildPlotargs];

    [self _setText: s_plotargs];
    areSettingsEdited = YES;

    return self;
}


- setThreeD:(BOOL) aCondition
{

    if (isThreeD != aCondition)  {
	int i;

	isThreeD = aCondition;

	for (i = [functions count] - 1; i >= 0; i--)
	    [[functions objectAt:i] setThreeD:isThreeD];

	if (s_parametric) {
	    if (isThreeD) {
		[self _copyString:"u" to:&(s_dummy_var[X_TAG])];
		[self _copyString:"v" to:&(s_dummy_var[Y_TAG])];
	    } else
		[self _copyString:"t" to:&(s_dummy_var[X_TAG])];
	}

	[self reportSettingsChange:self];
    }
    return self;
}


- (BOOL) isThreeD
{
    return isThreeD;
}



- setParametric:(BOOL) cond
{
    if (s_parametric != cond)  {

	s_parametric = cond;

	if (cond) {
	    if (isThreeD) {
		[self _copyString:"u" to:&(s_dummy_var[X_TAG])];
		[self _copyString:"v" to:&(s_dummy_var[Y_TAG])];
	    } else
		[self _copyString:"t" to:&(s_dummy_var[X_TAG])];
	} else {
	    [self _copyString:"x" to:&(s_dummy_var[X_TAG])];
	    [self _copyString:"y" to:&(s_dummy_var[Y_TAG])];
	}

	[self reportSettingsChange:self];

    }
    return self;
}


- (BOOL) parametric
{
    return s_parametric;
}



- setPolar:(BOOL) cond
{
    if (s_polar != cond) {
	s_polar = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) isPolar
{
    return s_polar;
}



- setBorder:(BOOL) cond
{
    if (s_border != cond) {
	s_border = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) border
{
    return s_border;
}



- setSurface:(BOOL) cond
{
    if (s_draw_surface != cond) {
	s_draw_surface = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) surface
{
    return s_draw_surface;
}



- setHiddenThreeD:(BOOL) cond
{
    if (s_hidden3d != cond) {
	s_hidden3d = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) hiddenThreeD
{
    return s_hidden3d;
}



- setGrid:(BOOL) cond
{
    if (s_grid != cond) {
	s_grid = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) grid
{
    return s_grid;
}



- setAxisCoord:(int)coord on:(BOOL)cond
{
    if (s_zeroaxis[coord] != cond) {
	s_zeroaxis[coord] = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL) axisCoord:(int)coord
{
    return s_zeroaxis[coord];
}



- setIsLogCoord:(int)coord isOn:(BOOL)isOn
{
    int type = s_polar? POLAR:CARTESIAN;

    if (s_isLogCoord[coord][type] != isOn) {
	s_isLogCoord[coord][type] = isOn;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) isLogCoord:(int)coord
{
    return s_isLogCoord[coord][s_polar? POLAR:CARTESIAN];
}



- setTitle:(const char *) aString
{
    [self _copyString:aString to:&s_title];
    makeOneQuoteType (s_title);
    [self reportSettingsChange:self];

    return self;
}


- (const char *) title
{
    return s_title;
}


- setLabelCoord:(int)coord to:(const char *) aString
{
    [self _copyString:aString to:&(s_labelCoord[coord])];
    makeOneQuoteType (s_labelCoord[coord]);
    [self reportSettingsChange:self];

    return self;
}



- (const char *) labelCoord:(int) coord
{
    return s_labelCoord[coord];
}


- setDummyVar:(int)coord to:(const char *)aString
{
    [self _copyString:aString to:&(s_dummy_var[coord])];
    [self reportSettingsChange:self];
    return self;
}


- (const char *)dummyVar:(int)coord
{
    if (!s_dummy_var[coord])
	switch (coord) {
	case X_TAG:
	    return "x";
	    break;
	case Y_TAG:
	    return "y";
	    break;
	}

    return s_dummy_var[coord];
}


- setAppendix: (const char *) aString
{
    NXZoneFree (zone, appendix);

    if (aString)
	appendix = NXCopyStringBufferFromZone (aString, zone);
    else
	appendix = NULL;

    return self;
}


- setFontsize: (int) anInt;
{
    if ((s_fontsize != anInt) && (anInt > 0)) {
	s_fontsize = anInt;
	[self reportSettingsChange:self];
	return self;
    } else
	return nil;
}


- (int) fontsize
{
    return s_fontsize;
}



- setFontname: (const char *) aString
{
    [self _copyString:aString to:&s_font];
    [self reportSettingsChange:self];

    return self;
}


- (const char *) fontname
{
    return s_font;
}



- setFont:aFont
{
    if (aFont)  {
	[self _copyString:[aFont name] to:&s_font];
	s_fontsize = [aFont pointSize];
	[self reportSettingsChange:self];
    }

    return self;
}



- font
{
    if (!s_font)
	return [Font newFont:DEFAULT_FONT size:s_fontsize];
    return [Font newFont:s_font size:s_fontsize];
}



- setKey:(BOOL) cond
{
    if (s_key != cond) {
	s_key = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) key
{
    return s_key;
}


- setKeyDefault:(BOOL) cond
{
    if (s_key_default != cond) {
	s_key_default = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) keyDefault
{
    return s_key_default;
}


- setKeyCoord:(int)coord to:(double)aDouble
{
    if (s_keyCoord[coord] != aDouble) {
	s_keyCoord[coord] = aDouble;
	[self reportSettingsChange:self];
    }
    return self;
}


- (double) keyCoord:(int)coord
{
    return s_keyCoord[coord];
}



- setTime:(BOOL) cond
{
    if (s_time != cond) {
	s_time = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) time
{
    return s_time;
}


- setTimeDefault:(BOOL) cond
{
    if (s_time_default != cond) {
	s_time_default = cond;
	[self reportSettingsChange:self];
    }

    return self;
}



- (BOOL) timeDefault
{
    return s_time_default;
}


- setTimeCoord:(int)coord to:(int)aDouble
{
    if (s_timeCoord[coord] != aDouble) {
	s_timeCoord[coord] = aDouble;
	[self reportSettingsChange:self];
    }
    return self;
}


- (int) timeCoord:(int)coord
{
    return s_timeCoord[coord];
}



- setSizeProp:(BOOL) cond
{
    sizeProp = cond;		/* Don't do anything, just record status */
    return self;
}


- (BOOL)sizeProp
{
    return sizeProp;
}


/*
 *  Size of zero means don't change... (e.g. if x=2 and y=0, we change 
 *  x but not y).
 */
- setSizeX:(float) xFloat Y:(float) yFloat
{
    if ((xFloat != s_xsize) || (yFloat != s_ysize)) {

	if (xFloat)
	    s_xsize = xFloat;

	if (yFloat)
	    s_ysize = yFloat;

	[self reportSettingsChange:self];
    }

    return self;
}


- (float) sizeX
{
    return s_xsize;
}


- (float) sizeY
{
    return s_ysize;
}



- setRotCoord:(int)coord to:(float)aFloat
{
    if (s_rotCoord[coord] != aFloat) {
	s_rotCoord[coord] = aFloat;
	[self reportSettingsChange:self];
    }
    return self;
}



- resetRotation
{
    s_rotCoord[X_TAG] = 60.0;
    s_rotCoord[Z_TAG] = 30.0;
    [self reportSettingsChange:self];
    return self;
}



- (float)rotCoord:(int)coord
{
    return s_rotCoord[coord];
}



- setSamplesCoord:(int)coord to:(int)anInt
{
    if ((anInt >= 2) && (anInt != s_samplesCoord[coord])) {
	s_samplesCoord[coord] = anInt;
	if (coord == X_TAG)
	    s_samples = anInt;
    } else
	return nil;

    [self reportSettingsChange:self];
    return self;
}


- (int) samples:(int)coord
{
    return s_samplesCoord[coord];
}



- setIsoSamplesCoord:(int)coord to:(int)anInt
{
    if ((anInt >= 2) && (anInt != s_iso_samplesCoord[coord]))
	s_iso_samplesCoord[coord] = anInt;
    else
	return nil;

    [self reportSettingsChange:self];
    return self;
}


- (int) isoSamples:(int)coord
{
    return s_iso_samplesCoord[coord];
}



- setAutoscaleCoord:(int)coord isOn:(BOOL)cond
{
    int type = s_polar? POLAR:CARTESIAN;

    if (s_autoscaleCoord[coord][type] != cond) {
	s_autoscaleCoord[coord][type] = cond;
	[self reportSettingsChange:self];
    }

    return self;
}


- (BOOL)autoscaleCoord:(int)coord
{
    return s_autoscaleCoord[coord][s_polar? POLAR:CARTESIAN];
}


- setRangeCoord:(int)coord min:(double)min max:(double)max
{
    int type = s_polar? POLAR:CARTESIAN;

    if ((s_maxCoord[coord][type] != max) || (s_minCoord[coord][type] != min)) {

	s_maxCoord[coord][type] = max;
	s_minCoord[coord][type] = min;

	[self reportSettingsChange:self];

    }

    return self;
}

- (double)minCoord:(int)coord
{
    return s_minCoord[coord][s_polar? POLAR:CARTESIAN];
}

- (double)maxCoord:(int)coord
{
    return s_maxCoord[coord][s_polar? POLAR:CARTESIAN];
}



- setDegrees:(BOOL)cond;
{
    if (degrees != cond) {
	degrees = cond;
	if (s_polar)
	    if (degrees)  {		/* Convert radians to degrees	*/
		s_maxCoord[X_TAG][POLAR] *= 57.2957795131;
		s_minCoord[X_TAG][POLAR] *= 57.2957795131;
	    } else {			/* Convert degrees to radians	*/
		s_maxCoord[X_TAG][POLAR] /= 57.2957795131;
		s_minCoord[X_TAG][POLAR] /= 57.2957795131;
	    }	

	[self reportSettingsChange:self];
    }
    return self;
}


- (BOOL)degrees
{
    return degrees;
}



- (List *)functions
{
    return functions;
}


// Shuts up the compiler about unused RCSId
- (const char *) rcsid
{
    return RCSId;
}


@end


@implementation Status (Private)


/*  
 *  We use a list of FunctionObjects to store each function (a 
 *  "function" is a mathematical expression that gnuplot will evaluate
 *  at certain points or a file containing data that gnuplot will 
 *  plot.)  We create these objects by parsing the plot arguments.
 */
- _grabFunctionsFrom: (char *)aString isThreeD:(BOOL)aCond;
{
    FunctionObject *aFunction;
    char *src;
    BOOL done;

    [functions freeObjects];

    if (!aString)
	return nil;

    /* Skip any range specifications */
    src = aString;
    while (isspace (*src))
	src++;
    while (*src == '[') {
	while (*src != ']')
	    src++;
	src++;
	while (isspace (*src))
	    src++;
    }

    /* Copy them to the strings list */

    done = NO;
    while (!done)  {
	char *end;

	if (end = _endFunction(src))
	    *end = '\0';
	else
	    done = YES;

	aFunction = [[FunctionObject allocFromZone:zone]
		 initFromString: _removeLeadingBlanks (src) isThreeD:aCond];
	[functions addObject:aFunction];

	if (!done) {
	    *end = ',';
	    src = ++end;
	}
    }

    return self;
}




/*
 *  We plot by assembling a Gnuplot "plot" command from the list of
 *  FunctionObjects.
 */

- _buildPlotargs
{
    int		index = [functions count];
    const char	*aString;
    FunctionObject	*function;
    int		size = 0, counter, anInt, anInt2;

    /*  
     *  First, we calculate the total size that will be required for 
     *  the entire plot command, including commas, etc.
     */
    if (counter = index)  {

	size = isThreeD? 6 : 5;				/* "splot" or "plot" */

	while (counter--) {
	    function = [functions objectAt:counter];

	    if (aString = [function stringValue])
		size += strlen (aString);

	    if ([function isDataFile])
		size += 2;				/* 2 quotation marks */

	    if (aString = [function title])
		size += (strlen (aString) + 9);		/* " title 'xxx'"    */

	    if (aString = [function styleString])
		size += (strlen (aString) + 12);	/* " with ??? xx xx" */

	    size += 2;					/* comma and space   */
	    size += 255;				/* "using" clause    */
	}

	/* 
	 *  No comma or space after the last functions	= -2
	 *  Terminating ASCII NULL			= +1
	 * 						----
	 * 				Total		= -1
	 */
	
	size--;
    }

    /*  Then we malloc enough space.  */

    NXZoneFree (zone, s_plotargs);
    s_plotargs = NXZoneMalloc (zone, size);


    /*  And finally, assemble the pieces.  */
    if (index) {
	if (isThreeD)
	    strcpy (s_plotargs, "splot ");
	else
	    strcpy (s_plotargs, "plot ");

	counter = 0;
	while (counter != index)  {
	    function = [functions objectAt:counter++];

	    if ([function isDataFile]) {
		char using[255];

		[self _buildUsing:using forFunction:function];
		sprintf (index (s_plotargs, '\0'), "'%s' %s",
			 [function stringValue], using);
	    } else
		s_plotargs = strcat (s_plotargs, [function stringValue]);

	    /* 
	     *  We determine which kind of quotation marks to use for 
	     *  the title by checking to see if the title itself 
	     *  contains any quotation marks and using the opposite 
	     *  kind.  Assumption:  that the title does not use both 
	     *  kinds of quotation marks, single and double.
	     */
	    if (aString = [function title]) {
		if (index (aString, '"'))
		    sprintf (index (s_plotargs, '\0'), " title '%s'", aString);
		else
		    sprintf(index(s_plotargs, '\0'), " title \"%s\"", aString);
	    }

	    if (aString = [function styleString])  {

		sprintf (index (s_plotargs, '\0'), " with %s", aString);

		/* 
		 *  The following logical gymnastics are required 
		 *  because the specific line and point styles may or 
		 *  may not have been specified by the user and may or 
		 *  may not be relevant.  Plus, Gnuplot requires that 
		 *  the line style be specified whenever the point 
		 *  style is specified, even if the function has no 
		 *  lines.  Basically, we specify what we must (based 
		 *  on the user's choices) and let the rest go to the 
		 *  default.
		 */
		
		switch ([function style]) {
		case FUNCTION_LINES:
		    if ((anInt = [function lineStyle]) != LINE_NOSTYLE)
			sprintf (index (s_plotargs, '\0'), " %d", anInt);
		    break;
		case FUNCTION_POINTS:
		    if ((anInt = [function pointsStyle]) != POINTS_NOSTYLE)
			sprintf (index (s_plotargs, '\0'), " 1 %d", anInt);
		    break;
		case FUNCTION_LINESPOINTS:
		    anInt2 = [function pointsStyle];
		    anInt = [function lineStyle];
		    if ((anInt != LINE_NOSTYLE) || 
			(anInt2 != POINTS_NOSTYLE)) {
			sprintf (index (s_plotargs, '\0'), " %d", anInt);
			if (anInt2 != POINTS_NOSTYLE)
			    sprintf (index (s_plotargs, '\0'), " %d", anInt2);
		    }
		    break;
		}

	    }
		
	    /* If there are more functions, we need a comma */
	    if (counter != index)
	        s_plotargs = strcat (s_plotargs, ", ");
	}
    }

    return self;
}


- _buildUsing:(char *)aString forFunction:(FunctionObject *)aFunction
{
    struct coldat *d = [aFunction columnData];

    *aString = '\0';

    if (!d->isOn)		/* No using clause */
	return self;

    if (isThreeD) {

	if (d->useX)
	    sprintf (aString, " using %d:%d:%d", d->x, d->y, d->z);
	else
	    sprintf (aString, " using %d", d->z);

    } else {

	if (!d->useX)
	    sprintf (aString, " using %d", d->y);

	else if (d->useX) {

	    sprintf (aString, "using %d:%d", d->x, d->y);
	    if (d->useYDelta)
		sprintf (aString, "%s:%d", aString, d->yDelta);
	    else if (d->useYLow) {
		sprintf (aString, "%s:%d:%d", aString, d->yLow, d->yHigh);

		if (d->useBoxWidth)
		    sprintf (aString, "%s:%d", aString, d->boxWidth);
	    } else if (d->useBoxWidth)
		sprintf (aString, "%s:%d:%d:%d",aString,d->y,d->y,d->boxWidth);

	}
    }

    return self;
}



- (const char *) _copyString:(const char *)source to:(char **)dest
{
    if (dest)  {
	NXZoneFree (zone, *dest);

	if (source)
	    *dest = NXCopyStringBufferFromZone (source, zone);
	else
	    *dest = NULL;
    }

    return *dest;
}


/*
 *  This method is nearly identical to load_file in misc.c, except 
 *  that instead of reading commands from a file, it reads from a 
 *  string, passing one line at a time to gnuplot.
 */
- _doText:(const char *)theText
{
    extern char input_line[];		/* Line to be read by do_line()	*/
    extern jmp_buf env;
    extern int do_line();
    extern int interactive;

    int len;				/* The length of a command	*/
    int start, max;			/* Indexes in input_line string	*/
    BOOL more;				/* Is there more of this line?	*/
    BOOL stop;				/* Are we done reading text?	*/
    int inline_num;			/* Text line number...		*/
    int inline_index;			/* ... which begins here	*/


    if (setjmp(env))  {			/*  Gnuplot bails out to here	*/
					/*  in case of parse error, etc.*/
	if (s_epsStream) {

	    NXCloseMemory (s_epsStream, NX_FREEBUFFER);
	    s_epsStream = NULL;

	}

	return nil;

    } else {

	interactive = 0;
	max = MAX_LINE_LEN;
	stop = NO;
	inline_num = inline_index = start = 0;

	if (s_epsStream)
	    NXCloseMemory (s_epsStream, NX_FREEBUFFER);
	s_epsStream = NXOpenMemory(NULL, 0, NX_READWRITE);

	EPSStream = s_epsStream;

	while (!stop) {              	/* Read every line in theText	*/

	    more = YES;			/* Read one command		*/
	    while (more) {
		if (!_get_line (&(input_line[start]), max,
				&theText[inline_index])) {
		    stop = YES;		/* End of string		*/
		    more = NO;
		}

		inline_num++;
		len = strlen (input_line);
		inline_index += (len + 1);
		if (len+1 >= max) {
		    fprintf (stderr, "Input line too long\n");
		    /* todo Bring up a modal panel here or something */
		}

		                        /* Continuation line		*/
		if (input_line[len - 1] == '\\') {
		    start = len - 1;
		    max -= start;
		} else
		    more = NO;
	    }

	    if (strlen (input_line) > 0) {
		do_line();			/* Parse input_line	*/
	    }

	}

	NEXTFE_reset();

	return self;
    }
}



- _setText: (const char *) aString
{
    NXZoneFree (zone, text);

    /* Magic number 100 -- todo be more accurate */
    text = NXZoneMalloc (zone, strlen (aString) + 130);
    sprintf (text, "set term _nextfe '%s' %d\nset output '/dev/null'\n%s",
	     s_font ? s_font : DEFAULT_FONT, s_fontsize, aString);

    return self;
}




@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.