/****************************************************
 * File cmd.c
 *
 * write and read to / from printer
 *
 *
 * Copyright (C) 2001 Jean-Jacques Sarton jj.sarton@t-online.de
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#if defined(HAVE_VARARGS_H) && !defined(HAVE_STDARG_H)
#include <varargs.h>
#else
#include <stdarg.h>
#endif

#if WITH_X
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#endif

#include "mtink.h"
#include "cmd.h"
#include "d4lib.h"

#ifdef MACOS
#include "usbHlp.h"
#include <pthread.h>
#define close mClose
#define open mOpen

#endif

#ifndef WRTIMEOUT
#define WRTIMEOUT 2000
#endif

#ifndef TEST
#define TEST 0
#define TEST_DEBUG 0
#endif

#ifndef O_SYNC
#define O_SYNC 0
#endif

/* these file build the code for the printer */
/* align function s                          */

#include "bldpcode.c"
#include "al820.c"
#include "alC82.c"
#include "align.c"

/* external function for alignment requires a greater amount of data */
static unsigned char printer_cmd[65536]; /* more as enough now */
static int  bufpos = 0;
int doRw = 0;

extern int inFromFile;

extern void setPrinterStateLabel(int state);

/* module global variables */
static int   currentAction     = 0;
static int   shmid             = -1;

static int   deviceHdl         = -1;
static pid_t pid               = -1;

static int   d4Initialized     = 0;
static int   sockId            = -1;
static int   sndSize           = 0x0200;
static int   rcvSize           = 0x0200;

static int   sockIdData        = -1;
static int   sndSizeData       = 0x1006;
static int   rcvSizeData       = 0x0200;
static int   dataCredit        = 0;
shmem_t     *shmem             = NULL;
int          connectedOnServer = 0;
#if WITH_X
static Cursor waitCursor       = 0;
extern XtIntervalId tid;
#endif

#ifdef MACOS
static threadRun = 0;
#endif

/* prototypes */

static int openAndTestDevice(char *dev);
static int openDevice(char *dev, int doTest);
static int exitD4(int deviceHdl);
int initializeD4(void);
static int tellPrinterState(char *buf);

static int getId(char *name, int prot);
static int getIq(char *name, int prot);
static int resetPrinter(char *name, int prot);
static int checkNozzle(char *name, int prot);
static int cleanNozzle(char *name, int prot);

static int getIdNoD4(int deviceHdl, int prot);
static int getIqOld(int deviceHdl, int prot);
static int resetPrinterOld(int deviceHdl, int prot);
static int checkNozzleOld(int deviceHdl, int prot);
static int cleanNozzleOld(int deviceHdl, int prot);

static int getIdD4(int deviceHdl);
static int getIqD4(int deviceHdl);
static int d4Reset(int deviceHdl);
static int d4CheckNozzle(int deviceHdl);
static int d4CleanNozzle(int deviceHdl);
static int doAlignHead(char *name,int deviceHdl, int prot, int pass, int choice, char *function);
static int doAlignHeadColor(char *name,int deviceHdl, int prot, int pass, int choice);

static int doExchange(char *name,int deviceHdl, int prot, int step, int choice);

static void waitForPrinterReady(int deviceHdl, int maxWaitTime);

int writeD4Data(int deviceHdl, unsigned char *printer_cmd, int len, int stop);

#define CMD_IDX          4
#define AllInkPosition   1
#define BlackInkPosition 3
#define ColorInkPosition 4
#define StartInkFillUp   0x80
#define CancelInkFillUp  0xff

static unsigned char exchangeInk[] = {
   'x', 'i', 1, 0, 0 /* last is for selection */
};
/* answer "xi:#OK;\014" where # is the parameter from command */

static unsigned char carriagePosition[] = {
   'c', 'x', 1, 0, 1
};
/* answer "cx:00;\014" Home position
          "cx:FF;\014" moving
          "cx:01;\014" Exchange Position
          "cx:NA;\014" command unknown
   pass this 5 sec after issuing the xi command
 */

#if 0 /* not used yet */
#define ReplyExchangePossibility 0
#define ReplyCatridgeState       1
static unsigned char CartridgeState[] = {
   'c', 's', 1, 0, 0 /* last is for selection */
};
#endif

/* answer */

static unsigned char exchangeInkTerminate[] = {
   'e', 'i', 1, 0, 0
};


static int _Write(int a, unsigned char *b, int c, int to)
{
   int i;
   if ( debugD4 )
   {
      fprintf(stderr, "Write: ");
      for(i=0;i<(c<200?c:200);i++)
         fprintf(stderr,"%02x",(unsigned char)b[i]);
      fprintf(stderr,"\n");
      fprintf(stderr, "       ");
      for(i=0;i<(c<200?c:200);i++)
         fprintf(stderr,"%c ",isprint(b[i])?b[i]:' ');
      fprintf(stderr,"\n");
   }
#ifndef MACOS
   return devWrite(a,b,c,to);
#else   
   return mWrite(a,b,c);
#endif
}

static int _Read(int a, unsigned char *b, int c, int to)
{
   int i;
#ifndef MACOS
   int ret = devRead(a,b,c,to);
# else
   int ret = mRead(a,b,c);
#endif
if ( inFromFile )
return 0;

   if ( ret > 0 && debugD4 )
   {
      fprintf(stderr, "Read : ");
      for(i=0;i<ret;i++)
         fprintf(stderr,"%02x",(unsigned char)b[i]);
      fprintf(stderr,"\n");
      fprintf(stderr, "       ");
      for(i=0;i<ret;i++)
         fprintf(stderr,"%c ",isprint(b[i])?b[i]:' ');
      fprintf(stderr,"\n");
   }
   return ret;
}

/******************************************************
 * Function: sigChild()
 *
 * Do nothings but allows to detect the dead of a
 * child 
 * 
 ******************************************************/
 
static void sigChild(int code)
{
   /* rearm */
   signal(SIGCHLD, sigChild);
}

/******************************************************
 * Function: parentDie()
 *
 * terminate the action if any on die of parent 
 * 
 ******************************************************/
 
static void parentDie(int code)
{
   /* block new incoming signals */
   signal(SIGHUP,  SIG_IGN);
   signal(SIGSEGV, SIG_IGN);
   signal(SIGINT,  SIG_IGN);
   signal(SIGQUIT, SIG_IGN);
   signal(SIGTERM, SIG_IGN);

   if ( code == 0 )
   {
      switch(currentAction)
      {
         case EXCHANGE_ALL:
         if ( deviceHdl )
            doExchange(NULL,deviceHdl, PROT_D4, 4, 0);
      }

      if ( d4Initialized )
      {
         if ( deviceHdl )
         {
            exitD4(deviceHdl);
         }
      }
      exit(0);
   }
}

#if ! SERVER

/******************************************************
 * Function: doCommands()
 *
 * Child / thread code
 * 
 ******************************************************/
 
void *doCommands(void *dummy)
{  
#if WITH_X
    if ( display )
    {
       /* close X11 connection */
       close(ConnectionNumber(display));
    }
#endif
#ifndef MACOS
    /* add a signal handler so we can terminate cleanly */
    signal(SIGHUP,  parentDie);
    signal(SIGSEGV, parentDie);
    signal(SIGINT,  parentDie);
    signal(SIGQUIT, parentDie);
    signal(SIGTERM, parentDie);
    /* get ppid for the d4lib file */
    ppid = getppid();
#endif
    /* initialite all our variables */
    d4Initialized = False;
    currentAction = 0;
    deviceHdl     = -1;
    sockId        = -1;

    /* our task/process shall remain leaving */
    for (;;)
    {
       int mode;
       int pass;
       int choice;
       char *name;

       /* for mew commands the  shmem->ready flag is 0 */
       if ( shmem->ready == 0 )
       {
          mode   = shmem->mode;
          pass   = shmem->pass;
          choice = shmem->choice;
          name   = shmem->name;
          shmem->buf[0] = '\0'; /* clean return buffer */

          switch(shmem->command)
          {
             case RELEASE:
                  shmem->retVal = 0;
             break;
             case TEST_DEV:
                 shmem->retVal = openAndTestDevice(name);
             break;
             case TERMINATE:
                 if ( deviceHdl > -1 )
                 {
                    switch(currentAction)
                    {
                       case EXCHANGE_ALL:
                          if ( deviceHdl )
                          {
                             doExchange(name,deviceHdl, PROT_D4, 4, 0);
                          }
                       break;
                    }

                    if ( d4Initialized )
                    {
                        exitD4(deviceHdl);
                    }
#if DEBUG
                    fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
                    close(deviceHdl);
                    deviceHdl = -1;
                 }
#ifndef MACOS
                 exit(0);
#endif
              break;
              case RESET_PRT:
                 shmem->retVal = resetPrinter(name, mode&PALL );
              break;
              case GET_ID:
                 shmem->retVal = getId(name,  mode&PALL);
              break;
              case GET_IQ:
                  shmem->retVal = getIq(name,  mode&PALL);
             break;
              case CHECK_NOZZLE:
                 shmem->retVal = checkNozzle(name,  mode&PALL);
#if 1
                 if ( shmem->choice )
                 {
		    sleep(10);
                    shmem->retVal = resetPrinter(name, mode&PALL );
                 }
#endif
              break;
              case CLEAN_NOZZLE:
                 shmem->retVal = cleanNozzle(name,  mode&PALL);
              break;
              case ALIGN_HEAD:
                  shmem->retVal = doAlignHead(name, deviceHdl,  mode&PALL, pass, choice, shmem->function );
              break;
              case ALIGN_HEAD_C:
                 shmem->retVal = doAlignHeadColor(name, deviceHdl,  mode&PALL, pass, choice);
              break;
              case EXCHANGE_ALL:
                 shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_ALL);
              break;
              case EXCHANGE_B:
                 shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_B);
              break;
              case EXCHANGE_C:
                 shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_C);
              break;
              case EXCHANGE_NEXT:
                 shmem->retVal = doExchange(name, deviceHdl, mode&PALL, pass, 0);
              break;
              default:
                 shmem->retVal = 0;
          }
          shmem->ready = 1; /* tell the parent that all is done */

          if ( errno == ENODEV && deviceHdl > -1 )
          {
#if DEBUG
              fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
              close(deviceHdl);
              errno         = 0;
              deviceHdl     = -1;
              d4Initialized =  False;
              sockId        = -1;
              sockIdData    = -1;
          }

          if ( errno == -1 && deviceHdl > -1 )
          {
               /* we had problems with the printer, restart from the begin */
             if ( d4Initialized )
                exitD4(deviceHdl);
#if DEBUG
             fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
             close(deviceHdl);
             deviceHdl     = -1;
             sockId        = -1;
             sockIdData    = -1;
             d4Initialized = False;
             errno         = 0;
          }
       }
       else
       {
          usleep(10000);
#ifndef MACOS
          if ( ! connectedOnServer && getppid() == 1 )
          {
             /* parent process dead */
             if ( shmem != NULL )
             {
                shmdt((void*)shmem);
                shmem = NULL;
                shmctl(shmid, IPC_RMID, NULL);
                shmid = -1;
             }
             if ( deviceHdl > -1 )
                parentDie(0);
             else
                exit(1);
          }
#endif
       }
    }
}

