This is FunctionObject.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: FunctionObject.m,v 1.12 1993/05/24 03:59:44 davis Exp $"; #import <streams/streams.h> #import <sys/stat.h> /* stat() */ #import <sys/types.h> /* stat() */ #import <objc/hashtable.h> /* NXCopyStringBufferFromZone() */ #import <objc/zone.h> #import <ctype.h> /* isspace() */ #import <libc.h> /* index(), rindex() */ #import "FunctionObject.h" const char *styleString[] = {"lines", "points", "linespoints", "dots", "impulses", "errorbars", "boxes", "boxerrorbars", "steps"}; /* Is the string blank (i.e. full of only white space)? */ static BOOL _isBlank (const char *aString) { const char *cur; for (cur = aString ; *cur != '\0' ; cur++) if (!isspace(*cur)) return NO; return YES; } static BOOL _isSpaceNotNL (char aChar) { return (isspace (aChar)) && (aChar != '\n'); } /* * aString is assumed to be a one-line string (i.e it contains no * newlines). This could be changed easily if need be. */ static void _removeComments (char *aString) { char *cur = aString; while (*cur) { if ((*cur == '\'') || (*cur == '"')) { /* * Skip the quotation. Note that gnuplot does not * support nested quotes. */ char c = *(cur++); while (*cur && (*cur != c)) cur++; } else if (*cur == '#') { /* * If we've encountered a pound sign outside of a * quotation, we've found a comment. The rest of the * line can be truncated. */ cur = '\0'; } cur++; } } static void _getWith (char *aString, int *style, int *lineStyle, int *pointsStyle) { char *cur, *with; /* * This attempts to detect a 'with' phrase and to extract the * style from it if it exists. There are several ways to trick * this. */ if ((with = rindex (aString, 'w')) && (*(with+1) == 'i') && (cur = index (with, ' '))) { switch (*(++cur)) { case 'p': *style = FUNCTION_POINTS; break; case 'd': *style = FUNCTION_DOTS; break; case 'i': *style = FUNCTION_IMPULSES; break; case 'e': *style = FUNCTION_ERRORBARS; break; case 'b': if (index (cur, 'r')) *style = FUNCTION_BOXERRORBARS; else *style = FUNCTION_BOXES; break; case 'l': if (index (cur, 'p')) *style = FUNCTION_LINESPOINTS; else *style = FUNCTION_LINES; break; case 's': *style = FUNCTION_STEPS; break; default: *style = FUNCTION_NOSTYLE; break; } *(with-1) = '\0'; /* Assume with is not first in aString */ /* * If the style type includes lines or points, the user has * the option of setting the point/line styles. Check for * those... */ /* Line Style */ if ((*style == FUNCTION_POINTS) || (*style == FUNCTION_LINES) || (*style == FUNCTION_LINESPOINTS)) { while (cur && *cur && ((*cur > '6') || (*cur < '1'))) cur++; if (cur && *cur) *lineStyle = *cur - '0'; /* Point Style */ if ((*style == FUNCTION_POINTS) || (*style == FUNCTION_LINESPOINTS)) { while (cur && *cur && ((*cur > '6') || (*cur < '1'))) cur++; if (cur && *cur) *pointsStyle = *cur - '0'; } } } } static char *_getTitle (char *aString) { while (aString && *aString) { /* * If this appears to be a "title" phrase, see if a quoted * string follows. If one does, chop it off and return it. */ if ((*aString == 't') && ((*(aString + 1) == 'i') || _isSpaceNotNL (*(aString + 1)))) { char *firstQuote, *beginning = aString; while (!_isSpaceNotNL (*aString)) aString++; while (_isSpaceNotNL (*aString)) aString++; if ((*aString == '\'') || (*aString == '"')) { /* * Okay, we are as sure as can be that this is a * "title" phrase. */ firstQuote = aString; if (aString = index (++aString, *firstQuote)) *aString = '\0'; *(beginning-1) = '\0'; /* Assume title isn't first in aString */ return firstQuote+1; } } aString++; } return NULL; } /* aString assumed to contain no newlines */ static void _getUsing (char *aString, int *nums, BOOL threeD) { while (aString && *aString) { /* Look for a using clause */ if ((*aString == 'u') && (*(aString + 1)) && ((*(aString + 1) == 's') || isspace (*(aString + 1)))) { int i = 0; *aString = '\0'; /* skip "using" and space */ while (*(++aString) && !isspace (*aString)) ; while (*(++aString) && isspace (*aString)) ; while (*aString) { if (isdigit (*aString)) nums[i] = (nums[i] * 10) + (*aString - '0'); else if (nums[i] == '0') { /* Stop reading -- problems */ *(aString+1) = '\0'; i--; } else if (*aString == ':') { if (i++ > 4) { /* Read all the numbers -- ignore anything else */ *(aString+1) = '\0'; i--; } } aString++; } return; } else if (*aString == '/') { while (*(++aString) && !isspace(*aString)) ; } else if ((*aString == '"') || (*aString == '\'')) { char quote = *aString; while (*(++aString) && (*aString != quote)) ; aString++; } else aString++; } return; } static BOOL _isNumberElement (char c) { return isdigit(c) || (c == '.') || (c == 'E') || (c == '+') || (c == '-'); } static int _getNumColumns (const char *path) { NXStream *s = NXMapFile (path, NX_READONLY); int count = 0; if (s) { int c, oldc = '\0'; c = NXGetc(s); while (c != EOF) { if (c == '#') { if (count == 0) { while (((c = NXGetc(s)) != EOF) && (c != '\n')) ; count = 0; oldc = c; if (c != EOF) c = NXGetc(s); } else c = EOF; } else if (_isNumberElement(c) && !_isNumberElement(oldc)) { count++; oldc = c; c = NXGetc(s); } else if (c == '\n') { if (count > 0) c = EOF; else c = NXGetc(s); } else { oldc = c; c = NXGetc(s); } } NXCloseMemory (s, NX_FREEBUFFER); } return count; } @interface FunctionObject (Private) - _initColumnDataUsing:(int *)usingData; @end @implementation FunctionObject /* Does the string contain something besides white space? */ + (BOOL) isAcceptableStringValue:(const char *)aString { return (aString && !_isBlank (aString)); /* todo, also check for valid functions, if possible */ } /* * Makes sure the file whose full path is specified by aString is a * regular, readable file. */ + (BOOL) isAcceptableDataFile:(const char *)aString { struct stat fileinfo; BOOL returnVal; returnVal = (aString && !stat (aString, &fileinfo) && ((fileinfo.st_mode & S_IFMT) == S_IFREG) && (fileinfo.st_mode & S_IREAD)); return returnVal; } /* * The attributes of a function are stored by an instance of * FunctionObject. The only complicated part is parsing those * attributes from a Gnuplot "plot" statement. aString should * specify only the part of that statement pertaining to the function * that will be stored by this instance and should contain no leading * white space. (This method could be nicer by ignoring leading * white space.) */ - initFromString:(const char *)aString isThreeD:(BOOL)aCond { char quote, *cur; NXZone *zone; int theUsing[5] = {0,0,0,0,0}; [super init]; zone = [self zone]; title = NULL; /* Defaults */ style = FUNCTION_NOSTYLE; pointsStyle = POINTS_NOSTYLE; lineStyle = LINE_NOSTYLE; isDataFile = NO; isThreeD = aCond; if (aString) { stringValue = NXCopyStringBufferFromZone (aString, zone); _removeComments (stringValue); _getWith (stringValue, &style, &lineStyle, &pointsStyle); [self setTitle:_getTitle (stringValue)]; /* * If this string value is quoted or if it begins with a * forward slash, it is a data file. (This forward slash * thing is an feature not present in the original gnuplot, * but it's very nice in the NeXTSTEP interface.) We remove * the quotes and set the isDataFile flag. The string is * assumed to have no leading white space, i.e. if it is a * data file, the first character is a quote. */ quote = *stringValue; /* Check to see if this is a data file */ if ((quote == '/') || (quote == '\'') || (quote == '"')) { /* It is */ isDataFile = YES; _getUsing (stringValue, theUsing, isThreeD); if (quote == '/') { int len; cur = NXCopyStringBufferFromZone (stringValue, zone); len = strlen (cur); /* Remove trailing space */ while (isspace(cur[--len])) cur[len] = '\0'; } else { *rindex (stringValue, quote) = '\0'; cur = NXCopyStringBufferFromZone (stringValue+1, zone); } NXZoneFree (zone, stringValue); stringValue = cur; [self _initColumnDataUsing:theUsing]; } else { /* This is a function, not a data file. */ /* Remove trailing blanks */ cur = stringValue + strlen (stringValue) - 1 ; while (isspace (*cur) && (cur >= stringValue)) *(cur--) = '\0'; } } else stringValue = NULL; return self; } - free { NXZoneFree ([self zone], title); return [super free]; } - setThreeD:(BOOL)cond { if (cond != isThreeD) { isThreeD = cond; if (isDataFile) { struct coldat temp = columnData; columnData = columnDataOther; columnDataOther = temp; } } return self; } - (BOOL)isDataFile { return isDataFile; } - (struct coldat *)columnData { return isDataFile? &columnData :NULL; } - setTitle:(const char *)aString { NXZone *zone = [self zone]; NXZoneFree (zone, title); if (aString) { char *c, quote = '\0'; title = NXCopyStringBufferFromZone (aString, zone); /* * Make sure aString doesn't mix both kinds of quotation marks. * Gnuplot can handle one or the other but not both in one title. * (If there's a mix, use the kind used first.) */ for (c = title; c && *c; c++) if ((*c == '\'') || (*c == '"')) { if (quote && (*c != quote)) *c = quote; else quote = *c; } } else title = NULL; return self; } - (const char *)title { return title; } - setStyle:(int) anInt { style = anInt; return self; } - (int)style { return style; } - (const char *)styleString { if (style == FUNCTION_NOSTYLE) return NULL; else return styleString[style]; } - setPointsStyle: (int)anInt { pointsStyle = anInt; return self; } - (int)pointsStyle { return pointsStyle; } - setLineStyle: (int)anInt { lineStyle = anInt; return self; } - (int)lineStyle { return lineStyle; } /* * If the function is actually a data file, it is similar to an * attachment, or a "file link," so we return YES. If the function * is not a data file, we return NO. */ - (BOOL)isAttachment { return isDataFile; } // Shuts up the compiler about unused RCSId - (const char *) rcsid { return RCSId; } @end @implementation FunctionObject (Private) - _initColumnDataUsing:(int *)usingData { struct coldat *two = isThreeD? &columnDataOther : &columnData; struct coldat *three = isThreeD? &columnData : &columnDataOther; int numUsing; int x; /* * Initialize the column data structures based on the number of * columns in the data file, but also consider the usingData. */ two->number = three->number = _getNumColumns (stringValue); for (numUsing = 0; usingData[numUsing]; numUsing++) ; two->isOn = three->isOn = numUsing; x = (numUsing)? numUsing : two->number; if (x >= 1) { two->useX = two->useY = three->useZ = YES; two->x = two->y = three->useZ = 1; if (x >= 2) { two->y = 2; if (x >= 3) { two->useYDelta = three->useX = three->useY = YES; two->yDelta = three->z = 3; three->y = 2; three->x = 1; if (x >= 4) { two->useYLow = two->useYHigh = YES; two->useYDelta = NO; two->yLow = 3; two->yHigh = 4; if (x >= 5) two->useBoxWidth = YES; two->boxWidth = 5; } } } } if (numUsing) { two->x = three->x = (numUsing > 1)? usingData[0] : 1; two->y = (numUsing > 1)? usingData[1] : usingData[0]; three->y = usingData[1]; two->yDelta = two->yLow = three->z = usingData[2]; two->yHigh = usingData[3]; two->boxWidth = usingData[4]; } three->useYDelta = NO; /* Can't use these in 3D */ three->useYLow = NO; three->useYHigh = NO; three->useZ = YES; three->useBoxWidth = NO; two->useZ = NO; /* Can't use this in 2D */ switch (two->number) { /* Note: this falls through */ case 0: two->useY = three->useZ = NO; case 1: two->useX = NO; case 2: two->useYDelta = three->useX = three->useY = NO; case 3: two->useYLow = two->useYHigh = NO; case 4: two->useBoxWidth = NO; } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.