/* specialparse.c */

/************************************************************************/
/* Some functions of this file are borrowed from Tomas Rokicki's	*/
/* printer driver "dvips". The functions GetKeyStr and GetKeyVal are,	*/
/* with little changes, from the file dospecial.c.			*/
/* "dvips" is a freely redistributable PostScript driver for dvi files.	*/
/* "dvips" is (C) Copyright 1987 by Tomas Rokicki.			*/
/*									*/
/*					Georg Hessmann   26.06.1990	*/
/************************************************************************/


KeyDesc KeyTab[] = 
 {{"ifffile", String},			/* ok */
  {"hsize",   Dimension},		/* ok */
  {"vsize",   Dimension},		/* ok */
  {"hoffset", Dimension},		/* ok */
  {"voffset", Dimension},		/* ok */
  {"scale",   Number},			/* not implemented */
  {"hscale",  Number},			/* not implemented */
  {"vscale",  Number},			/* not implemented */
  {"mode",    String},			/* new by MiL */
  {"red",     Number},			/* new by MiL */
  {"green",   Number},			/* new by MiL */
  {"blue",    Number},			/* new by MiL */
  {"gamma",   Number}};			/* new by MiL */
  
#define NKEYS (sizeof(KeyTab)/sizeof(KeyTab[0]))




/* local funktions */
static	void	offset_to_inch		(float *, float *, unsigned short);
static	char   *GetKeyStr		(char *str, KeyWord *kw);
static  int	GetKeyVal		(KeyWord *kw, KeyDesc tab[],
					 int nt, int *tno);




/*******   P A R S E R   *******************/


/* Umrechnung von 'offset' nach inch */ 
static void offset_to_inch(float *inch,float *offset,unsigned short einh)
{
  float temp;

  /** einh == pt|pc|in|bp|cm|mm|dd|cc **/
  switch (einh) {
    case 0x7074:		/* pt */
	temp = (float)*offset / 72.27;
	break;
    case 0x7063:		/* pc */
	temp = (float)*offset / 6.0225;
	break;
    case 0x696e:		/* in */
	temp = (float)*offset;
	break;
    case 0x6270:		/* bp */
	temp = (float)*offset / 72.0;
	break;
    case 0x636d:		/* cm */
	temp = (float)*offset / 2.5322;
	break;
    case 0x6d6d:		/* mm */
	temp = (float)*offset / 25.322;
	break;
    case 0x6464:		/* dd */
	temp = (float)*offset / 67.3467;
	break;
    case 0x6363:		/* cc */
	temp = (float)*offset / 5.6146;
	break;
    default:
 	temp = *offset;		/* NOP */
	break;
  }

  *inch = temp;
}


/**********************************************************************/
/*****************************  GetKeyStr  ****************************/
/**********************************************************************/

/* extract first keyword-value pair from string (value part may be null)
 * return pointer to remainder of string
 * return NULL if none found
 */

static char *GetKeyStr( char *str, KeyWord *kw )
{
  char *s, *k, *v, t;

  if ( str== NULL ) {
    return( NULL );
  }

  for ( s=str; *s == ' '; s++ );	 /* skip over blanks */
  if ( *s == '\0' ) {
    return( NULL );
  }

  for ( k=kw->Key;			/* extract keyword portion */
       (*s != ' ') && (*s != '\0') && (*s != '='); 
       *k++ = *s++ );

  *k = '\0';
  v = NULL;
  kw->Val[0] = '\0';
  kw->vt = None;

  for ( ; *s == ' '; s++ );		/* skip over blanks */

  if ( *s != '=' ) {			/* look for "=" */
    return( s );
  }

  for( s++ ; *s == ' '; s++ );		/* skip over blanks */
  if ( *s == '\'' || *s == '\"' ) {	/* get string delimiter */
    t = *s++;
  }
  else {
    t = ' ';
  }
  for (v=kw->Val;			/* copy value portion up to delim */
      *s != t && *s != '\0';
      *v++ = *s++ ) ;

  if ( t != ' ' && *s == t ) {
    s++;
  }

  *v = '\0';
  kw->vt = String;

  return( s );
}

/**********************************************************************/
/*****************************  GetKeyVal  ****************************/
/**********************************************************************/