/******************************************************
 * Function: callPrg()
 *
 * Call the send/receive function we need and make
 * sure that the main programm will be refreshed
 * 
 * The command send to the printer and also reading
 * from printer will be made in an extra process so
 * we avoid problems due to signal handling.
 *
 ******************************************************/

int callPrg(int command, char *name, int mode, int pass, int choice, char **retBuf, char* function)
{
   int    stat = -1;
   key_t  key  = -1;
   struct shmid_ds buf;
#if WITH_X
   XEvent xevent;
#endif
   int    answered;
   int count;
#ifdef MACOS
   pthread_t pt;
#endif
   if ( debugD4 )
   {
      char *s = "-";
      switch(command)
      {
         case TEST_DEV:       s = "TEST_DEV";       break;
         case GET_ID:         s = "GET_ID";         break;
         case GET_STAT:       s = "GET_STAT";       break;
         case GET_IQ:         s = "GET_IQ";         break;
         case RESET_PRT:      s = "RESET_PRT";      break;
         case CHECK_NOZZLE:   s = "CHECK_NOZZLE";   break;
         case CLEAN_NOZZLE:   s = "CLEAN_NOZZLE";   break;
         case CLEAN_NOZZLE_C: s = "CLEAN_NOZZLE_C"; break;
         case CLEAN_NOZZLE_B: s = "CLEAN_NOZZLE_B"; break;
         case WAIT_STAT_04:   s = "WAIT_STAT_04";   break;
         case WAIT_STAT_01:   s = "WAIT_STAT_01";   break;
         case EXCHANGE_ALL:   s = "EXCHANGE_ALL";   break;
         case EXCHANGE_B:     s = "EXCHANGE_B";     break;
         case EXCHANGE_C:     s = "EXCHANGE_C";     break;
         case EXCHANGE_NEXT:  s = "EXCHANGE_NEXT";  break;
         case ALIGN_HEAD:     s = "ALIGN_HEAD";     break;
         case ALIGN_HEAD_C:   s = "ALIGN_HEAD_C";   break;
         case TERMINATE:      s = "TERMINATE";      break;
         case RELEASE:        s = "RELEASE";        break;
      }
      fprintf(stderr,"callPrg(%s, %s,%d,%d,%d,...,%s)\n",
              s,
              name ? name : "-",
              mode,
              pass,
              choice,
              function ? function:"-"
              );
   }

   /* this portion of code will all time be processed */ 
   if ( name )
   {
#ifdef MACOS
      if ( strncmp(name,USB_PRT_NAME_PREFIX,strlen(USB_PRT_NAME_PREFIX)) )
#else
      if ( strncmp(name, "/dev/", 5) && !inFromFile)
#endif
      {
        /* server must be running */
         if ( !connectedOnServer )
         {
            if ( shmid != -1 )
            {
               /* terminate forked process */
               if ( pid > 0 )
               {
                  kill(pid,SIGTERM);
                  if ( shmem )
                  {
                     shmdt((void*)shmem);
                     shmctl(shmid, IPC_RMID, &buf);
                     shmem = NULL;
                     shmid = -1;
                  }
               }
            }
            /* not attached to server ? */
            if ( shmid == -1 )
            {
               /* get shared memory to server */
               key = ftok(name, 'M');
               if ( key > -1 )
               {
                  if ((shmid = shmget(key, sizeof(shmem_t), 0)) ==  -1)
                  {
#if DEBUG
                     perror("shmget");
#endif
                     return -1;
                  }
                  if ( shmctl(shmid, IPC_STAT, &buf) == -1 )
                  {
                     shmid = -1;
                     perror("shmctl");
                     return -1;
                  }
                  if ( buf.shm_nattch > 1)
                  {
                     shmid = -1;
                     errno = EBUSY;
                     perror("shmat");
                     return -1;
                  }
                  shmem = (shmem_t*)shmat(shmid, NULL, 0);
                  if ( shmem == NULL )
                  {
                    shmid = -1;
                     perror("shmat");
                     return -1;
                  }
                  connectedOnServer = 1;
               }
               else
               {
                  /* server don't run, return error */
#if DEBUG
                  perror("ftok");
#endif
                  return -1;
               }
            }
         }
      }
      else
      {
         /* port change, no more on server */
         if ( connectedOnServer )
         {
            /* detach from server shared memory */
            shmdt((void*)shmem);
            shmid             = -1;
            shmem             = NULL;
            connectedOnServer = 0;
            /* return -1; */
         }
      }

      /* get shared memory for port /dev/... */
      if ( shmid == -1 && ! connectedOnServer )
      {
         shmid = shmget(IPC_PRIVATE, sizeof(shmem_t), IPC_CREAT|IPC_EXCL|0777);
         if ( shmid > -1 )
         {
            shmem = (shmem_t*)shmat(shmid, NULL, 0);
            if ( name )
            {
               strcpy(shmem->name,name);
            }
         }
         else
         {
#if DEBUG
            perror("shmget private memory");
#endif
         }
      }
   }

   if ( shmid == -1 && command == TERMINATE )
   {
      /* this is OK */
      return 0;
   }
   else if ( shmid == -1 )
   {
      return -1;
   }

   /* check the shared memory */
   if ( shmctl(shmid, IPC_STAT, &buf) == -1 )
   {
      /* on error detach */
      perror("shmctl");
      shmdt((void*)shmem);
      shmem = 0;
      shmid = -1;
      connectedOnServer = 0;
      /* and return with error */
      return -1;
   }

   /* is the server is no more running ? */
   if ( connectedOnServer && buf.shm_nattch == 1 )
   {
      /* detach from memory, the server has terminated */
      /* and the shared memory will be removed         */
      shmdt((void*)shmem);
      shmem = 0;
      shmid = -1;
      connectedOnServer = 0;
      /* and inform the user iterface about that */
      return -1;
   }

   if ( command == RELEASE )
   {
      /* this is OK, only a check for server alive */
      return  0;
   }

   if ( shmem && shmem->block == 2 )
   {
      /* printer not attached to server */
      return  -1;
   }

   if ( shmem && shmem->block == 1 && command == GET_IQ )
   {
      /* other command pending OK */
      return  0;
   }

#if WITH_X
   /* get a cursor so the user see that the app. is busy */
   if ( waitCursor == (Cursor)0  && command > -1 )
   {
      waitCursor = XCreateFontCursor(XtDisplay(topLevel), XC_watch);
   }
   
   if (  command > -1 )
   {
      /* and set it */
      XDefineCursor(XtDisplay(topLevel),
                    XtWindow(topLevel),
                    waitCursor);
   }

   /* if we have a timer, kill it */
   if ( tid )
   {
      if ( shmem && shmem->block )
         ;
      else
      {
         XtRemoveTimeOut(tid); 
         tid = 0;
      }
   }
#endif
   /* no server, create the shared memory   */
   /* and the sub process for port /dev/... */
   if ( pid == -1 && ! connectedOnServer )
   {
      *shmem->function    = '\0';
      shmem->printerState = 4;
      shmem->mode         = mode;
      shmem->command      = command;
      shmem->pass         = pass;
      shmem->choice       = choice;
      *shmem->buf         = '\0';
      shmem->ready        = 0;

#ifdef MACOS
      /* on MacOS we can't fork, we need a thread ! */
      pthread_t pt;
      if ( !threadRun )
      {
         threadRun =1;
         pthread_create(&pt,NULL, doCommands,NULL);
      }
#else
      switch((pid=fork()))
      {
         case 0:
            doCommands(NULL);
            break;
         case -1:
         break;
         default:
            /* allow to react on child dead */
            signal(SIGCHLD, sigChild);
      }
#endif
   }

   /* set values to our shared memory */
   if ( shmem && !shmem->block )
   {
      if ( name )
         strcpy(shmem->name, name);
      if ( function )
         strcpy(shmem->function, function);
      else
         *shmem->function = '\0';
      shmem->printerState = 4;
      shmem->mode         = mode;
      shmem->command      = command;
      shmem->pass         = pass;
      shmem->choice       = choice;
      *shmem->buf         = '\0';
      shmem->ready        = 0;
   }
   else
   {
      if ( errno == ENODEV )
      {
          if ( deviceHdl > 0 )
          {
#if DEBUG
              fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
             close(deviceHdl);
          }
          deviceHdl = -1;
      }
      return -1;
   }

   /* avoid remove all user inputs                       */
   /* wait for end of child and refesh the X application */

   answered = 0;
   count = 0;
   for(;command > -1 ;)
   {
#if WITH_X
      if ( XtAppPending(theApp) & XtIMXEvent )
      {
         XtAppNextEvent(theApp, &xevent);

        /* check for button and key events, donīt dispatch them */
         if ( xevent.xany.type == ButtonPress || xevent.xany.type == ButtonRelease ||
              xevent.xany.type == KeyPress || xevent.xany.type == KeyRelease )
         {
            ;
         }
         else
         {
            /* none of the unwanted X events, process them */
            XtDispatchEvent(&xevent);
         }
      }
      else
      {
         /* no events sleep a little bit */
         if  ( shmem )
         {
            setPrinterStateLabel(shmem->printerState);
         }
         usleep(10000);
      }
#else
      usleep(10000);
#endif
      count++;
      /* check child process and leave the loop if dead */

#ifdef MACOS
      if ( threadRun == 0 )
      {
         shmdt((void*)shmem);
         shmem = NULL;
         shmctl(shmid, IPC_RMID, NULL);
         shmid = -1;
         break;
      }
#else
      if ( pid > 0 && waitpid(pid, &stat, WNOHANG) == pid )

      {
         stat = (stat >> 8) &0xff;
         if ( !connectedOnServer )
         {
            shmdt((void*)shmem);
            shmem = NULL;
            shmctl(shmid, IPC_RMID, NULL);
            shmid = -1;
         }
         pid   = -1;
         break;
      }
      else 
#endif
      if ( shmem && shmem->ready )
      {
         stat = shmem->retVal;
         break;
      }
      if ( shmem == NULL )
         break;
      /* if more that 30 sec elapsed */
      if ( count == 3000 )
      {
         if ( shmid != -1 )
         {
            shmdt((void*)shmem);
            shmem = NULL;
            shmctl(shmid, IPC_RMID, NULL);
            shmid = -1;
         }
         break;
      }
     
   }

   if ( shmem )
   {
      if ( retBuf )
      {
         *retBuf = shmem->buf;
      }
      if ( command > -1 )
         setPrinterStateLabel(shmem->printerState);
   }

#if WITH_X
   if ( command > -1 )
   { 
      /* restore the normal cursor */
      XUndefineCursor(XtDisplay(topLevel),
                      XtWindow(topLevel));

      if ( shmem && shmem->block == 0 && tid == 0 )
      {
         tid = XtAppAddTimeOut(theApp, CYCLE_TIME, handleTi, (XtPointer)NULL);
      }
   }
#endif

   if ( command == EXCHANGE_NEXT && retBuf != 0 )
   {
      *((int*)retBuf) = 1;
   }
   return stat;
}
#else
/******************************************************
 * Function: doCommand(int working)
 *
 * Call the send/receive function we need and make
 * sure that the main programm will be refreshed
 * 
 * This is the main entry for the server
 ******************************************************/

