/* $Log:	dospecial.c,v $
 * Revision 0.8  92/11/23  19:46:42  19:46:42  bt (Bo Thide')
 * Fixed resolution bug. Portable downloading. Added/changed options. PJXL color support
 * 
 * Revision 0.7  92/11/13  02:41:26  02:41:26  bt (Bo Thide')
 * More bug fixes and improvements. Support for PaintJet XL
 * 
 * Revision 0.6  92/11/10  21:47:42  21:47:42  bt (Bo Thide')
 * Bug fixes. Added -R option. Better font handling.
 * 
 * Revision 0.5  92/11/09  16:25:29  16:25:29  bt (Bo Thide')
 * Rewrite of dospecial.c. Extended \special support
 * 
 * Revision 0.4  92/11/08  02:45:45  02:45:45  bt (Bo Thide')
 * Changed to portable bit manipulations. Replaced strrstr for non-POSIX compliant C. Fixed numerous bugs. Added support for more \special's.
 * 
 * Revision 0.3  92/08/24  12:45:37  12:45:37  bt (Bo Thide')
 * Fixed 8 bit (dc font) support.
 * 
 * Revision 0.2  92/08/23  17:28:56  17:28:56  bt (Bo Thide')
 * Source cleaned up.  Changed certain function calls.  Removed globals.
 * 
 * Revision 0.1  92/08/22  23:58:47  23:58:47  bt (Bo Thide')
 * First Release.
 *  */

/*
 * This procedure will handle any special commands that might appear.
 * The following variants are handled:
 *
 * \special{command}
 * \special{command(parameters)}
 * \special{command=parameters}
 * \special{command parameters}
 * \special{command:parameters}
 *
 * The string contained in the special command is in the name array starting at
 * 'nextnamesfree' and ending with a '\0'. Also, the 'prescan' boolean should
 * be checked to see if this is the first or second time that this string was
 * encountered.
 *
 * Copyright 1992 Bo Thide', Uppsala, Sweden <bt@irfu.se>.
 */

#define S_LANDSCAPE	0	/* Original \special "landscape" */
#define S_INCLUDE	1	/* Original \special "include()" */
#define S_GRAFIX	2	/* Original \special "grafix()" */
#define S_MOVE		3	/* Original \special "move()" */
#define S_ESCAPESEQ	4 	/* dvi \special "escapeseq ..." */
#define S_RASTERFILE	5 	/* dvi \special "rasterfile ..." */
#define S_RAWSTRING	6 	/* dvi \special "rawstring ..." */
#define S_ORIENTATION	7	/* Gustaf Neumann's \special "orientation" */
#define S_FILE		8	/* Gustaf Neumann's \special "file" */
#define S_PS		9 	/* PostScript \special's "ps:" */
#define S_EM		10	/* 'emtex' \special's "em:" */
#define S_PCL		11	/* PCL \special's "pcl:" */
#define S_HPGL		12	/* PCL5 \special's "hpgl:" */
#define S_PJXL		13	/* PaintJet XL \special "rawstring ..." */
#define SPECIALS	14      /* Number of \special's supported */
#define GRAFLIST	7	/* Number of graphics resolutions supported */
#define COLORLIST	8	/* Number of PaintJet XL text colors */

#include <ctype.h>
#include <stdio.h>
#include "globals.h"
#include "pcl.h"

static char rcsid[] = "$Header: dospecial.c,v 0.8 92/11/23 19:46:42 bt Exp $";

