/* gradu.c - Graphic Dump */
/* Chris X. Edwards */
/* $Revision: 1.19 $ $Date: 2003/02/15 01:04:41 $ */
/* cxe- This simple program basically gives a unix user access to the
 * functionality of the GNU libplot library. The existing
 * programs that are packaged with this library seem to dwell too much
 * on "graphs" as found in high school math. This program is designed
 * to simply take vector data and put it somewhere graphical. This
 * program could be the output stage of a bar graph composer, but
 * unlike a bar graph composer (i.e. GNU graph), it can also be used
 * to draw free form vectors willy nilly without a lot of fuss.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <plot.h> /* What this program is designed to use. */

void readuserdata (FILE *, plPlotter *);
int strip_bogus_stuff(char *);
void usage_help_msg ( void );

static int v=0, r=0; /* Verbose and Relative flags */

/* Set defaults for text alignment global vars cxe-03.02.13 */
int hortextalign='l', vertextalign='b';

int main (int argc, char **argv )
{
static int o;
static double usize[4]={ 0, 0, 1.0, 1.0};
static char optstring[]="a:b:c:dhs:t:vx:X:y:Y:",
            outputformat[9]="X",
            defaultcolor[25]="#ffffff";
plPlotterParams * usersettings = pl_newplparams ();
plPlotter * userplotter;

/* Set background color black as default cuz I like it that way. */
pl_setplparam (usersettings, "BG_COLOR", "#000000")  ;

while ( (o = getopt(argc, argv, optstring)) != -1)
 switch(o) {

  case 'a': {     /* Animated GIF output - arg=interframe time */
pl_setplparam (usersettings, "GIF_ANIMATION", "yes");
pl_setplparam (usersettings, "GIF_DELAY", optarg);
pl_setplparam (usersettings, "GIF_ITERATIONS", "0");
if (v) fprintf(stderr,"# option: -a %s (GIF delay)\n", optarg);
break;
            }

  case 'b': {     /* Background color setting - arg=color */
pl_setplparam (usersettings, "BG_COLOR", optarg)  ;
if (v) fprintf(stderr,"# option: -b %s (background color)\n", optarg);
break;
            }

  case 'c': {    /* Color setting for inital pen - arg=color */
strcpy(defaultcolor, optarg);
if (v) fprintf(stderr,"p c %s\n", defaultcolor);
break;
            }

  case 'd': {    /* Double buffering on - no arg */
pl_setplparam (usersettings, "USE_DOUBLE_BUFFERING", "yes")  ;
if (v) fprintf(stderr,"# option: -d (X double buffering)\n");
/*Should check for incompatible setting like GIF_ANIMATION */
break;
            }

  case 'h': {     /* Help message - no arg */
usage_help_msg();
return(0);
            }

  case 's': {     /* Size of bitmap - arg=maybe like X geometry */
pl_setplparam (usersettings, "BITMAPSIZE", optarg);
if (v) fprintf(stderr,"# option: -s %s (size of bitmap)\n", optarg);
break;
            }

  case 't': {     /* Type of output - arg=X|ps|gif|png|pnm */
strcpy(outputformat, optarg);
if (v) fprintf(stderr,"# option: -t %s (output mode)\n", outputformat);
break;
            }

  case 'v': {     /* Verbose - generates same graphics */
v=1;
fprintf(stderr,"# option: -v verbose output\n");
   break;
            }
  case 'x': {     /* X coord of user space lower left - arg=value */
usize[0]=atof(optarg);
if (v) fprintf(stderr,"# option: -x %s (left user coordinate)\n", optarg);
   break;
            }

  case 'y':{      /* Y coord of user space lower left - arg=value */
usize[1]=atof(optarg);
if (v) fprintf(stderr,"# option: -y %s (bottom user coordinate)\n", optarg);
   break;
           }

  case 'X':{      /* X coord of user space upper right - arg=value */
usize[2]=atof(optarg);
if (v) fprintf(stderr,"# option: -X %s (right user coordinate)\n", optarg);
   break;
           }

  case 'Y':{      /* Y coord of user space upper right - arg=value */
usize[3]=atof(optarg);
if (v) fprintf(stderr,"# option: -Y %s (top user coordinate)\n", optarg);
   break;
           }

  default:
   if (v) fprintf(stderr,"# option: %c (unknown)\n", o);

} /* close switch block for options processing */

/* Graphics initialization. */
userplotter = pl_newpl_r (outputformat, stdin, stdout, stderr, usersettings); 

pl_openpl_r (userplotter);
 pl_fspace_r (userplotter, usize[0], usize[1], usize[2], usize[3]);
 pl_colorname_r (userplotter, defaultcolor);
 pl_erase_r (userplotter);

/* Parse file names and call data readers */
if ( optind == argc) {
 readuserdata ( stdin, userplotter );
   } /* if - there are no file names spec'd */
else {
 for ( ; optind < argc; ++optind ) {
   if ( strcmp( argv[optind] , "-") ) {
 readuserdata ( fopen( argv[optind], "r" ), userplotter );
       } /* if - there is a non "-", normal filename */
   else {
 readuserdata ( stdin , userplotter );
        } /* else - there is a dash for a file name */
 pl_endpath_r (userplotter);
 pl_flushpl_r (userplotter);
     } /*for - step through filenames */
   } /*else - there are one or more filenames spec'd */


/* Close graphics */
  pl_endpath_r (userplotter);
  pl_flushpl_r (userplotter);
  pl_closepl_r (userplotter);
pl_deletepl_r (userplotter);

return(0);
} /* End of main */

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* Function to scan input file and act on the instructions. */
void readuserdata (FILE * userfile, plPlotter * p) {
static const int LINEBUFFERSIZE=1000;
static const char * optlist="abcdjlmnoprtABCDJLMNOPRT";
int (*fun)(); /* pointer to a function - use rel or abs? */
unsigned int clean;
char * pos;
static char test;
unsigned int marktype=5;
double x1, y1, x2, y2, x3, y3, marksize=0.1;
char command='l', subcommand;
char dataline[LINEBUFFERSIZE];
extern int hortextalign, vertextalign; /*cxe 03.02.13 */

while ( fgets(dataline, LINEBUFFERSIZE, userfile ) != NULL ){
clean=0;
/* Toss out lines to ignore with 'continue'. */
 test = dataline[ strspn(dataline, " \t") ];
 switch (test) {
   case '/':
   case '"': 
   case '!':
   case '#':
    { if (v) fprintf(stderr, "%s", dataline);
      continue; }
  case '\n':
    { if (v) fprintf(stderr, "#\n");
   /* a good place to force a draw - helps when using console input */
      pl_endpath_r (p);
      pl_flushpl_r(p);
      continue; }
        } /* End switch to test for first non whitespace char */

  command='l'; /* Default in case we don't find anything */

  if ( (pos=strpbrk( dataline, optlist)) ) { 
       command=*pos; *pos=' ';
     if ( strpbrk ( &command, "pP" ) ) {
     if ( (pos=strpbrk(dataline, "acwjeflhvACWJEFLHV")) )
      { subcommand=*pos; *pos=' ';}
     else
     fprintf(stderr, "# error: missing parameter: %s"
                          ,dataline);
     } /* if - a command requiring a param (p,P) is found */

    } /* if - there is a letter signifying a command */

  switch(command) {

/*  These commands might have text so don't strip non numbers yet.  */
    case 'P':
    case 'p': {
      switch(subcommand) {
        
       case 'A': /* cxe- Just added this feature 2003.02.07 */
       case 'a': {
       sscanf(dataline, "%lg", &x1);
       if (v) fprintf(stderr,"p a %g\n", x1 );
       pl_textangle_r( p, x1 );
         break;  } /* subcase of textangle change */

       case 'C':
       case 'c': {
       sscanf(dataline, "%s", dataline);
       if (v) fprintf(stderr,"p c %s\n", dataline );
       pl_colorname_r( p, dataline );
         break;  } /* subcase of color change */

       case 'E':
       case 'e': {
       sscanf(dataline, "%s", dataline);
       if (v) fprintf(stderr, "p e %s\n", dataline );
       pl_capmod_r (p, dataline );
         break;  } /* subcase of endcap mode change */

       case 'J':
       case 'j': {
       sscanf(dataline, "%s", dataline);
/* "miter" (default), "round", "bevel", "triangular" */
       if (v) fprintf(stderr, "p j %s\n", dataline );
       pl_joinmod_r( p, dataline );
         break;  } /* subcase of join mode change */

       case 'L':
       case 'l': {
/* "solid", "dotted", "dotdashed", "shortdashed", "longdashed",
 * "dotdotdashed", "dotdotdotdashed", "disconnected" */
       if (v) fprintf(stderr, "p l %s\n", dataline );
       pl_linemod_r( p, dataline );
         break;  } /* subcase of line mode change */

       case 'F':
       case 'f': {
       strip_bogus_stuff(dataline);
       sscanf(dataline, "%lg", &x1);
       if (v) fprintf(stderr, "p f %g\n", x1 );
       pl_ffontsize_r( p, x1 );
         break;  } /* subcase of fontsize change */

/* added this feature 2003.02.13 -
   sets global horizontal text align type var
   l=left, c=center, r=right */
       case 'H':
       case 'h': {
       sscanf(dataline, "%s", dataline);
       hortextalign = (int) dataline[0];
       if (v) fprintf(stderr, "p h %c\n", (char) hortextalign );
         break;  } /* subcase of horizontal text align type change */

/* added this feature 2003.02.13 -
   sets global vertical text align type var 
   b=bottom, x=baseline, c=center, C=capline, t=top */
       case 'V':
       case 'v': {
       sscanf(dataline, "%s", dataline);
       vertextalign = (int) dataline[0];
       if (v) fprintf(stderr, "p v %c\n", (char) vertextalign );
         break;  } /* subcase of vertical text align type change */

       case 'W':
       case 'w': {
       strip_bogus_stuff(dataline);
       sscanf(dataline, "%lg", &x1);
       if (v) fprintf(stderr, "p w %g\n", x1 );
       pl_flinewidth_r( p, x1 );
         break;  } /* subcase of linewidth change */
        

       default: {
       fprintf(stderr, "# error: couldn't find  property\n");
       return; }

                } /* end of sub switch structure */
       break;} /* case of property change mode */


    case 'L':
    case 'l': {
    fun = strip_bogus_stuff(dataline) ? &pl_fcontrel_r : &pl_fcont_r ;
    if (r && v) fprintf(stderr, "@"); 
    clean=1;
     if ( sscanf(dataline, "%lg %lg %lg %lg", &x1, &y1, &x2, &y2 ) > 2)
      { if (v) fprintf(stderr, "line (%g,%g) to (%g,%g)\n",
                              x1,y1,x2,y2);
/*     pl_fline_r ( p, x1, x2, y1, y2 );  */
/* pl_fline_r seems messed up, i.e. it doesn't work. How about this? */
       pl_fmove_r ( p, x1, y1 );
       (*fun)( p, x2, y2 ); 
        break; } }/* break only IF there are 4 args else fall to 'n' */
                 /* case of line */

    case 'N':
    case 'n': {
     if (!clean) {
    fun = strip_bogus_stuff(dataline) ? &pl_fcontrel_r : &pl_fcont_r ;
    if (r && v) fprintf(stderr, "@"); }
     sscanf(dataline, "%lg %lg", &x1, &y1 );
       if (v) fprintf(stderr, "next to (%g,%g)\n", x1,y1);
       (*fun)( p, x1, y1 ); 
               break; } /* case of cont */
       
    case 'C':
    case 'c': {
    fun = strip_bogus_stuff(dataline) ? &pl_fcirclerel_r : &pl_fcircle_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg %lg", &x1, &y1, &x3 );
      if (v) fprintf(stderr, "circle cen=(%g,%g) rad=%g\n", x1,y1,x3);
      (*fun)( p, x1, y1, x3 ); 
               break; } /* case of circle */

    case 'A':
    case 'a': {
    fun = strip_bogus_stuff(dataline) ? &pl_farcrel_r : &pl_farc_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg %lg %lg %lg %lg",
                       &x1, &y1, &x2, &y2, &x3, &y3 );
       if (v) fprintf(stderr, "arc cen=(%g,%g) from=(%g,%g) to=(%g,%g)\n"
                              ,x1,y1,x2,y2,x3,y3);
       (*fun)( p, x1, y1, x2, y2, x3, y3 );
               break; } /* case of arc */
     
    case 'B':
    case 'b': {
    fun = strip_bogus_stuff(dataline) ? &pl_fboxrel_r : &pl_fbox_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg %lg %lg", &x1, &y1, &x2, &y2 );
       if (v) fprintf(stderr, "box from=(%g,%g) to=(%g,%g)\n", x1,y1,x2,y2);
       (*fun)( p, x1, y1, x2, y2 );
               break; } /* case of box */

    case 'D':
    case 'd': {
    fun = strip_bogus_stuff(dataline) ? &pl_fpointrel_r : &pl_fpoint_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg", &x1, &y1 );
       if (v) fprintf(stderr, "dot (%g,%g)\n", x1,y1);
       (*fun)( p, x1, y1 );
               break; } /* case of dot pixel */

    case 'J':
    case 'j': {
    fun = strip_bogus_stuff(dataline) ? &pl_fmoverel_r : &pl_fmove_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg", &x1, &y1 );
       if (v) fprintf(stderr, "jump to=(%g,%g)\n", x1,y1);
       (*fun)( p, x1, y1 );
               break; } /* case of jump move */

    case 'M':
    case 'm': {
    fun = strip_bogus_stuff(dataline) ? &pl_fmarkerrel_r : &pl_fmarker_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg %i %lg", &x1, &y1, &marktype, &marksize );
       if (v) fprintf(stderr, "marker at=(%g,%g) type=%i size=%g\n"
                          , x1,y1,marktype,marksize);
       (*fun)( p, x1, y1, marktype, marksize);
               break; } /* case of marker */

    case 'R':
    case 'r': {
       if (v) fprintf(stderr, "reset\n");
       pl_erase_r( p );
               break; } /* case of reset erase */

    case 'O':
    case 'o': {
    fun = strip_bogus_stuff(dataline) ? &pl_fellipserel_r : &pl_fellipse_r ;
    if (r && v) fprintf(stderr, "@");
     sscanf(dataline, "%lg %lg %lg %lg %lg", &x1, &y1, &x2, &y2, &x3 );
       if (v) fprintf(stderr, "oval at=(%g, %g) height=%g width=%g ang=%g\n",
                           x1,y1,x2,y2,x3);
/*        This is all in radians!! If we want deg, convert x3 here */
       (*fun)( p, x1, y1, x2, y2, x3 );
               break; } /* case of ellipse (oval) */

    case 'T':
    case 't': {
    fun = strip_bogus_stuff(dataline) ? &pl_fmoverel_r : &pl_fmove_r ;
    if (r && v) fprintf(stderr, "@");
     if ( sscanf(dataline, "%lg %lg", &x1, &y1 ) > 1 ) {
        (*fun)(p, x1, y1);
        if (v) fprintf(stderr, "text at=(%g, %g)\n", x1, y1 ); }
     
    if (fgets(dataline, LINEBUFFERSIZE, userfile) == NULL )
       {fprintf(stderr,"# error: missing text data line\n"); return;}

       if (v) fprintf(stderr, "%s", dataline );
       /* cxe-2003.02.07-fix problem:can't send lf/cr to pl_label_r */
       dataline[strlen(dataline)-1]=0;
       pl_alabel_r( p, hortextalign, vertextalign, dataline );
               break; } /* case of text */

     default: {
        fprintf(stderr, "# error: I don't know what to do for this:\n");
        fprintf(stderr, "# '%s'\n", dataline );
              } /* End of default case */


  } /* end of switch - which modifier to act on */


  } /* End of while - no more lines to process in this file */