/* for the server */
static char *idStr = NULL;

int doCommand(int working)
{
   int          mode;
   int          pass;
   int          choice;
   char        *function;
   char        *name;
   errno  = 0;
   
   /* set the ppid variable for the d4lib.c file */
   if ( shmem )
   {
      mode     = shmem->mode;
      pass     = shmem->pass;
      choice   = shmem->choice;
      function = shmem->function;
      name     = shmem->name;
      shmem->buf[0] = '\0'; /* clean return buffer */
    
      if ( debugD4 )
      {
        char *s = "-";
        switch(shmem->command)
        {
           case TEST_DEV:       s = "TEST_DEV";       break;
           case GET_ID:         s = "GET_ID";         break;
           case GET_STAT:       s = "GET_STAT";       break;
           case GET_IQ:         s = "GET_IQ";         break;
           case RESET_PRT:      s = "RESET_PRT";      break;
           case CHECK_NOZZLE:   s = "CHECK_NOZZLE";   break;
           case CLEAN_NOZZLE:   s = "CLEAN_NOZZLE";   break;
           case CLEAN_NOZZLE_C: s = "CLEAN_NOZZLE_C"; break;
           case CLEAN_NOZZLE_B: s = "CLEAN_NOZZLE_B"; break;
           case WAIT_STAT_04:   s = "WAIT_STAT_04";   break;
           case WAIT_STAT_01:   s = "WAIT_STAT_01";   break;
           case EXCHANGE_ALL:   s = "EXCHANGE_ALL";   break;
           case EXCHANGE_B:     s = "EXCHANGE_B";     break;
           case EXCHANGE_C:     s = "EXCHANGE_C";     break;
           case EXCHANGE_NEXT:  s = "EXCHANGE_NEXT";  break;
           case ALIGN_HEAD:     s = "ALIGN_HEAD";     break;
           case ALIGN_HEAD_C:   s = "ALIGN_HEAD_C";   break;
           case TERMINATE:      s = "TERMINATE";      break;
           case RELEASE:        s = "RELEASE";        break;
        }
        fprintf(stderr,"doCommand(%s, %s,%d,%d,%d,...,%s)\n",
        	s,
        	name ? name : "-",
        	mode,
        	pass,
        	choice,
        	function ? function:"-");
      }
      

      if ( working == -1 && shmem->command != TERMINATE )
      {
         if ( deviceHdl > -1 )
         {
            if ( d4Initialized )
            {
                exitD4(deviceHdl);
            }
         }
         d4Initialized = 0;
         sockId        = -1;
         sockIdData    = -1;
         if ( deviceHdl > -1 )
         {
#if DEBUG
            fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
            close(deviceHdl);
         }
         deviceHdl = -1;
         shmem->retVal = 0;
         shmem->block  = 0;
         shmem->ready  = 1;
         if ( idStr )
         {
            free(idStr);
            idStr = NULL;
         }
#if DEBUG
         fprintf(stderr,"device file closed\n");
#endif
         return 0;
      }

      if ( working == 1 )
      {
         if ( !(shmem->command == GET_IQ || shmem->command == TEST_DEV ||
                shmem->command == GET_ID || shmem->command == GET_STAT)   )
         {
            shmem->retVal = 0;
            shmem->fd = deviceHdl;
            shmem->ready = 0;
            shmem->block = 1;
            return -1;
         }
      }

      switch(shmem->command)
      {
         case RELEASE:
            shmem->block  = 0;
            shmem->retVal = 0;
            break;
         case TEST_DEV:
            if ( deviceHdl == -1 )
            {
               /*shmem->retVal = openAndTestDevice(name);*/
               shmem->retVal = openDevice(name,shmem->mode&PROT_D4 ? 0 : 1 );
            }
            else if ( idStr )
            {
               strcpy(shmem->buf,idStr);
               shmem->retVal = 0;
            }
            shmem->block  = 0;
         break;
         case TERMINATE:
         
            if ( deviceHdl > -1 )
            {
               switch(currentAction)
               {
                  case EXCHANGE_ALL:
                     if ( deviceHdl > 0 )
                        doExchange(name,deviceHdl, PROT_D4, 4, 0);
               }
               if ( working == -1 )
               {
#if DEBUG
                  fprintf(stderr,"Received command TERMINATE\n");
#endif
                  /* close all */
                  exitD4(deviceHdl);
               }
            }
         break;
         case RESET_PRT:
            shmem->retVal = resetPrinter(name, mode&PALL );
            shmem->block  = 0;
         break;
         case GET_ID:
            if ( idStr )
            {
               shmem->retVal = 1;
               strcpy(shmem->buf, idStr);
            }
            else
            {
               /* try first with the D4 command, even if the */
               /* command contain prot old                   */
               shmem->retVal = getId(name,  PROT_D4);
               /* if this fail use the non D4 protocol */
               if ( shmem->retVal  )
                  shmem->retVal = getId(name,  mode&PALL);
               if ( shmem->retVal == 0)
               {
                  if ( idStr )
                  {
                     free(idStr);
                  }
                  idStr = strdup(shmem->buf);
               }
            } 
            shmem->block = 0;
         break;
         case GET_IQ:
            shmem->retVal = getIq(name,  mode&PALL);
            shmem->block = 0;
         break;
         case CHECK_NOZZLE:
            shmem->retVal = checkNozzle(name,  mode&PALL);
#if 1
            if ( shmem->choice )
            {
               sleep(10);
               shmem->retVal = resetPrinter(name, mode&PALL );
            }
#endif
            shmem->block = 0;
         break;
         case CLEAN_NOZZLE:
            shmem->retVal = cleanNozzle(name,  mode&PALL);
            shmem->block = 0;
         break;
         case ALIGN_HEAD:
             shmem->retVal = doAlignHead(name, deviceHdl,  mode&PALL, pass, choice, shmem->function );
         break;
         case ALIGN_HEAD_C:
            shmem->retVal = doAlignHeadColor(name, deviceHdl,  mode&PALL, pass, choice);
         break;
         case EXCHANGE_ALL:
            shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_ALL);
         break;
         case EXCHANGE_B:
            shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_B);
         break;
         case EXCHANGE_C:
            shmem->retVal = doExchange(name, deviceHdl, mode&PALL, 0, EXCHANGE_C);
         break;
         case EXCHANGE_NEXT:
            shmem->retVal = doExchange(name, deviceHdl, mode&PALL, pass, 0);
         break;
         default:
           shmem->block  = 0;
           shmem->retVal = 0;
      }
      shmem->ready = 1;
      shmem->fd = deviceHdl;
      if ( errno == ENODEV && idStr )
      {
         free(idStr);
         idStr = NULL;
      }
      return shmem->retVal;
   }
   return 0;
}

/******************************************************
 * Function: setPrinterName()
 *
 * if the -model option is passed we have to build an
 * ID string
 *
 ******************************************************/

void setPrinterName(char *name)
{
   char *s;
   if ( idStr == NULL )
   {
      idStr = (char*)calloc(strlen(name)+20,1);
      if ( idStr )
      {
         sprintf(idStr, "MDL:Stylus %s;",name);
         s = idStr;
         while ( (s = strchr(s, '_')) )
            *s = ' ';
      }
   }
}
#endif