/* get next keyword-value pair
 * decode value according to table entry
 */

static int GetKeyVal( KeyWord *kw, KeyDesc tab[], int nt, int *tno)
{
  int i;
  unsigned short ei;
  char *einh;
  float f;

  einh = (char *)&ei;
  ei = 0;
  *tno = -1;

  for(i=0; i<nt; i++) {
    if ( !strcmp(kw->Key, tab[i].Entry) ) {
      *tno = i;
      switch ( tab[i].Type ) {
        case None: 
	        if ( kw->vt != None ) {
	          return( FALSE );
        	}
	        break;
        case String:
	        if ( kw->vt != String ) {
	          return( FALSE );
	        }
	        break;
        case Integer:
	        if ( kw->vt != String ) {
	          return( FALSE );
	        }
	        if ( sscanf(kw->Val,"%d", &(kw->v.i)) != 1) {
	          return( FALSE );
	        }
	        break;
        case Dimension:
	        if (kw->vt != String ) {
	          return( FALSE );
	        }
	        if (sscanf(kw->Val,"%f%2s", &f, einh) != 2) {
	          return( FALSE );
	        }
	        offset_to_inch((float *)&(kw->v.n),&f,ei);
	        break;
        case Number:
	        if ( kw->vt != String ) {
	          return( FALSE );
	        }
	        if( sscanf(kw->Val,"%f", &(kw->v.n)) != 1) {
	          return( FALSE );
	        }
	        break;
     }
     kw->vt = tab[i].Type;
     return( TRUE );
    }
  }
  return( TRUE );
}


/*--------------------------------------------------------------------*/

/**********************************************************************/
/****************************  ParseSepcial  **************************/
/**********************************************************************/

void ParseSpecial (char *str, struct parse_result *res)
  /* interpret a \special command, made up of keyword=value pairs */
{
  KeyWord k;
  int i;
  
  while( (str=GetKeyStr(str,&k)) != NULL ) {	/* get all keyword-value pairs */

    /* for compatibility, single words are taken as file names */
    if ( k.vt == None /* && access(k.Key,0) == 0 */) {
      strcpy(res->iffile, k.Key);
    }
    else if (GetKeyVal(&k, KeyTab, NKEYS, &i) && i != -1) {
	   switch (i) {
	     case IFFILE:
	        strcpy(res->iffile, k.Val);
	        break;
	     case HSIZE:
	        res->hsize = k.v.n;
	        break;
	     case VSIZE:
	        res->vsize = k.v.n;
	        break;
	     case HOFFSET:
	        res->hoffset = k.v.n;
	        break;
	     case VOFFSET:
	        res->voffset = k.v.n;
	        break;
	     case SCALE:
	        res->scale = k.v.n;
	        break;
	     case HSCALE:
	        res->hscale = k.v.n;
	        break;
	     case VSCALE:
	        res->vscale = k.v.n;
	        break;
	     case MODE:
		if (!strcmp(k.Val, "bw")) res->mode = BandW;
		else if (!strcmp(k.Val, "gray"))
		{
		 res->mode = Gray;
		 res->red = 1.0;
		 res->green = 1.0;
		 res->blue = 1.0;
		}
		else if (!strcmp(k.Val, "color"))
		{
		 res->mode = Color;
		 res->red = 2.99;
		 res->green = 5.87;
		 res->blue = 1.14;
		}
		else if (!strcmp(k.Val, "ham"))
		{
		 res->mode = Ham;
		 res->red = 2.99;
		 res->green = 5.87;
		 res->blue = 1.14;
		}
		else res->mode = BandW;
		break;
	     case RED:
	        res->red = k.v.n;
		break;
             case GREEN:
	     	res->green = k.v.n;
		break;
	     case BLUE:
	        res->blue = k.v.n;
		break;
	     case GAMMA:
	        res->gamma = k.v.n;
		break;
	     default:
	        pline("%f @%s\n", k.v.n, KeyTab[i].Entry);
	        break;
           }
    }
    else {
      pline("Invalid keyword or value in \\special: (%d)",i);
      pline("    \"%s\" ignored", k.Key );
    }
  }
}
