home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / aa_Intel_Only / Gnuplot / GnuplotSource / FunctionObject.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  12.7 KB  |  630 lines

  1. /*
  2.  *  Copyright (C) 1993  Robert Davis
  3.  *
  4.  *  This program is free software; you can redistribute it and/or
  5.  *  modify it under the terms of Version 2, or any later version, of 
  6.  *  the GNU General Public License as published by the Free Software 
  7.  *  Foundation.
  8.  */
  9.  
  10. static char RCSId[]="$Id: FunctionObject.m,v 1.12 1993/05/24 03:59:44 davis Exp $";
  11.  
  12.  
  13. #import <streams/streams.h>
  14. #import <sys/stat.h>        /* stat()            */
  15. #import <sys/types.h>        /* stat()            */
  16. #import <objc/hashtable.h>    /* NXCopyStringBufferFromZone()    */
  17. #import <objc/zone.h>
  18. #import <ctype.h>        /* isspace()            */
  19. #import <libc.h>        /* index(), rindex()        */
  20.  
  21. #import "FunctionObject.h"
  22.  
  23.  
  24. const char *styleString[] = {"lines", "points", "linespoints", "dots",
  25.                  "impulses", "errorbars", "boxes", "boxerrorbars",
  26.                  "steps"};
  27.  
  28.  
  29. /*  Is the string blank (i.e. full of only white space)?  */
  30. static BOOL _isBlank (const char *aString)
  31. {
  32.     const char *cur;
  33.  
  34.     for (cur = aString ; *cur != '\0' ; cur++)
  35.     if (!isspace(*cur))
  36.         return NO;
  37.  
  38.     return YES;
  39. }
  40.  
  41.  
  42. static BOOL _isSpaceNotNL (char aChar)
  43. {
  44.     return (isspace (aChar)) && (aChar != '\n');
  45. }
  46.  
  47.  
  48.  
  49.  
  50. /*  
  51.  *  aString is assumed to be a one-line string (i.e it contains no 
  52.  *  newlines).  This could be changed easily if need be.
  53.  */
  54. static void _removeComments (char *aString)
  55. {
  56.     char *cur = aString;
  57.  
  58.     while (*cur) {
  59.     if ((*cur == '\'') || (*cur == '"')) {
  60.  
  61.         /* 
  62.          *  Skip the quotation.  Note that gnuplot does not 
  63.          *  support nested quotes.
  64.          */
  65.         
  66.         char c = *(cur++);
  67.         while (*cur && (*cur != c))
  68.         cur++;
  69.  
  70.     } else if (*cur == '#') {
  71.  
  72.         /*  
  73.          *  If we've encountered a pound sign outside of a 
  74.          *  quotation, we've found a comment.  The rest of the 
  75.          *  line can be truncated.
  76.          */
  77.         cur = '\0';
  78.  
  79.     }
  80.  
  81.     cur++;
  82.     }
  83.  
  84. }
  85.  
  86.  
  87. static void _getWith (char *aString, int *style, int *lineStyle,
  88.               int *pointsStyle)
  89. {
  90.     char *cur, *with;
  91.  
  92.     /* 
  93.      *  This attempts to detect a 'with' phrase and to extract the 
  94.      *  style from it if it exists.  There are several ways to trick 
  95.      *  this. 
  96.      */
  97.     if ((with = rindex (aString, 'w')) && (*(with+1) == 'i') &&
  98.     (cur = index (with, ' '))) {
  99.     switch (*(++cur)) {
  100.     case 'p':  *style = FUNCTION_POINTS;    break;
  101.     case 'd':  *style = FUNCTION_DOTS;    break;
  102.     case 'i':  *style = FUNCTION_IMPULSES;    break;
  103.     case 'e':  *style = FUNCTION_ERRORBARS;    break;
  104.     case 'b':
  105.         if (index (cur, 'r')) *style = FUNCTION_BOXERRORBARS;
  106.         else *style = FUNCTION_BOXES;
  107.         break;
  108.     case 'l':
  109.         if (index (cur, 'p')) *style = FUNCTION_LINESPOINTS;
  110.         else *style = FUNCTION_LINES;
  111.         break;
  112.     case 's':  *style = FUNCTION_STEPS;    break;
  113.     default:   *style = FUNCTION_NOSTYLE;    break;
  114.     }
  115.     *(with-1) = '\0';    /* Assume with is not first in aString */
  116.  
  117.     /* 
  118.      *  If the style type includes lines or points, the user has 
  119.      *  the option of setting the point/line styles.  Check for 
  120.      *  those...
  121.      */
  122.                         /* Line Style */
  123.     if ((*style == FUNCTION_POINTS) || (*style == FUNCTION_LINES) ||
  124.         (*style == FUNCTION_LINESPOINTS)) {
  125.  
  126.         while (cur && *cur && ((*cur > '6') || (*cur < '1')))
  127.         cur++;
  128.         if (cur && *cur)
  129.         *lineStyle = *cur - '0';
  130.  
  131.                         /* Point Style */
  132.         if ((*style == FUNCTION_POINTS) ||
  133.         (*style == FUNCTION_LINESPOINTS))  {
  134.  
  135.         while (cur && *cur && ((*cur > '6') || (*cur < '1')))
  136.             cur++;
  137.         if (cur && *cur)
  138.             *pointsStyle = *cur - '0';
  139.         }
  140.  
  141.     }
  142.  
  143.     }
  144.  
  145. }
  146.  
  147.  
  148.  
  149.  
  150. static char *_getTitle (char *aString)
  151. {
  152.     while (aString && *aString) {
  153.  
  154.     /*
  155.      *  If this appears to be a "title" phrase, see if a quoted
  156.      *  string follows.  If one does, chop it off and return it.
  157.      */
  158.     
  159.     if ((*aString == 't') &&
  160.         ((*(aString + 1) == 'i') || _isSpaceNotNL (*(aString + 1)))) {
  161.  
  162.         char *firstQuote, *beginning = aString;
  163.  
  164.         while (!_isSpaceNotNL (*aString))
  165.         aString++;
  166.         while (_isSpaceNotNL (*aString))
  167.         aString++;
  168.         if ((*aString == '\'') || (*aString == '"')) {
  169.  
  170.         /*  
  171.          *  Okay, we are as sure as can be that this is a 
  172.          *  "title" phrase.
  173.          */
  174.         firstQuote = aString;
  175.         if (aString = index (++aString, *firstQuote))
  176.             *aString = '\0';
  177.         *(beginning-1) = '\0';    /* Assume title isn't first in aString */
  178.  
  179.         return firstQuote+1;
  180.         }
  181.  
  182.     }
  183.  
  184.     aString++;
  185.     }
  186.  
  187.     return NULL;
  188. }
  189.  
  190.  
  191. /* aString assumed to contain no newlines */
  192. static void _getUsing (char *aString, int *nums, BOOL threeD)
  193. {
  194.     while (aString && *aString) {
  195.  
  196.  
  197.     /*  Look for a using clause  */
  198.     
  199.     if ((*aString == 'u') && (*(aString + 1)) &&
  200.         ((*(aString + 1) == 's') || isspace (*(aString + 1)))) {
  201.  
  202.         int        i = 0;
  203.  
  204.         *aString = '\0';
  205.         /* skip "using" and space */
  206.         while (*(++aString) && !isspace (*aString))
  207.         ;
  208.         while (*(++aString) && isspace (*aString))
  209.         ;
  210.  
  211.         while (*aString) {
  212.         if (isdigit (*aString))
  213.             nums[i] = (nums[i] * 10) + (*aString - '0');
  214.         else if (nums[i] == '0') {
  215.             /* Stop reading -- problems */
  216.             *(aString+1) = '\0';
  217.             i--;
  218.         } else if (*aString == ':') {
  219.             if (i++ > 4) {
  220.             /* Read all the numbers -- ignore anything else */
  221.             *(aString+1) = '\0';
  222.             i--;
  223.             }
  224.         }
  225.  
  226.         aString++;
  227.         }
  228.  
  229.         return;
  230.  
  231.     } else if (*aString == '/') {
  232.         while (*(++aString) && !isspace(*aString))
  233.         ;
  234.     } else if ((*aString == '"') || (*aString == '\'')) {
  235.         char quote = *aString;
  236.         while (*(++aString) && (*aString != quote))
  237.         ;
  238.         aString++;
  239.     } else
  240.         aString++;
  241.     }
  242.  
  243.     return;
  244. }
  245.  
  246.  
  247. static BOOL _isNumberElement (char c)
  248. {
  249.     return isdigit(c) || (c == '.') || (c == 'E') || (c == '+') || (c == '-');
  250. }
  251.  
  252. static int _getNumColumns (const char *path)
  253. {
  254.     NXStream    *s = NXMapFile (path, NX_READONLY);
  255.     int        count = 0;
  256.  
  257.     if (s) {
  258.     int        c, oldc = '\0';
  259.  
  260.     c = NXGetc(s);
  261.     while (c != EOF)  {
  262.         if (c == '#') {
  263.         if (count == 0) {
  264.             while (((c = NXGetc(s)) != EOF) && (c != '\n'))
  265.             ;
  266.             count = 0;
  267.             oldc = c;
  268.             if (c != EOF)
  269.             c = NXGetc(s);
  270.         } else
  271.             c = EOF;
  272.         } else if (_isNumberElement(c) && !_isNumberElement(oldc)) {
  273.         count++;
  274.         oldc = c;
  275.         c = NXGetc(s);
  276.         } else if (c == '\n') {
  277.         if (count > 0)
  278.             c = EOF;
  279.         else
  280.             c = NXGetc(s);
  281.         } else {
  282.         oldc = c;
  283.         c = NXGetc(s);
  284.         }
  285.  
  286.     }
  287.  
  288.     NXCloseMemory (s, NX_FREEBUFFER);
  289.     }
  290.  
  291.     return count;
  292. }
  293.  
  294.  
  295.  
  296. @interface FunctionObject (Private)
  297. - _initColumnDataUsing:(int *)usingData;
  298. @end
  299.  
  300.  
  301. @implementation FunctionObject
  302.  
  303.  
  304. /*  Does the string contain something besides white space?  */
  305. + (BOOL) isAcceptableStringValue:(const char *)aString
  306. {
  307.     return (aString && !_isBlank (aString));
  308.     /* todo, also check for valid functions, if possible */
  309. }
  310.     
  311.  
  312. /*  
  313.  *  Makes sure the file whose full path is specified by aString is a 
  314.  *  regular, readable file.
  315.  */
  316. + (BOOL) isAcceptableDataFile:(const char *)aString
  317. {
  318.     struct stat fileinfo;
  319.     BOOL returnVal;
  320.  
  321.     returnVal = (aString && !stat (aString, &fileinfo) &&
  322.          ((fileinfo.st_mode & S_IFMT) == S_IFREG) &&
  323.          (fileinfo.st_mode & S_IREAD));
  324.     return returnVal;
  325. }
  326.  
  327.  
  328.  
  329. /*  
  330.  *  The attributes of a function are stored by an instance of 
  331.  *  FunctionObject.  The only complicated part is parsing those 
  332.  *  attributes from a Gnuplot "plot" statement.  aString should 
  333.  *  specify only the part of that statement pertaining to the function 
  334.  *  that will be stored by this instance and should contain no leading 
  335.  *  white space.  (This method could be nicer by ignoring leading 
  336.  *  white space.)
  337.  */
  338. - initFromString:(const char *)aString isThreeD:(BOOL)aCond
  339. {
  340.     char    quote, *cur;
  341.     NXZone    *zone;
  342.     int        theUsing[5] = {0,0,0,0,0};
  343.  
  344.     [super init];
  345.  
  346.     zone = [self zone];
  347.  
  348.     title = NULL;            /* Defaults */
  349.     style = FUNCTION_NOSTYLE;
  350.     pointsStyle = POINTS_NOSTYLE;
  351.     lineStyle = LINE_NOSTYLE;
  352.     isDataFile = NO;
  353.     isThreeD = aCond;
  354.  
  355.     if (aString)  {
  356.  
  357.     stringValue = NXCopyStringBufferFromZone (aString, zone);
  358.     _removeComments (stringValue);
  359.     _getWith (stringValue, &style, &lineStyle, &pointsStyle);
  360.     [self setTitle:_getTitle (stringValue)];
  361.  
  362.     /* 
  363.      *  If this string value is quoted or if it begins with a 
  364.      *  forward slash, it is a data file.  (This forward slash 
  365.      *  thing is an feature not present in the original gnuplot, 
  366.      *  but it's very nice in the NeXTSTEP interface.)  We remove
  367.      *  the quotes and set the isDataFile flag.  The string is 
  368.      *  assumed to have no leading white space, i.e. if it is a 
  369.      *  data file, the first character is a quote.
  370.      */
  371.  
  372.     quote = *stringValue;
  373.  
  374.     /*  Check to see if this is a data file  */
  375.     if ((quote == '/') || (quote == '\'') || (quote == '"')) { /* It is */
  376.  
  377.         isDataFile = YES;
  378.         _getUsing (stringValue, theUsing, isThreeD);
  379.  
  380.         if (quote == '/') {
  381.         int len;
  382.         cur = NXCopyStringBufferFromZone (stringValue, zone);
  383.         len = strlen (cur);    /* Remove trailing space */
  384.         while (isspace(cur[--len]))
  385.             cur[len] = '\0';
  386.         } else {
  387.         *rindex (stringValue, quote) = '\0';
  388.         cur = NXCopyStringBufferFromZone (stringValue+1, zone);
  389.         }
  390.  
  391.         NXZoneFree (zone, stringValue);
  392.         stringValue = cur;
  393.  
  394.         [self _initColumnDataUsing:theUsing];
  395.  
  396.     } else {        /* This is a function, not a data file. */
  397.  
  398.         /* Remove trailing blanks */
  399.         cur = stringValue + strlen (stringValue) - 1 ;
  400.         while (isspace (*cur) && (cur >= stringValue))
  401.         *(cur--) = '\0';
  402.  
  403.     }
  404.  
  405.     } else
  406.     stringValue = NULL;
  407.       
  408.   return self;
  409. }
  410.  
  411.  
  412. - free
  413. {
  414.     NXZoneFree ([self zone], title);
  415.     return [super free];
  416. }
  417.  
  418.  
  419.  
  420. - setThreeD:(BOOL)cond
  421. {
  422.     if (cond != isThreeD) {
  423.     isThreeD = cond;
  424.  
  425.     if (isDataFile) {
  426.         struct coldat temp = columnData;
  427.         columnData = columnDataOther;
  428.         columnDataOther = temp;
  429.     }
  430.     }
  431.  
  432.     return self;
  433. }
  434.  
  435.  
  436. - (BOOL)isDataFile
  437. {
  438.     return isDataFile;
  439. }
  440.  
  441.  
  442. - (struct coldat *)columnData
  443. {
  444.     return isDataFile? &columnData :NULL;
  445. }
  446.  
  447.  
  448. - setTitle:(const char *)aString
  449. {
  450.     NXZone    *zone = [self zone];
  451.  
  452.     NXZoneFree (zone, title);
  453.     if (aString) {
  454.     char    *c, quote = '\0';
  455.  
  456.     title = NXCopyStringBufferFromZone (aString, zone);
  457.  
  458.     /* 
  459.      *  Make sure aString doesn't mix both kinds of quotation marks.  
  460.      *  Gnuplot can handle one or the other but not both in one title. 
  461.      *  (If there's a mix, use the kind used first.)
  462.      */
  463.     
  464.     for (c = title; c && *c; c++)
  465.         if ((*c == '\'') || (*c == '"')) {
  466.         if (quote && (*c != quote))
  467.             *c = quote;
  468.         else
  469.             quote = *c;
  470.         }
  471.     
  472.     } else
  473.     title = NULL;
  474.  
  475.     return self;
  476. }
  477.  
  478.  
  479. - (const char *)title
  480. {
  481.     return title;
  482. }
  483.  
  484.  
  485. - setStyle:(int) anInt
  486. {
  487.     style = anInt;
  488.     return self;
  489. }
  490.  
  491.  
  492. - (int)style
  493. {
  494.     return style;
  495. }
  496.  
  497.  
  498. - (const char *)styleString
  499. {
  500.     if (style == FUNCTION_NOSTYLE)
  501.     return NULL;
  502.     else
  503.     return styleString[style];
  504. }
  505.  
  506.  
  507. - setPointsStyle: (int)anInt
  508. {
  509.     pointsStyle = anInt;
  510.     return self;
  511. }
  512.  
  513.  
  514. - (int)pointsStyle
  515. {
  516.     return pointsStyle;
  517. }
  518.  
  519.  
  520. - setLineStyle: (int)anInt
  521. {
  522.     lineStyle = anInt;
  523.     return self;
  524. }
  525.  
  526.  
  527. - (int)lineStyle
  528. {
  529.     return lineStyle;
  530. }
  531.  
  532.  
  533.  
  534. /*  
  535.  *  If the function is actually a data file, it is similar to an 
  536.  *  attachment, or a "file link," so we return YES.  If the function 
  537.  *  is not a data file, we return NO.
  538.  */
  539. - (BOOL)isAttachment
  540. {
  541.     return isDataFile;
  542. }
  543.  
  544.  
  545. // Shuts up the compiler about unused RCSId
  546. - (const char *) rcsid
  547. {
  548.     return RCSId;
  549. }
  550.  
  551.  
  552. @end
  553.  
  554.  
  555. @implementation FunctionObject (Private)
  556.  
  557. - _initColumnDataUsing:(int *)usingData
  558. {
  559.     struct coldat    *two = isThreeD? &columnDataOther : &columnData;
  560.     struct coldat    *three = isThreeD? &columnData : &columnDataOther;
  561.     int        numUsing;
  562.     int        x;
  563.  
  564.     /*  
  565.      *  Initialize the column data structures based on the number of 
  566.      *  columns in the data file, but also consider the usingData.
  567.      */
  568.     
  569.     two->number = three->number = _getNumColumns (stringValue);
  570.  
  571.     for (numUsing = 0; usingData[numUsing]; numUsing++)
  572.     ;
  573.  
  574.     two->isOn = three->isOn = numUsing;
  575.     x = (numUsing)? numUsing : two->number;
  576.  
  577.     if (x >= 1) {
  578.     two->useX = two->useY = three->useZ = YES;
  579.     two->x = two->y = three->useZ = 1;
  580.     if (x >= 2) {
  581.         two->y = 2;
  582.         if (x >= 3) {
  583.         two->useYDelta = three->useX = three->useY = YES;
  584.         two->yDelta = three->z = 3;
  585.         three->y = 2;
  586.         three->x = 1;
  587.         if (x >= 4) {
  588.             two->useYLow = two->useYHigh = YES;
  589.             two->useYDelta = NO;
  590.             two->yLow = 3;
  591.             two->yHigh = 4;
  592.             if (x >= 5)
  593.             two->useBoxWidth = YES;
  594.             two->boxWidth = 5;
  595.         }
  596.         }
  597.     }
  598.     }
  599.  
  600.     if (numUsing) {
  601.     two->x = three->x = (numUsing > 1)? usingData[0] : 1;
  602.     two->y = (numUsing > 1)? usingData[1] : usingData[0];
  603.     three->y = usingData[1];
  604.     two->yDelta = two->yLow = three->z = usingData[2];
  605.     two->yHigh = usingData[3];
  606.     two->boxWidth = usingData[4];
  607.     }
  608.  
  609.     three->useYDelta = NO;    /* Can't use these in 3D */
  610.     three->useYLow = NO;
  611.     three->useYHigh = NO;
  612.     three->useZ = YES;
  613.     three->useBoxWidth = NO;
  614.  
  615.     two->useZ = NO;        /* Can't use this in 2D */
  616.  
  617.     switch (two->number) {    /* Note:  this falls through */
  618.     case 0:    two->useY = three->useZ = NO;
  619.     case 1:    two->useX = NO;
  620.     case 2:    two->useYDelta = three->useX = three->useY = NO;
  621.     case 3:    two->useYLow = two->useYHigh = NO;
  622.     case 4:    two->useBoxWidth = NO;
  623.     }
  624.  
  625.     return self;
  626. }
  627.  
  628.  
  629. @end
  630.