#if 1
#ifdef MACOS
#define RESET_TIMER(ti,oti) { signal(SIGALRM, sig); \
                              memset((void*)&ti,0,sizeof(ti)); \
                              memset((void*)&oti,0,sizeof(oti)); \
                              setitimer( ITIMER_REAL ,&ti, &oti); \
                              doRw = 0; \
                            }

#define SET_TIMER(ti,oti,val) { memset((void*)&ti,0,sizeof(ti)); \
                                memset((void*)&oti,0,sizeof(oti)); \
                                ti.it_value.tv_sec  = val/1000; \
                                ti.it_value.tv_usec = (val%1000)*1000; \
                                setitimer( ITIMER_REAL ,&ti, &oti); \
                                sig = signal(SIGALRM, _sigAlarm); \
                                doRw = 1; \
                              }
#else
#define RESET_TIMER(ti,oti)
#define SET_TIMER(ti,oti,val)
#endif
#endif

/******************************************************
 * Function: openAndTestDevice()
 *
 * Under Linux the lp port may be opened if no printer
 * is attached.
 * 
 * After the port is opened we send an old fashioned id query
 * If the write call is OK the printer is attached and
 * powered on, if not, we return ERROR
 *
 ******************************************************/

static int openAndTestDevice(char *dev )
{
   int len,i;
   int oldTimeOut;
   char iBuf[512];

   if ( deviceHdl != -1 )
   {
      if ( d4Initialized )
      {
         /* exit from D4 */
         exitD4(deviceHdl);
      }
#if DEBUG
      fprintf(stderr,"Close(%d)\n",deviceHdl);
#endif
      close(deviceHdl);
      deviceHdl     = -1;
   }

   if ( deviceHdl == -1 )
   {
#if DEBUG
      fprintf(stderr,"Open %s\n",dev);
#endif
      SET_TIMER(ti,oti,5000);
#ifndef MACOS   
      if ( (deviceHdl = open(dev, O_RDWR|O_NDELAY)) == -1 )
#else
      if ( (deviceHdl = mOpen(dev, O_RDWR)) == -1 )
#endif
      {
         /* Fatal error */
         RESET_TIMER(ti,oti);
#if DEBUG
         fprintf(stderr,"Can't open %s, error %d (%s)\n",dev, errno,strerror(errno));
#endif
         return -1;
      }
      RESET_TIMER(ti,oti);

#if DEBUG
      fprintf(stderr,"openAndTestDevice() Open %s -> %d\n",dev,deviceHdl);
#endif
     /* read all information remaining in the printer */

      oldTimeOut = d4RdTimeout;
      d4RdTimeout = 200;
      for(i = 1; i > 0;)
      {
         i = _Read(deviceHdl, (uc*)iBuf, 511, 500);
      }
      d4RdTimeout = oldTimeOut;
   }

   len = _Write(deviceHdl, (uc*)"\033\1@EJL ID\r\n",11, 500);
   if ( len != 11 )
   {
#if DEBUG
         fprintf(stderr,"write on %s failed write %d/11 bytes\n",dev,len);
#endif
      /* fatal error */
      return -1;
   }

   /* wait a little bit */
   usleep(50000);

   /* arm a timer so we will not block */
   for ( i = 0; i < 5; i++ )
   {
#if DEBUG
      fprintf(stderr,"read from %s\n",dev);
#endif

      len = _Read(deviceHdl, (uc*)shmem->buf, 511, 500);
      if ( len > 0 )
         break;
      usleep(50000);
   }

   if ( len == -1 )
   {
#if DEBUG
      fprintf(stderr,"no info from %s\n",dev);
#endif
      /* no information available */
      return 1;
   }

   shmem->buf[len] = 0;
   /* Answer id the printer is in the D4 Mode          */
   /* 0x00 0x00 0x00 0x0a 0x00 0x00 0x7f 0x00 0x00 0x80 */
   if ( shmem->buf[3] == len && shmem->buf[6] == 0x7f ) 
   {
      /* tell that we have a D4 printer */
#if DEBUG
      fprintf(stderr,"D4 device detected on %s\n",dev);
#endif
      return 2;
   }

   /* hope that all is OK */
#if DEBUG
      fprintf(stderr,"printer detected on %s\n",dev);
#endif
   return 0; 
}

/******************************************************
 * Function: openDevice()
 *
 * Test if device file is opened, if not open it
 * 
 *
 ******************************************************/

static int openDevice(char *dev, int doTest )
{
   int i;
   int oldTimeOut;
   char iBuf[512];
#if DEBUG
   fprintf(stderr,"+++ Enter openDevice(%s, %d)\n",dev, doTest);
#endif

   if ( deviceHdl == -1 && doTest )
   {
      i = openAndTestDevice(dev);
      if ( i == 2 )
      {
         /* exit and reenter D4 Mode */
         if ( deviceHdl != -1 )
         {
            exitD4(deviceHdl);
            initializeD4();
         }
         d4Initialized = True;
      }
      if ( i == 1 )
      {
         /* state unknow ! */;
         d4Initialized = True;
      }
      if ( i == 0 )
      {
         /* answer got for get id assume not D4 */;
         d4Initialized = False;
      }
   }
   else /* not tests, open only the printer port */
   {
      if ( deviceHdl == -1 )
      {
#if DEBUG
         fprintf(stderr,"Open %s\n",dev);
#endif
         SET_TIMER(ti,oti,5000);
#ifndef MACOS
         if ( (deviceHdl = open(dev, O_RDWR|O_NDELAY)) == -1 )
#else
         if ( (deviceHdl = mOpen(dev, O_RDWR)) == -1 )
#endif
         {
            /* Fatal error */
            RESET_TIMER(ti,oti);
#if DEBUG
            fprintf(stderr,"Can't open %s, error %d (%s)\n",dev, errno,strerror(errno));
#endif
            return -1;
         }
         RESET_TIMER(ti,oti);
#if DEBUG
         fprintf(stderr,"%s opened (%d)\n",dev, deviceHdl);
#endif
         if ( inFromFile ) /* Test: we read from a file, don't read anythings */
         {
#if DEBUG
            fprintf(stderr,"openDevice() done, not Flush\n");
#endif
            return deviceHdl;
         }
      }
      /* flush the printer output buffer */
#if DEBUG
      fprintf(stderr,"Read old messages from %s\n",dev);
#endif
      oldTimeOut = d4RdTimeout;
      d4RdTimeout = 200;
      for(i = 1; i > 0;)
      {
         i = _Read(deviceHdl, (uc*)iBuf, 511, 500);
      }
      d4RdTimeout = oldTimeOut;
   }
#if DEBUG
   fprintf(stderr,"openDevice() done\n");
#endif
   return deviceHdl;
}

/******************************************************
 * Function: exitD4()
 *
 * send the exit D4 command to the printer
 * 
 *
 ******************************************************/

static int exitD4(int deviceHdl)
{
   int len;

   d4Initialized = False;

   if ( deviceHdl == -1 )
   {
      return -1;
   }

   if ( sockIdData != -1 )
      CloseChannel(deviceHdl,sockIdData);
   if ( sockId != -1 )
      CloseChannel(deviceHdl,sockId);
   sockIdData = sockId = -1;

   len = _Write(deviceHdl, (uc*)"\0\0\0\033\1@EJL 1284.4\n@EJL     \n", 27, 500);
   if ( len != 27 )
   {
      /* write failed */
      return 1;
   }
   /* we have to wait a little bit */
   usleep(100000);

   len = _Read(deviceHdl, (uc*)shmem->buf, 511, 1000);
   if ( len == -1 )
   {
      /* no information available */
#if DEBUG
      fprintf(stderr,"exitD4: nothing read\n");
#endif
      return 1;
   }

   shmem->buf[len] = 0;
 
   /* If the printer expect the D4 Mode, we will */
   /* get an error message (malformed packet)    */
   /* else the printer ID                        */
#if DEBUG
   {
   int i;
   fprintf(stderr,"ExitD4() ");
   for(i=0;i<(len>40)?40:len;i++)
      if ( isprint(shmem->buf[i]) )
         fprintf(stderr,"%c",shmem->buf[i]);
      else
         fprintf(stderr,"0x%02x", (unsigned char)shmem->buf[i] );
   fprintf(stderr,"\n");
   }
#endif  
   if ( len == 10 && (unsigned char)shmem->buf[6] == 0x7f &&
                           (unsigned char)shmem->buf[9] == 0x80 )
      return 0;

   shmem->buf[len] = 0;
   return 0;   
}

/******************************************************
 * Function: getId()
 *
 * Get the ID of attached printer.
 *
 * The called function is dependent of the given protocol
 * 
 *
 ******************************************************/

static int getId(char *name, int prot)
{
   int i;

   if ( deviceHdl == -1 )
   {
      if ( (deviceHdl = openDevice(name, prot&PROT_D4?0:1)) == -1 )
      {
        return -1;
      }
   }

   if ( (prot & PROT_OLD) == PROT_OLD )
   {
      return getIdNoD4(deviceHdl, prot);
   }
   else if ( prot == PROT_D4 )
   {
      if ( ! d4Initialized )
      {
         for ( i = 0; i < 2; i++ )
         {
            if ( initializeD4() )
            {
               if ( i > 0 )
               {
                  return 1;
               }
            }
            else
            {
               break;
            }
         }
      }
      return getIdD4(deviceHdl);
   }    
   return 0;
}

/******************************************************
 * Function: getIq()
 *
 * Get the In quantity of attached printer.
 *
 * The called function is dependent of the given protocol
 * 
 *
 ******************************************************/