return;
} /* End of readuserdata function */



int strip_bogus_stuff(char * dataline) {

static const char * nonnumlist=
"abcdfghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXYZ\
!@+#$%^&*()[]{}~`,';<>?:_=|\t\"\\";
char * pos;

/* A good place to replace all "^" with e */
  while ( (pos=strpbrk( dataline, "^" )) )  *pos='e'; 

/* NEW */
/* Changing the input format to be ++ or @ for relative. That way, a
 * simple + can be a simple plus. The strategy is to look here for a
 * ++ and if found, change it to a single plus and carry on as before.
 * If only a single plus is found, change it to a 0 since that should
 * keep numbers intact such as +0.0009e+03 to 00.0009e003  */
while ( (pos=strstr( dataline, "++")) ) { *pos=' '; *(pos+1)='@'; }
        /* printf("What now?:%s\n", dataline); */
while ( (pos=strpbrk( dataline, "+" )) )  *pos='0'; 
        /* printf("What now?:%s\n", dataline); */
/* End of NEW change - this whole idea can be reverted by deleting
 * this while block.... */

/* Here would be a good place to have a look at the e's and E's in 
 * the dataline. If the e is preceded by a (0123456789.) and
 * followed by (0123456789+-). */
  while ( (pos=strpbrk( dataline, "Ee" )) ) 
   {
    if ( ( strchr ( "0123456789.", pos[-1]) == NULL) ||
         ( strchr ( "0123456789-", pos[1]) == NULL) )
     *pos=' '; else *pos='^';
   }
  while ( (pos=strpbrk( dataline, "^" )) )  *pos='e'; 

/* Look for @ and + characters to signify that the ops should be */
/* relative - if r=0 then abs, else return 1 */
  r = (pos=strpbrk (dataline, "@+")) ? 1 : 0 ;

/* The following cases work out best when all the extraneous letters */
/* are stripped away. They take only numbers. */
  while ( (pos=strpbrk( dataline, nonnumlist )) ) {
       *pos=' '; 
       } /* End while - clear out any other option letters in data */
return(r);
} /* end special function to strip extraneous non-number characters */


void usage_help_msg ( void ){
fprintf(stderr, "gradu - Graphics Dump $Revision: 1.19 $\n");
fprintf(stderr, "Chris X. Edwards $Date: 2003/02/15 01:04:41 $\n");
fprintf(stderr, "Usage: gradu [options] [datafile]\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -a <msecs> = GIF animation with frame interval\n");
fprintf(stderr, " -b <color> = Background color (red or #FF0000)\n");
fprintf(stderr, " -c <color> = Main drawing element color\n");
fprintf(stderr, " -d         = Use double buffering\n");
fprintf(stderr, " -h         = This message with version\n");
fprintf(stderr, " -s <size>  = Bitmap size in this format: 640x480[+50+50]\n");
fprintf(stderr, " -t <type>  = X, png, pnm, gif, ps, hpgl, etc.\n");
/* Or "Xdrawable", "svg", "ai", "fig", "pcl", "regis", "tek", "meta" */
fprintf(stderr, " -x <value> = Left extent in user coordinates\n");
fprintf(stderr, " -y <value> = Bottom extent in user coordinates\n");
fprintf(stderr, " -X <value> = Right extent in user coordinates\n");
return;
} /* end function - usage_help_msg */