void dospecial(bitfile, prescan, pcllevel, hh, vv, resolution)
FILE	*bitfile;
bool	prescan;
short	pcllevel;
long	hh, vv;
short	resolution;
{

  /* The \special arguemnt keywords and their assocated delimiters */
  static char *spclargv[SPECIALS][2] = {
	"landscape", "",
	"include", "(",
	"grafix", "(",
	"move",	"(",
	"escapeseq", " ",
	"rasterfile", " ",
	"rawstring", " ",
	"orientation", "=",
	"file", "=",
	"ps", ":",
	"em", ":",
	"pcl", ":",
	"hpgl", ":",
	"pjxl", ":"
	};
  static char *graflist[] = {"600","300","180","150","100","90","75"};
  static char *colorlist[] = {"black","red","green","yellow","blue",
			      "magenta","cyan","white"};
  char	*bufptr;
  char	*delimptr;		/* Pointer to the argument delimiter */
  char	*iptr, *jptr;		/* Generic pointers */
  char	c;
  char	drawrow[8];
  char	curname[NAMELENGTH];	/* Current name of generic file */
  bool	relative;
  short	angle=0;
  short	planes=1;
  int	i, j, k, l;
  int	pxl_rows, row_bytes;
  int	gflag;
  int	ic;
  long	hoffset, voffset;
  FILE	*genfile;		/* Generic file pointer */

  bufptr = nextnamesfree;

#ifdef DEBUG
fprintf(stderr,"\ndospecial: nextnamesfree = '%s'\n", nextnamesfree);
fprintf(stderr,"dospecial: prescan = '%d'\n", prescan);
fprintf(stderr,"dospecial: pcllevel = '%d'\n", pcllevel);
#endif /* DEBUG */

  /* Process as long as the 'nextnamesfree' is not empty */
  while(*bufptr){
    while(isspace(*bufptr))	/* Skip over all leading white space */
      bufptr++;
    if(*bufptr){		/* Process only if it was not all white space */
      iptr = jptr = bufptr;
      while(isalpha(*jptr))	/* Move to the end of the first alpha string */
        jptr++;

      /* See if the first alpha string is an OK keyword */
      k = 0;
      while(strncmp(bufptr,spclargv[k][0],strlen(spclargv[k][0])))
	k++;			/* If the keyword is correct k points to it */

      /* Locate the correct delimiter associated with the keyword */
      while(*jptr && (*jptr != *spclargv[k][1]))
	jptr++;
      delimptr = jptr;

      /*
      * Bail out if no correct keyword  (k >= SPECIALS)
      * or no correct delimiter (*jptr == NULL) was found
      */
      if(prescan && ((k >= SPECIALS) || !*jptr)) {
          fprintf(stderr,"\ndospecial: \\special command not understood: %s\n",
	    nextnamesfree);
	  return;
      }
      /*
      *  Here we know that the keyword is OK and that delimptr points to
      *  the correct delimiter associated with that keyword.
      */

#ifdef DEBUG
fprintf(stderr,"\ndospecial: delimptr = '%s'\n", delimptr);
#endif /* DEBUG */

      /* Move bufptr to point to the delimiter, after the first alpha string */
      bufptr = delimptr;

      /* Make sure the delimiter '(' has a matching ')' */
      if(*delimptr == '(') {
	bufptr++;
	while((*bufptr != ')') && *bufptr)
	  bufptr++;
	if(*bufptr == '\0')
	  fprintf(stderr,"\ndospecial: Expected ) after parameter in \\special{%s}.\n", nextnamesfree);
	else
	  bufptr++;
      }
      else {		/* Handle the other delimiters */
      bufptr++;
      while(*bufptr)
	bufptr++;
      }
      /* Now bufptr points to the end of the nextnamesfree string */


      /* Move jptr to point to after the (one byte) delimiter */
      jptr = delimptr + 1;

      /* Parse the rest of the string for keyword entry spclargv[k][0] */
      switch (k) {

      case S_LANDSCAPE:  	/* Landscape mode */
        if(prescan)
          landscape = TRUE;
        break;

      case S_INCLUDE: 	 	/* include(filename) */
        if(!prescan) {
          iptr = curname;
          while((*jptr != ' ') && (*jptr != ')') && *jptr)
            *iptr++ = *jptr++;
          *iptr = '\0';

#ifdef DEBUG
fprintf(stderr,"\ndospecial: S_INCLUDE curname = %s\n", curname);
#endif /* DEBUG */

	  if((genfile = fopen(curname,"r")) == NULL) {
	    fprintf(stderr,"Cannot open the \\special{include()} file %s for input.\n", curname);
	    exit(-1);
	    }
	  /* Make the cursor position current and push it */
	  /*
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
	  */
          while((ic = getc(genfile)) != EOF)
            putc(ic, bitfile);
	  fclose(genfile);
	  /*
          fputs(PCL4_POP, bitfile);
	  */
        }
        break;

      case S_MOVE:	/* move(h=..dim v=..dim) */
        if(!prescan) {
          hoffset = voffset = 0L;
          relative = TRUE;
          while(*jptr != ')'){
            while(*jptr == ' ')
              jptr++;
	    if(!(c = *jptr++));
	    if(*jptr == '=') jptr++;
	    switch(c)
	    {
	    case 'h':
	      hoffset = dim_to_dots(jptr, resolution);
	      break;
	    case 'v':
	      voffset = dim_to_dots(jptr, resolution);
	      break;
	    case 'a':
	      relative = FALSE;
	      break;
	    case 'r':
	      relative = TRUE;
	      break;
	    default:
	      fprintf(stderr,"\ndospecial: Invalid parameter in \\special{move}\n");
	      break;
	    }
	  while((*jptr != ' ') && (*jptr != ')') && *jptr)
	    jptr++;
	}

#ifdef DEBUG
fprintf(stderr,"\ndospecial: S_MOVE relative = %d, hoffset = %ld, voffset = %ld\n", relative, hoffset, voffset);
#endif /* DEBUG */

	if(relative) {
	  if(hoffset) 
	    fprintf(bitfile, PCL4_MOVE_HREL, hoffset);
	  if(voffset)
	  fprintf(bitfile, PCL4_MOVE_VREL, voffset);
	}
	else {
	  if(hoffset)
	    fprintf(bitfile, PCL4_MOVE_H_POSITION, hoffset);
	  if(voffset)
	    fprintf(bitfile, PCL4_MOVE_V_POSITION, voffset);
	}
      }
      break;

      case S_GRAFIX:		/* grafix(filename [resolution]) */
        if(!prescan) {
          iptr = curname;
          while((*jptr != ' ') && (*jptr != ')') && *jptr)
            *iptr++ = *jptr++;
          *iptr = '\0';
	  if((genfile = fopen(curname,"r")) == NULL) {
	    fprintf(stderr,"Cannot open the \\special{grafix()} input file %s for input.\n", curname);
	    exit(-1);
	  }
          while(*jptr == ' ') jptr++;
          delimptr = jptr;
          while((*jptr != ' ') && (*jptr != ')') && *jptr)
            jptr++;
          if(jptr != delimptr)
          {
            *jptr = '\0';
            l = 0;
            while(l < GRAFLIST)
              if(!strcmp(delimptr, graflist[l]))
                break;
              else l++;
          }
          else l = 2;
          if(l == GRAFLIST)
          {
            fprintf(stderr,"\ndospecial: Invalid resolution in \\special{grafix}: %s.\n", delimptr);
            l = 2;
          }
          if(l)
            fprintf(bitfile, PCL4_GRAPHRES, graflist[l]);
	  /* Make the cursor position current and push it */
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
          fputs(PCL4_START_RASTER, bitfile);
          pxl_rows = (getc(genfile) << 8);
          pxl_rows = pxl_rows + getc(genfile);
          gflag = getc(genfile);
          if(!gflag)
            sprintf(drawrow, PCL4_DRAWROW, row_bytes = getc(genfile));
          for(i = 0 ; i < pxl_rows ; i++) {
            if(gflag)
              fprintf(bitfile, PCL4_DRAWROW, row_bytes = getc(genfile));
            else
              fputs(drawrow, bitfile);
            for(j = 0 ; j < row_bytes ; j++)
              putc(getc(genfile), bitfile);
          }
	  fclose(genfile);
          fputs(PCL4_END_RASTER, bitfile);
          if(l)
            fprintf(bitfile, PCL4_GRAPHRES, graflist[0]);
          fputs(PCL4_POP, bitfile);
        }
        break;

      case S_ORIENTATION:	/* PCL5 print orientation/direction */
        if(!prescan) {
	  if(pcllevel < 5)
	    fprintf(stderr,"\ndospecial: Changing print direction via \\special not supported at PCL level %d.\n           Only at PCL level 5 (option -5).", pcllevel);
	  else {

	    angle = (short)atoi(jptr);
	    if(angle==1)
	      angle=90;
	    else if(angle==2)
	      angle=180;
	    else if(angle==3)
	      angle=270;

#ifdef DEBUG
fprintf(stderr,"\ndospecial: S_ORIENTATION angle = %d\n", angle);
#endif /* DEBUG */

	    /* Make the cursor position current */
	    /*
	    fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
	    */
	    fprintf(bitfile, PCL5_PRINT_DIRECTION, angle);
	  }
	}
	break;

      case S_FILE:		/* Input file */
        if(!prescan) {
	  iptr = curname;
          while((*jptr != ' ') && *jptr)
            *iptr++ = *jptr++;
          *iptr = '\0';
	  if((genfile = fopen(curname,"r")) == NULL) {
	    fprintf(stderr,"Cannot open the \\special{input=} file %s for input.\n", curname);
	    exit(-1);
	  }
	  /* Make the cursor position current and push it */
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
          while((ic = getc(genfile)) != EOF)
            putc(ic, bitfile);
	  fclose(genfile);
          fputs(PCL4_POP, bitfile);
        }
        break;

      case S_PCL:		/* PCL code */
      case S_PS:		/* PostScript code */
        if(!prescan) {
	  /* Make the cursor position current and push it */
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
            while(*jptr)
              putc(*jptr++, bitfile);
          fputs(PCL4_POP, bitfile);
        }  
	break;

      case S_HPGL:		/* PCL5 HP-GL/2 code */
        if(!prescan) {
	  if(pcllevel < 5)
	    fprintf(stderr,"\ndospecial: Using HP-GL/2 commands in \\special not supported at PCL level %d.\n           Only at PCL level 5 or higher (option -5).", pcllevel);
	  else {
	  /* Make the cursor position current and push it */
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
	  /* Enter HP-GL/2 mode. Pen to previous PCL pen position */
	  fputs(PCL5_HPGLMODE_PCLPEN, bitfile);
	  while(*jptr)
	    putc(*jptr++, bitfile);
	  /* Enter PCL mode. Pen to previous PCL pen position */
	  fputs(PCL5_PCLMODE_PCLPEN, bitfile);
          fputs(PCL4_POP, bitfile);
	  }
        }  
	break;

      case S_RASTERFILE:	/* rasterfile ...*/
        if(!prescan) {
	  iptr = curname;
          while((*jptr != ' ') && *jptr)
            *iptr++ = *jptr++;
          *iptr = '\0';

#ifdef DEBUG
fprintf(stderr,"\ndospecial: S_RASTERFILE curname = %s\n", curname);
#endif /* DEBUG */

	  if((genfile = fopen(curname,"r")) == NULL){
	    fprintf(stderr,"Cannot open the \\special{rasterfile} file %s for input.\n", curname);
	    exit(-1);
	    }
	  /* Make the cursor position current and push it */
	  fprintf(bitfile, PCL4_MOVE_POSITION, hh+h_offset, vv+v_offset);
          fputs(PCL4_PUSH, bitfile);
          while((ic = getc(genfile)) != EOF)
            putc(ic, bitfile);
	  fclose(genfile);
          fputs(PCL4_POP, bitfile);
        }
        break;

      case S_PJXL:
        if(!prescan) {

#ifdef DEBUG
fprintf(stderr,"\ndospecial: S_PJXL jptr = %s\n", jptr);
#endif /* DEBUG */

          l = 0;
          while(l < GRAFLIST) {
            if(!strcmp(jptr, colorlist[l]))
              break;
            else
	      l++;
          }
          if(l == GRAFLIST) {
            fprintf(stderr,"\ndospecial: Invalid color in \\special{pjxl:}: %s.\n", jptr);
	    l = 0;
          }
	  fprintf(bitfile, PJXL_TEXT_COLOR, l);
        }
	break;

      case S_ESCAPESEQ:
      case S_RAWSTRING:
      case S_EM:
        if(prescan) {
          fprintf(stderr,"\ndospecial: \\special command not implemented: %s\n",
	    nextnamesfree);
        }
        break;
      }
    }
  }
}