static int getIq(char *name, int prot)
{
#if TEST
#if TEST_DEBUG
printf("Call getIqD4(%s, %d)\n".name,prot);
#endif
return getIqD4(deviceHdl);
#endif

   /* This may be the first call from mtink, no autodetection */
   /* if the printer is a D4 model and the ink is low we will */
   /* block the printer if we query for identification with   */
   /* the old code, take account from them                    */
   int doTest = (prot & PROT_D4) ? 0 : 1;

   if ( deviceHdl == -1 )
   {
      if ( (deviceHdl = openDevice(name, doTest)) == -1 )
      {
        return -1;
      }
   }

   if ( deviceHdl != -1 )
   {
      if ( (prot & PROT_OLD) == PROT_OLD )
      {
        return getIqOld(deviceHdl, prot);
      }
      if ( prot == PROT_D4 )
      {
         if ( ! d4Initialized )
            if ( initializeD4() )
               return 1;
         return getIqD4(deviceHdl);
      }
   }
   return 0;
}

/******************************************************
 * Function: resetPrinter()
 *
 * Perform a software reset of the printer.
 *
 * The called function is dependent of the given protocol
 * 
 *
 ******************************************************/

static int resetPrinter(char *name, int prot)
{
   int ret = -1;
   if ( deviceHdl == -1 )
   {
      if ( (deviceHdl = openDevice(name,1)) == -1 )
      {
        return ret;
      }
   }

   if ( (prot & PROT_OLD) == PROT_OLD )
   {
      ret = resetPrinterOld(deviceHdl, prot);
   }
   else if ( prot == PROT_D4 )
   {
      if ( ! d4Initialized )
         if ( initializeD4() )
            return 1;

      ret = d4Reset(deviceHdl);
   }

   return 0;
}


/******************************************************
 * Function: checkNozzle()
 *
 * Call the check nozzle function of the printer
 *
 * The called function is dependent of the given protocol
 * 
 *
 ******************************************************/

static int checkNozzle(char *name, int prot)
{
   if ( deviceHdl == -1 )
   {
      if ( (deviceHdl = openDevice(name,1)) == -1 )
      {
        return -1;
      }
   }
   
   if ( (prot & PROT_OLD) == PROT_OLD )
   {
      return checkNozzleOld(deviceHdl, prot);
   }
   else if ( prot == PROT_D4 )
   {
      if ( ! d4Initialized )
         if ( initializeD4() )
            return 1;
      
      return d4CheckNozzle(deviceHdl);
   }

   return 0;
}

/******************************************************
 * Function: cleanNozzle()
 *
 * Call the clean nozzle function of the printer
 *
 * The called function is dependent of the given protocol
 * 
 *
 ******************************************************/

static int cleanNozzle(char *name, int prot)
{
   if ( deviceHdl == -1 )
   {
      if ( (deviceHdl = openDevice(name,1)) == -1 )
      {
        return -1;
      }
   }
   
   if ( (prot & PROT_OLD) == PROT_OLD )
   {
      return cleanNozzleOld(deviceHdl, prot);
   }
   else if ( prot == PROT_D4 )
   {
      if ( ! d4Initialized )
         if ( initializeD4() )
            return 1;

      return d4CleanNozzle(deviceHdl);
   }
   
   return 0;
}

/******************************************************
 * Function: tellPrinterState()
 *
 * Try to get the printer state value from the passed
 * buffer, return the state if found (and put the value
 * into the shared memory)
 * else return "unknown"
 *
 ******************************************************/

static int tellPrinterState(char *buf)
{
   int i;
   while ( *buf )
   {
      if ( buf[0] == 'S' && buf[1] == 'T' && buf[2] == ':' )
      {
         i = atoi(buf+3);
         if ( shmem )
         {
            shmem->printerState = i;
         } 
         return i;
      }
      buf++;
   }
   return 7;
}
 
/******************************************************
 * Function: initializeD4()
 *
 * enter into the D4 packet mode and make the initialisation
 * works
 *
 * return 0 if OK 1 on problems
 *
 ******************************************************/

int initializeD4()
{
   unsigned char iBuf[65536];
   int     oldTimeOut;
   int     i;

   /* try to enter the IEEE conversation */
   if ( ! EnterIEEE(deviceHdl) )
   {
#if DEBUG
      fprintf(stderr,"EnterIEEE Terminate with error\n");
#endif
      return(1);
   }

   if ( ! Init(deviceHdl) )
   {
#if DEBUG
      fprintf(stderr,"Init Terminate with error\n");
#endif
      return(1);
   }
   /* get the socket numbet for the EPSON-Control channel */
   sockId = GetSocketID(deviceHdl, "EPSON-CTRL");
   
   if ( sockId == 0 )
   {
#if DEBUG
      fprintf(stderr,"Close(%d)\n", deviceHdl);
#endif
      close(deviceHdl);
      deviceHdl = -1;
      return 1;
   }

   /* open ctrl channel credit for receiving of data = 0 */
   switch ( OpenChannel(deviceHdl, sockId, &sndSize, &rcvSize) )
   {
      case -1:
#if DEBUG
         fprintf(stderr,"Fatal Error return 1\n");
#endif
         return 1; /* unrecoverable error */
         break;
      case  0:
#if DEBUG
         fprintf(stderr, "Error\n"); /* recoverable error ? */
#endif
         return 1;
         break;
   }

   if ( !inFromFile )
   {
      /* flush */
      oldTimeOut = d4RdTimeout;
      d4RdTimeout = 200;
      for (i=1;i>0;)
      {
         i = readData(deviceHdl,sockId, (uc*)iBuf, 511);
         if ( !inFromFile ) break;
      }
      d4RdTimeout = oldTimeOut;
   }
   else
   {
      /* read the next answer (credit) which what issued bey the */
      /* above call of readData() in normal mode                 */
      devRead(deviceHdl, (uc*)iBuf, 511, 0);
   }
   d4Initialized = True;

   return 0;
}

/******************************************************
 * Function: getIdNoD4()
 *
 * Get the device identification using old code
 * 
 *
 ******************************************************/

static int getIdNoD4(int deviceHdl, int prot)
{
   int len;
   int i;
 
   if ( (prot & PROT_EXIT) )
      exitD4(deviceHdl);

   len = _Write(deviceHdl, (uc*)"\033\1@EJL ID\r\n", 11, 500);
   if ( len != 11 )
   {
#if DEBUG
      fprintf(stderr,"write failed (%d/11 bytes)\n",len);
#endif
      return 1;
   }   
   /* we have to wait a little bit */
   usleep(100000);

   /* arm a timer so we will not block */
   for ( i = 0; i < 5; i++ )
   {
      len = _Read(deviceHdl, (uc*)shmem->buf, 511, WRTIMEOUT);
      if ( len > 0 )
         break;
      usleep(100000);
   } 

   if ( len == -1 )
   {
      /* no information available */
      return 1;
   }

   shmem->buf[len] = 0;
   
   return 0;
}

/******************************************************
 * Function: getIqOld()
 *
 * Get the device identification using old code
 * 
 *
 ******************************************************/

static int getIqOld(int deviceHdl, int prot)
{
   int len;
   int i;

   if  ( (prot & PROT_EXIT )  )
   {
      exitD4(deviceHdl);
   }

   len = _Write(deviceHdl, (uc*)"\033@\033@\033(R\010\0\0REMOTE1IQ\1\0\1\033\0\0\0", 26, 500);
   if ( len != 26 )
   {
      return 1;
   }   

   /* we have to wait a little bit */
   usleep(100000);

   for ( i = 0; i < 5; i++ )
   {
      len = _Read(deviceHdl,(uc*) shmem->buf, 511, 500);
      if ( len > 0 )
         break;
      usleep(1000000);
   } 
   if ( len == -1 )
   {
      /* no information available */
      return 1;
   }

   shmem->buf[len] = 0;

   return 0;
}

/******************************************************
 * Function: resetPrinterOld()
 *
 * send the remote software reset command 
 * 
 *
 ******************************************************/
static unsigned char resetCommand []= {
   "\033@\033@\033(R\010\0\0REMOTE1RS\1\0\1\033\0\0\0" };

static int resetPrinterOld(int deviceHdl, int prot)
{
   int len;
   int  resetCommandLen = 26;

   if  (  (prot & PROT_EXIT )  )
      exitD4(deviceHdl);


   if ( !(prot & PROT_D4) )
   {
      len = _Write(deviceHdl, resetCommand, resetCommandLen, 500);
      if ( len != 26 )
      {
         return 1;
      }
   }
   else
   {
       if ( ! d4Initialized )
          if ( initializeD4() )
             return 1;
      len = writeD4Data(deviceHdl,resetCommand,resetCommandLen,1);
      if ( len != 26 )
      {
         return 1;
      }
   }

   sleep(3);

   return 0;
}

/******************************************************
 * Function: checkNozzleOld()
 *
 * send the remote software nozzle cjeck command 
 * 
 *
 ******************************************************/

static int checkNozzleOld(int deviceHdl, int prot)
{
   int len;

   /* id was required before */
   if  ( (prot & PROT_EXIT )  )
      exitD4(deviceHdl);

   len = _Write(deviceHdl,
             (uc*)"\033@\033@\033(R\010\0\0REMOTE1NC\2\0\0\0\033\0\0\0\f", 28, 500);
   if ( len != 28 )
   {
      return 1;
   }   

   sleep(3);
  
   return 0;
}

/******************************************************
 * Function: cleanNozzleOld()
 *
 * send the remote software nozzle cjeck command 
 * 
 *
 ******************************************************/

static int cleanNozzleOld(int deviceHdl, int prot)
{
   int len;

   /* id was required before */
   if  ( (prot & PROT_EXIT)  )
      exitD4(deviceHdl);

   len = _Write(deviceHdl, (uc*)"\033@\033@\033(R\010\0\0REMOTE1CH\2\0\0\0\033\0\0\0", 27, 500);
   if ( len != 27 )
   {
      return 1;
   }   

   sleep(5);
   
   return 0;
}

/******************************************************
 * Function: getID4()
 *
 * Get the device identification using D4 mode
 * 
 *
 ******************************************************/

static int getIdD4(int deviceHdl)
{
   int credit;
   int i;
   int ret = 1;
   shmem->buf[0] = '\0';

   credit = askForCredit(deviceHdl, sockId, &sndSize, &rcvSize);
   if ( credit > -1 )
   {
      /* request status command */
      if ( (i=writeData(deviceHdl, sockId, (uc*)"di\1\0\1", 5, 1)) > 0 )
      {
readAgain:
         if ( ( i = readData(deviceHdl, sockId, (uc*)shmem->buf, 511) ) > -1 )
         {
            shmem->buf[i] = '\0';
            if ( strncmp("di", shmem->buf, 2) )
            {
               /* not di */
               if ( strncmp("@E", shmem->buf, 2) )
               {
                  /* not @ E*/
                  /* wrong message returned, command from an older command */
                  goto readAgain;
               }
            }
            shmem->buf[i] = '\0';
            ret = 0;
         }
         else
         {
            ret = 1;
         }
      }
      else
      {
         ret = 1;
      }
   }
   else
   {
      ret = 1;
   }

   return ret;
}

/******************************************************
 * Function: getColorModel()
 *
 * Look for color found in the IQ data field and
 * return the color model we have found
 *
 ******************************************************/

char getColorModel(char *s, int count)
{
   int K = 0;
   int C = 0;
   int M = 0;
   int y = 0;
   int c = 0;
   int m = 0;
   int Y = 0;
   int R = 0;
   int B = 0;
   int X = 0;
   int k = 0;
   int g = 0;

   int idx;
   for ( idx = 0; idx < count; idx++ )
   {
       switch(*s)
       {
          case  0:
             switch(idx)
             {
                 case 3:  k++; break;
                 case 4:  k++; break;
                 case 5:  k++; break;
                 case 12: g++; break;
                 default: K++; break;
             }
          break;
          case  1: C++; break;
          case  2: M++; break;
          case  3: y++; break;
          case  4: c++; break;
          case  5: m++; break;
          case  6: Y++; break;
          case  7: k++; break;
          case  9: R++; break;
          case 10: B++; break;
          case 11: X++; break;
          case 12: g++; break;
       }
       s += 3;
   }

   if ( R && B && X && k ) return MODEL_KCMyRBkX;
   else if ( g && k ) return MODEL_KCMycmGg;
   else if ( R && B && X ) return MODEL_KCMyRBX;
   else if ( R && B ) return MODEL_KCMyRB;
   else if ( Y )      return MODEL_KCMycmY;
   else if ( k )      return MODEL_KCMycmk;
   else if ( c )      return MODEL_KCMycm;
   else               return MODEL_KCMy;
}

/******************************************************
 * Function: convertToAsciiSt()
 *
 * Convert the binary format to the ascii format
 *
 * The message look as follow:
 *
 * '@', 'B', 'D', 'C', ' ', 'S', 'T', '2', 0x0d, 0x0a
 * Tag  Cnt  Dat1 Dat2 ....
 * Tag  Cnt  Dat1 Dat2 ....
 * Tag  Cnt  Dat1 Dat2 ....
 * ...
 *
 * Tag is 1 for ST,  2 for ER 0x0f for IC
 * Cnt is the length for data bytes
 * 
 * For ER the first data byte is the binary value of old Hex value
 * For ST  ""     ""
 *
 *  Index for value is index of Tag + 2
 *
 * For IC Cnt Dat1 ... is as follow
 *
 *   IQ: data follow the ST and Error fields and are
 *   Cnt / 3 = number of inks
 *   Tag Cnt ? ? ? ? K ? ? ? C ? ? ? M ? ? ? ...
 *   
 *   INK: data as follow:
 *   Tag Cnt ? ? ? I ? ? ? I ? ? ? I ? ? ?          
 *
 *   The string value according to the field is for 0 ... 7
 *   1101, 3202, 4304, 5408, 6210, 7320, 1101, 9440
 *
 *  Stream read from a C84
 *   Off 00   40 42 44 43 20 53 54 32 0d 0a  
 *             @  B  D  C     S  T  2
 *       10   21 00
 *
 *       12   01 01 04
 *            ST:   OK
 *       15   0b 05 02 00 00 00 00
 *
 *       22   0f 0d 03 01 00 38 03 01 40 04 02 40 05 03 41
 *            IC:         ik  K    ic  C    im  M    iy  Y   
 *       37   10 03 01 0c 4e
 *
 *       42   13 01 01
 *
 *    the color is given by the bytes marked ik, ic, ...
 *    the floowing inks are known:
 *      0x00 black / Mate Black / Photo Black
 *      0x01 Cyan
 *      0x02 Magenta
 *      0x03 Yellow
 *      0x04 light Cyan
 *      0x05 Light Magenta
 *      0x06 Dark Yellow
 *      0x07 Light Black
 *      0x09 Red
 *      0x0a Blue
 *      0x0b Clear
 *      0x0c light light black (Photo R2400)
 *
 *    The different Black are recognised according to there position
 *    eg;
 *                    0         1        2        3        4
 *        of xx xx xx 00 vv xx 01 vv xx 02 vv xx 00 vv xx 00 vv
 *     Pos 0 -> normal black, pos 3 = Matte Black pos 4 = Photo Black
 *
 ******************************************************/

void convertToAsciiSt(char *buf, int len)
{
    unsigned char tmp[1024];
    int  i;
    int  j;
    int  cnt;
    int  val[8];
    char *s = buf+7;
    char colorModel;
    int K = 0;  /* Black */
    int C = 0;  /* Cyan  */
    int M = 0;  /* Magenta */
    int y = 0;  /* light yellow */
    int c = 0;  /* light cyan */
    int m = 0;  /* light magenta */
    int Y = 0;  /* Yellow */
    int R = 0;  /* Red */
    int B = 0;  /* Blue */
    int X = 0;  /* Gloss */
    int k = 0;  /* matte/light grey */
    int g = 0;  /* light light grey */

    /* save the data to a temporary buffer so we can */
    /* modify the input buffer                       */
    memcpy(tmp, buf, len);
    *s = '\0';
    strcat(s,"\r\n");
    s += 2;
    buf[len] = '\0';
    memset(val,0xff,sizeof(val));
    for (i = 10; i < len; )
    {
#if TEST_DEBUG
printf("Tag = %02x length %d\n", (unsigned char)tmp[i],tmp[i+1]);
#endif
       switch(tmp[i])
       {
          case 1: /* printer status */
             sprintf(s, "ST:%02x;",tmp[i+2]);
             s += 6;
             break;
          case 2: /* printer error */
             sprintf(s, "ER:%02x;",tmp[i+2]);
             s += 6;
             break;
          case 15: /* ink quantity */
             sprintf(s, "INQ:");
             s += 4;
             cnt = tmp[i+1] / 3;
#if TEST_DEBUG
printf("%d inks\n",cnt);
#endif
             *s = colorModel = getColorModel((char*)tmp+i+4, cnt);
#if TEST_DEBUG
printf("%d inks model %c\n",cnt,*s);
#endif
             s++;
             memset(val,-1, sizeof(val));
             /* make shure that color order is OK */
             /* MODELS ARE: KCMy      '1'    colorcode 0123
                            KCMycm    '2'              012345
                            KCMycmY   '3'              0123456
                            KCMycmk   '4'              01239A
                            KCMyRB    '5'              01239AB
                            KCMyRBX   '6'              01239ABX
                            KCMyRBkX  '7'              01239AB0X
                            KCMycmGg  '8'              012359Ag
             */
             for ( j = 0; j < cnt; j++ )
             {
#if TEST_DEBUG
printf("Color code st pos %d = %d val=%d\n",j,tmp[i+4+(3*j)], tmp[i+5+(3*j)]);
#endif
                switch(tmp[i+4+(3*j)])
                {
                   case  0: /* black       at pos 0 */
                            /* or 4                 */
                      if ( j == 3 ) /* black */
                      {
                         K = (unsigned char)tmp[i+5+(3*j)];
                      }
                      else if ( j == 4 && colorModel != MODEL_KCMycmGg )
                      {                    /* black on R2400     */
                         /* photo black on R800 */
                         k = (unsigned char)tmp[i+5+(3*j)];
                      }
                      else
                      {
                         K = (unsigned char)tmp[i+5+(3*j)];
                      }
                   break;
                   case  1: /* cyan        at pos 1 */
                       C = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case  2: /* magenta     at pos 2 */
                       M = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case  3: /* yellow      at pos 3 */
                       y = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case  4: /* lcyan       at pos 4 */
                       c = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case  5: /* lmagenta    at pos 5 */
                       m = tmp[i+5+(3*j)];
                   break;
                   case  6: /* dark yellow at pos 6 */
                       Y = tmp[i+5+(3*j)];
                   break;
                   case  7: /* grey        at pos 6 */
                   case  8: /* grey        at pos 3 */
                       k = (unsigned char)(unsigned char)tmp[i+5+(3*j)];
                   break;
                   case  9: /* red         at pos 4 */
                       R = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case 10: /* blue        at pos 5 */
                       B = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case 11: /* gloss       at pos 6 or 7 */
                       X = (unsigned char)tmp[i+5+(3*j)];
                   break;
                   case 12: /*ligth light black pos 0 */
                       g = (unsigned char)tmp[i+5+(3*j)];
                   break;
                }
             }

             switch (colorModel )
             {
                case MODEL_KCMycm:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", c); s += 2;
                   sprintf(s, "%02x", m); s += 2;
                break;
                case MODEL_KCMycmY:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", c); s += 2;
                   sprintf(s, "%02x", m); s += 2;
                   sprintf(s, "%02x", Y); s += 2;
                break;
                case MODEL_KCMycmk:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", c); s += 2;
                   sprintf(s, "%02x", m); s += 2;
                   sprintf(s, "%02x", k); s += 2;
                break;
                case MODEL_KCMyRB:
                   *s++ = K;
                   *s++ = C;
                   *s++ = M;
                   *s++ = y;
                   *s++ = B;
                break;
                case MODEL_KCMyRBX:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", R); s += 2;
                   sprintf(s, "%02x", B); s += 2;
                   sprintf(s, "%02x", X); s += 2;
                break;
                case MODEL_KCMyRBkX:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", R); s += 2;
                   sprintf(s, "%02x", B); s += 2;
                   sprintf(s, "%02x", k); s += 2;
                   sprintf(s, "%02x", X); s += 2;
                break;
                case MODEL_KCMycmGg:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                   sprintf(s, "%02x", c); s += 2;
                   sprintf(s, "%02x", m); s += 2;
                   sprintf(s, "%02x", k); s += 2;
                   sprintf(s, "%02x", g); s += 2;
                case MODEL_KCMy:
                default:
                   sprintf(s, "%02x", K); s += 2;
                   sprintf(s, "%02x", C); s += 2;
                   sprintf(s, "%02x", M); s += 2;
                   sprintf(s, "%02x", y); s += 2;
                break;
             }
             strcat(s,";");
             s++;
             break;
       }
#if TEST_DEBUG
printf("\n");
#endif
       i += tmp[i+1] + 2;
    }
}

/******************************************************
 * Function: getIq4()
 *
 * Get the ink quantity and the printer state.
 * Use the D4 packet mode.
 *
 ******************************************************/

static int getIqD4(int deviceHdl)
{
   int credit;
   int i;
   int ret = 1;
   shmem->buf[0] = '\0';


   credit = askForCredit(deviceHdl, sockId, &sndSize, &rcvSize);
   if ( credit > -1 )
   {
      /* request status command */
      if ( (i=writeData(deviceHdl, sockId, (uc*)"st\1\0\1", 5, 1)) > 0 )
      {
readAgain:
         if ( ( i = readData(deviceHdl, sockId, (uc*)shmem->buf, 511) ) > -1 )
         {
            if ( strncmp("st", shmem->buf, 2) )
            {
               /* not st, may be good */
               if  (strncmp("@B", shmem->buf, 2) )
               {
                  /* not @B, is wrong */
                  /* wrong message returned, command from an older command */
                  goto readAgain;
               }
               else /* "@BCD ST ST"  found check/convert to old format */
               {
                   if ( shmem->buf[7] == '2' )
                   {
                      /* new binary format ! */
                      convertToAsciiSt(shmem->buf,i);
                   }
                   else
                      shmem->buf[i] = '\0';
               }
            }

            ret = 0;
            /* shmem->buf[i] = '\0';*/
         }
         else
         {
            ret = 1;
         }
      }
      else
      {
         ret = 1;
      }
   }
   else
   {
      ret = 1;
   }
   return ret;
}

/******************************************************
 * Function: d4Reset()
 *
 * Send the D4 reset command to the printer.
 *
 ******************************************************/

static int d4Reset(int deviceHdl)
{
   int credit;
   int i;
   int ret = 1;
   int dataChannelOpen = 0;
   shmem->buf[0] = '\0';

   /* may be we had a print job before */
   if ( sockIdData > -1 )
   {
      /* close the data channel */
      CloseChannel(deviceHdl, sockIdData );
      sockIdData       = -1;
      dataChannelOpen  = 1;
   }
 
   credit = askForCredit(deviceHdl, sockId, &sndSize, &rcvSize);
   if ( credit > -1 )
   {
      /* request status command */
      if ( (i=writeData(deviceHdl, sockId, (uc*)"rs\1\0\1", 5, 1)) > 0 )
      {
         if ( ( i = readData(deviceHdl, sockId, (uc*)shmem->buf, 511) ) > -1 )
         {
            shmem->buf[i] = '\0';
            ret = 0;
         }
         else
         {
            ret = 1;
         }
      }
      else
      {
         ret = 1;
      }
   }
   else
   {
      ret = 1;
   }

#if WAIT_FOR_PRINTER
   /* ask for the printer state and leave the loop */
   /* if the printer tell us that it is OK         */
   waitForPrinterReady(deviceHdl, 5);
#endif

   return ret;
}

/******************************************************
 * Function: d4CheckNozzle()
 *
 * Send the D4 check nozzle command to the printer.
 *
 ******************************************************/

static int d4CheckNozzle(int deviceHdl)
{
   int credit;
   int i;
   int ret = 1;

   shmem->buf[0] = '\0';

   credit = askForCredit(deviceHdl, sockId, &sndSize, &rcvSize);
   if ( credit > -1 )
   {
      /* request status command */
      if ( (i=writeData(deviceHdl, sockId, (uc*)"nc\1\0\0", 5, 1)) > 0 )
      {
         if ( ( i = readData(deviceHdl, sockId, (uc*)shmem->buf, 511) ) > -1 )
         {
            shmem->buf[i] = '\0';
            ret = 0;
         }
         else
         {
            ret = 1;
         }
      }
      else
      {
         ret = 1;
      }
   }
   else
   {
      ret = 1;
   }

   return ret;
}

/******************************************************
 * Function: d4CleanNozzle()
 *
 * Send the D4 clean head command to the printer.
 *
 ******************************************************/

static int d4CleanNozzle(int deviceHdl)
{
   int credit;
   int i;
   int ret = 1;

   shmem->buf[0] = '\0';
   
   credit = askForCredit(deviceHdl, (uc)sockId, &sndSize, &rcvSize);
   if ( credit > -1 )
   {
      /* request status command */
      if ( (i=writeData(deviceHdl, (uc)sockId, (uc*)"ch\1\0\1", 5, 1)) > 0 )
      {
         if ( ( i = readData(deviceHdl, sockId, (uc*)shmem->buf, 511) ) > -1 )
         {
            shmem->buf[i] = '\0';
            ret = 0;
         }
         else
         {
            ret = 1;
         }
      }
      else
      {
         ret = 1;
      }
   }
   else
   {
      ret = 1;
   }
    return ret;
}

/******************************************************
 * Function: doExchange()
 *
 * exchange cartridge.
 *
 ******************************************************/

static int doExchange(char *name, int deviceHdl, int prot, int step, int choice)
{
   int            ret = 1;
   unsigned char *buf = NULL;
   int            len = 0;
   unsigned char  returnValue[50];
   int            i;

   if ( deviceHdl == -1 )
   {
      if ( name == NULL || (deviceHdl = openDevice(name,0)) == -1 )
      {
        return -1;
      }
   }

   if ( (prot & PROT_D4) == 0 )
   {
      shmem->ready = 1;
      return 1;
   }

   if ( ! d4Initialized )
   {
      for ( i = 0; i < 2; i++ )
      {
         if ( initializeD4()  )
         {
            if ( i > 0 )
            {
               shmem->ready = 1;
               return 1;
            }
         }
         else
         {
            break;
         }
      }
   }

   switch (step)
   {
      case 0: /* start ExchangeInk choice */
         buf = exchangeInk;
         len = sizeof(exchangeInk);
         currentAction = EXCHANGE_ALL;
         switch(choice)
         {
            case EXCHANGE_B:
               exchangeInk[CMD_IDX] = BlackInkPosition;
            break;
            case EXCHANGE_C:
               exchangeInk[CMD_IDX] = ColorInkPosition;
            break;
            case EXCHANGE_ALL:
               exchangeInk[CMD_IDX] = AllInkPosition;
            break;
            default:
               shmem->ready = 1;
               return 1;
         }               
      break;
      case 1:  /* wait For CarriagePosition cx:01 */       
         sleep(5);
         /* waitFor CarriagePosition cx:01 */
         for (;;)
         {
            len = sizeof(carriagePosition);
            askForCredit(deviceHdl,sockId,  &sndSize, &rcvSize);
            writeData(deviceHdl, sockId, carriagePosition, len, 1);
            readData(deviceHdl, sockId, returnValue, sizeof(returnValue)-1 );
            if ( strncmp("cx:01;", (char*)returnValue,5) == 0 )
            {
                break;
            }
            /* this will hoppely not happen, but if the user ... */
            if ( ! connectedOnServer && getppid() == 1 )
            {
               parentDie(0);
               break;
            }
            usleep(5000);
         }
      break;
      case 3: /* ExchangeInk Start  (fill up), cartridge was changed */
         exchangeInk[CMD_IDX] = StartInkFillUp;
         len =  sizeof(exchangeInk);
         askForCredit(deviceHdl,sockId,  &sndSize, &rcvSize);
         i = writeData(deviceHdl, sockId, exchangeInk, len, 1);
         readData(deviceHdl, sockId, returnValue, sizeof(returnValue)-1 );
         sleep(5);
         /* waitFor PrinterStatus ST:04 */
         waitForPrinterReady( deviceHdl, 30);
         /* this will hoppely not happen, but if the user ... */
         if ( ! connectedOnServer && getppid() == 1 )
         {
            parentDie(0);
            break;
         }
      break;
      case 4: /* ExchangeInkTerminate */
         buf = exchangeInkTerminate;
         len = sizeof(exchangeInkTerminate);
      break;

   }
   
   /* write command */
   if ( buf )
   {
      askForCredit(deviceHdl,sockId,  &sndSize, &rcvSize);
      if ( writeData(deviceHdl, sockId, buf, len, 1) == len )
      {
         if ( (i = readData(deviceHdl, (uc)sockId, (uc*)shmem->buf, 511)) > 0 )
         {
            shmem->buf[i] = '\0';
            /* check if operation is OK */
            if (  shmem->buf[6] == 'N' )
               ret = 1;
            else
               ret = 0;
         }
         else
            ret = 1;
      }
      else
         ret = 1;
   }
   shmem->ready = 1;
   if ( step == 4 ) currentAction = 0;
   return ret;
}

/******************************************************
 * Function: waitForPrinterReady()
 *
 * wait for the printer ready state.
 *
 ******************************************************/

static void waitForPrinterReady(int deviceHdl, int maxWaitTime)
{
   time_t t=time(NULL) + maxWaitTime;
   int oldRdTimeout = d4RdTimeout;
   int oldWrTimeout = d4WrTimeout;

   oldRdTimeout = 200000;
   oldWrTimeout = 200000;

   while (t > time(NULL) )
   {
      *shmem->buf = 0;
      getIqD4(deviceHdl);
      if ( tellPrinterState(shmem->buf) == 4 )
         break;
      sleep(2);
      if ( ! connectedOnServer && getppid() == 1 )
      {
         parentDie(0);
         break;
      }
   }
   d4RdTimeout = oldRdTimeout;
   d4WrTimeout = oldWrTimeout;
}

/******************************************************
 * Function: doAlignHead()
 *
 * handle the head alignement.
 *
 ******************************************************/

static int doAlignHead(char *name, int deviceHdl, int prot, int pass, int choice, char *function)
{
   int   i  = 0;
   int   pos;
   int   toWrite;
   int   len;
   int   stop = 0;
   int   cnt;

   if ( deviceHdl == -1 )
   {
/*     if ( name == NULL || (deviceHdl = openDevice(name,1)) == -1 )*/
      if ( name == NULL || (deviceHdl = openDevice(name,0)) == -1 )
      {
         fprintf(stderr,"doAlignHead Error 1\n");
         return -1;
      }
   }

   bufpos = 0;

   /* print pattern */
   if ( choice == 0 )
   {
      switch(pass)
      {
         case 1:
         case 2:
         case 3:
         case -3: /* 3 pass printed out all at the same time */
            if (  *function == '\0' )
            {
               bufpos = add_newlines(7 * (pass - 1), bufpos, printer_cmd);
               bufpos = do_remote_cmd(bufpos, printer_cmd, "DT", 3, 0, pass - 1, 0);
               bufpos = add_formfeed(bufpos, printer_cmd);
            }
            else if ( *function == '/' )
            {
               bufpos = interpretFile(function, bufpos, printer_cmd, sizeof(printer_cmd));
            }
            else if ( strcmp(function, "Pattern820") == 0 )
            {
               bufpos = doAlignPattern820(pass, bufpos, printer_cmd);
            }
            else if ( strcmp(function, "PatternC82") == 0 )
            {
               bufpos = doAlignPatternC82(pass, bufpos, printer_cmd);
            }
            break;         
         default: /* save */
            if (  ! function || (function && *function != '/') )
            {
               bufpos = do_remote_cmd(bufpos, printer_cmd, "SV", 0);
            }
            else
            {
                bufpos = do_remote_cmd_new(bufpos, printer_cmd, "JE", 1, 0 ,0);
                printer_cmd[bufpos++] = 0x1b;
                printer_cmd[bufpos++] = '@';
                printer_cmd[bufpos++] = 0x1b;
                printer_cmd[bufpos++] = '@';
                bufpos = do_remote_cmd_new(bufpos, printer_cmd, "SV", 0);
            }
      }
   }
   else if ( choice > 0 )
   {
      if ( pass > -1 )
      {
         /* set choice as stated by the user */
         bufpos = do_remote_cmd(bufpos, printer_cmd,"DA", 4, 0, pass - 1, 0, choice);
      }
      else
      { 
         bufpos = do_remote_cmd_new(bufpos, printer_cmd, "LC", 2, 0, 0, 0);
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] = '@';
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] = '@';
         bufpos = do_remote_cmd_new(bufpos, printer_cmd, "JE", 1, 0 ,0);
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] = '@';
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] = '@';
         bufpos = do_remote_cmd_first(bufpos, printer_cmd,"DA", 4, 0, 0, 0,  choice&0xff);
         bufpos = do_remote_cmd_add(bufpos, printer_cmd,  "DA", 4, 0, 1, 0, (choice>>8)&0xff);
         bufpos = do_remote_cmd_last(bufpos, printer_cmd, "DA", 4, 0, 2, 0, (choice>>16)&0xff);
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] = '@';
         printer_cmd[bufpos++] = 0x1b;
         printer_cmd[bufpos++] =  '@';
      }
   }
   else /* choice < 0, final test */
   {
      if ( ! function )
      {
         bufpos = add_newlines(7 * pass, bufpos, printer_cmd);
         for ( i = 0; i < pass; i++ )
         {
            bufpos = do_remote_cmd(bufpos, printer_cmd,"DT", 3, 0,i , 0);
         }
         bufpos = add_formfeed(bufpos,printer_cmd);
      }
      else if ( *function == '/' )
      {
         bufpos = interpretFile(function, bufpos, printer_cmd, sizeof(printer_cmd));		
      }
      else if ( strcmp(function, "Pattern820") == 0 )
      {
         bufpos = doAlignPattern820(0, bufpos, printer_cmd);
      }
      else if ( strcmp(function, "PatternC82") == 0 )
      {
         bufpos = doAlignPatternC82(0, bufpos, printer_cmd);
      }
      stop = 1;
   }

   if ( prot & PROT_D4 )
   {
      if ( ! d4Initialized )
      {
         for ( i = 0; i < 2; i++ )
         {
            if ( initializeD4() )
            {
               if (i > 0 )
               {
                  return 1;
               }
            }
            else
            {
               break;
            }
         }
      }
      i = writeD4Data(deviceHdl, printer_cmd, bufpos, stop);
#if WAIT_FOR_PRINTER
      waitForPrinterReady(deviceHdl, 30);
#endif
      return i > 0 ? 0 : 1;
   }
   else
   {
      if ( d4Initialized )
      {
         exitD4(deviceHdl);
      }

      len = bufpos;
      pos = 0;
      cnt = 0;
      while (len > 0 )
      {
         toWrite = len > 1024 ? 1024 : len;
         i = _Write(deviceHdl, (uc*)printer_cmd + pos, toWrite,WRTIMEOUT);
         if ( i > 0 )
         {
            pos += i;
            len -= i;
         }
         else if ( i < 0 && errno != EAGAIN)
         {
            break;
         }
         else if ( i < 0 )
         {
             cnt++;
             usleep(100000);
             if ( cnt == 60 )
                return -1;
         }
      }
      return i > 0 ? 0 : -1;
   }
}

/********************************************************************
 * Function doAlignHeadColor()
 *
 * handle the color head alignment
 * 
 *
 ********************************************************************/

static int doAlignHeadColor(char *name, int deviceHdl, int prot, int pass, int choice)
{
   int   i;
   int   stop = 0;
   bufpos = 0;

   if ( choice == 0 )
   {
      switch(pass)
      {
         case 1:
         case 2:
         case 3:
            bufpos = add_newlines(7 * (pass - 1), bufpos, printer_cmd);
            bufpos = do_remote_cmd(bufpos, printer_cmd, "DU", 6, 0, pass, 0, 9, 0, pass-1);
            bufpos = add_formfeed(bufpos, printer_cmd);
            break;
         default: /* save */
            bufpos = do_remote_cmd(bufpos, printer_cmd, "SV", 0);
      }
   }
   else if ( choice > 0 )
   {
      /* set choice as stated by the user */
      bufpos = do_remote_cmd(bufpos, printer_cmd, "DA", 6, 0, 0, 0, choice, 9, 0);
   }
   else
   {
      stop = 1;
   }

   if ( deviceHdl == -1 )
   {
      if ( name == NULL || (deviceHdl = openDevice(name,1)) == -1 )
      {
        return -1;
      }
   }

   /* choose the write function */

   if ( prot & PROT_D4 )
   {
      if ( ! d4Initialized )
      {
         for ( i = 0; i < 2; i++ )
         {
            if ( initializeD4() )
            {
               if ( i > 0 )
               {
                 return 1;
               }
            }
            else
            {
               break;
            }
         }
      }
      return writeD4Data(deviceHdl,  printer_cmd, bufpos,stop);
   }
   else
   {
      if ( d4Initialized )
      {
         exitD4(deviceHdl);
      }
      i = _Write(deviceHdl, (uc*)printer_cmd, bufpos, WRTIMEOUT);
      return i;
   }

}

/********************************************************************
 * Function writeD4Data()
 *
 * write the data contained in printer_cmd with len bufpos
 * with the D4 protocol via the data channel
 *
 ********************************************************************/

int writeD4Data(int deviceHdl, unsigned char *data, int len, int stop)
{
   int  ret = -1; /* assume error */
   int  toWrite;
   int  pos = 0;

   if ( sockIdData == -1 )
   {
      sockIdData = GetSocketID(deviceHdl, "EPSON-DATA");
      if ( sockIdData > 0 )
      {
         if ( OpenChannel(deviceHdl, sockIdData, &sndSizeData, &rcvSizeData) > 0 )
         {
            ret = 0;
         }
      }
   }
   else
   {
      ret = 0;
   }
 
   if ( len == 0 )
   {
      shmem->dataSocketId = sockIdData;
      return sndSizeData;
   }

   while (len > 0 && ret != -1 )
   {
      if ( ret >= 0 && !dataCredit )
      {
         dataCredit = askForCredit(deviceHdl, sockIdData, &sndSize, &rcvSize);
      }

      if ( ret >= 0 && dataCredit > 0 )
      {
         toWrite = len > (sndSizeData-6) ? (sndSizeData-6) : len;
         ret = writeData(deviceHdl,sockIdData,data+pos,toWrite, stop);

         if ( ret > 0 )
         {
            len -= toWrite;
            pos += toWrite;
            ret = pos;
         }
         else
         {
            return -1;
         }
         dataCredit--;
      }
   }

   return ret;
}
