/* mtinkd.c 
 * 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.
 */

/* d4Print.c allow to print data via the EPSON D4 protocol.
 *
 * The data to be printed are to be passed via a pipe and must
 * be "printer code".
 * d4Print donīt modify the datas which will only be packed
 * int the EPSON D4 protocol
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//#ifdef MACOS
//#include "fakepoll.h"
//#else
#include <sys/poll.h>
//#endif
#include <dirent.h>
#include <sys/ioctl.h>

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

#include "mtink.h"
#ifndef DEVICE
#define DEVICE "/dev/lp0"
#endif

static int      terminate = 0;
static int      debug     = 0;
static char    *prgName;
static char    *fifoName = NULL;
extern shmem_t *shmem;
int             shmid = -1;
char           *printerName = NULL;
char           *runFile     = NULL;
configData_t    actConfig; /* not used now */

/* the printers listed here are known as no D4-able printers */
char *noD4Printers[] =
{
   "Color",
   "Color 1500",
   "Color 1520",
   "Color 300",
   "Color 3000",
   "Color 400",
   "Color 440",
   "Color 500",
   "Color 600",
   "Color 640",
   "Color 800",
   "Color 850",
   "Color Pro",
   "Color Pro XL",
   "Photo",
   "Photo 700", 
   "Photo EX",
   "Pro 5000",
   "Pro 5500",
   "Pro 7000",
   "Pro 7500",
   "Pro 9000",
   "Pro 9500",
   NULL,
};

/*******************************************************************/
/* Function checkForD4able                                         */
/*                                                                 */
/* scan the table noD4Printers, if match return 0 else return 1    */
/*                                                                 */
/* Input:  char *name printer ID                                   */
/*                                                                 */
/* Return: 0/1                                                     */
/*                                                                 */
/*******************************************************************/
 
 int checkForD4able(char *name)
 {
    char **model = noD4Printers;
    char *s;
    int   l;
    if ( name == NULL || *name == '\0' )
    {
       /* assume this is OK */
       return 1;
    }

    while (*model)
    {
       if ( (s = strstr(name, *model)) )
       {
          l = strlen(*model);
          if ( s[l] == ';' )
          {
             return 0;
          }
       }
       model++;
    }
    return 1;
 }
 
/*******************************************************************/
/* Function makeRunFile                                            */
/*                                                                 */
/* create the file /var/run/mtink/<name> and write the pid into it */
/*                                                                 */
/* Input:  char *fifo file                                         */
/*                                                                 */
/* Return: -                                                       */
/*                                                                 */
/*******************************************************************/

void makeRunFile(char *fifo, int create)
{
   char           *s;
   FILE           *fp;
   int             um;
   if ( (s = strrchr(fifo, '/')) )
   {
      runFile = (char*)calloc(strlen("/var/run/mtink/") + strlen(s), 1);
      sprintf(runFile,"/var/run/mtink/%s", s+1);
      if ( debug )
      {
         fprintf(stderr,"makeRunFile(%s, create=%d)\n",fifo, create);
      }
      if ( create )
      {
         if ( access("/var/run", O_RDONLY) == -1 )
         {
            um = umask(0);
            if ( mkdir("/var/run", 0700) == -1 )
            {
               perror("mkdir ");
               if ( debug )
               {
                  fprintf(stderr,"can't create directory /var/run\n");
               }
               exit(1);
            }
            umask(um);
         }
         if ( access("/var/run/mtink", O_RDONLY) == -1 )
         {
            um = umask(0);
            if ( mkdir("/var/run/mtink", 0600) == -1 )
            {
               perror("mkdir ");
               if ( debug )
               {
                  fprintf(stderr,"can't create directory /var/run/mtink\n");
               }
               exit(1);
            }
            umask(um);
         }
         if ( (fp = fopen(runFile, "w")) )
         {
             fprintf(fp,"%d\n", getpid());
             fclose(fp);
         }
      }
   }
}

/*******************************************************************/
/* Function killServer                                             */
/*                                                                 */
/* tell the server that he has to abort                            */
/*                                                                 */
/* Input:  char *fifo file                                         */
/*                                                                 */
/* Return: 0 -> OK 1 = error                                       */
/*                                                                 */
/*******************************************************************/

int killServer(char *fifo)
{
   key_t           key;
   struct shmid_ds buf;
   int             shmid;
   FILE           *fp;
   char            ibuf[100];
   int             pid;

   if ( fifo == 0 )
   {
      return 1;
   }
   else
   {
      key = ftok(fifo, 'M');
      if ( key > -1 )
      {
         if ((shmid = shmget(key, sizeof(shmem_t), 0)) > -1)
         {
            if ( shmctl(shmid, IPC_STAT, &buf) > -1 )
           {
               if ( debug )
               {
                  fprintf(stderr,"Kill process %d\n",buf.shm_cpid);
               }
               if ( kill(buf.shm_cpid, SIGINT) == -1 )
               {
                  return 1;
               }
            }
         }
      }
      else
      {
         /* look for pid into /var/run/mtink(<fifo> */
         if ( (fp = fopen(runFile,"r")) )
         {
            if ( fgets(ibuf, sizeof(buf), fp) )
            {
               if ( (pid = atoi(ibuf)) )
               {
                  if ( debug )
                  {
                     fprintf(stderr,"Kill process %d\n",pid);
                  }
                  if ( kill(pid, SIGINT) == -1 )
                  {
                     return 1;
                  }
               }
            }
            fclose(fp);
         }
      }
   }
   
   return 0;
}

/*******************************************************************/
/* Function getServerState                                         */
/*                                                                 */
/* tell the server that he has to abort                            */
/*                                                                 */
/* Input:  char *fifo file                                         */
/*                                                                 */
/* Return: 0 -> OK 1 = error                                       */
/*                                                                 */
/*******************************************************************/

int getServerState(char *fifo)
{
   key_t           key;
   struct shmid_ds buf;
   int             shmid;

   if ( fifo == NULL )
   {
      return 1; /* not running */
   }
   else
   {
      key = ftok(fifo, 'M');
      if ( key > -1 )
      {
         if ((shmid = shmget(key, sizeof(shmem_t), 0)) > -1)
         {
            if ( shmctl(shmid, IPC_STAT, &buf) > -1 )
            {
               return 0;
            }
         }
      }
      else
      {
         if ( runFile && access(runFile, O_RDONLY) > -1 )
         {
            return 0;
         }
      }
   }
   
   return 1;
}

/*******************************************************************/
/* Function decodePrinterType                                      */
/*                                                                 */
/* decode the string returned from printer and print the           */
/* informations in an human readable way                           */
/*                                                                 */
/* Input: unsigned char *buf The String returned from printer      */
/*        int            len Size of string                        */
/*                                                                 */
/* Return: 0 if errro 1 if found                                   */
/*                                                                 */
/*******************************************************************/

static int decodePrinterType(char *buf, int len)
{
   char *s = buf;
   char *t = buf;
   int   i;

   if ( debug )
   {
      fprintf(stderr,"decode:\n%s\n",buf);
   }
   if ( buf )
   {
      for (i=0; i < len; i++)
      {
         if ( strncmp(s, "DES:", 4) == 0 || strncmp(s, "MDL:", 4) == 0 )
         {
            s +=4;
            t = s;
            while(*t && *t != ';')
               t++;
            *t = '\0';
            printerName = strdup(s);
            *t = ';';
            return 1;
         }
         else
         {
            s++;
         }
      }
   }
   return 0;
}

/*******************************************************************/
/* Function terminatePrg()                                         */
/*        delete the shared memory and exit with passed code       */
/*                                                                 */
/* Input:   int exitCode                                           */
/*                                                                 */
/*                                                                 */
/* Output:  -                                                      */
/*                                                                 */
/* Return:  -                                                      */
/*                                                                 */
/*******************************************************************/

static void terminatePrg(int code)
{
   struct shmid_ds buf;

   if ( debug )
      fprintf(stderr,"Received signal %d\n",code);

   if ( fifoName && code > -2 )
      unlink(fifoName);
   if ( runFile )
      unlink(runFile); 
   if ( debug )
      fprintf(stderr,"Handle shm\n");
   if ( shmem )
   {
      /* close D4 */
      shmem->command = TERMINATE;      
      if ( debug )
         fprintf(stderr,"Process %d call doCommand(-1)\n", getpid());
      doCommand(-1);
      if ( debug )
         fprintf(stderr,"Process %d doCommand(-1) returned \n", getpid());

      shmdt((void*)shmem);
      shmctl(shmid, IPC_RMID, &buf);
   }
   exit(1);
}

/*******************************************************************/
/* Function createIpc()                                            */
/*        create the IPC shared memory and semaphore               */
/*                                                                 */
/* Input:  char *fifo name of fifo used for generation of key      */
/*                                                                 */
/*                                                                 */
/* Output:  -                                                      */
/*                                                                 */
/* Return:  0 if OK -1 if wrong                                    */
/*                                                                 */
/*******************************************************************/

static int createIpc(char *fifo, int mode, uid_t uid, gid_t gid)
{
   key_t  key;
   int    ret = -1;
   struct shmid_ds buf;

   if ( debug )
   {
      fprintf(stderr,"createIpc(%s,...)\n",fifo);
   }
   
   /* create the key */
   key = ftok(fifo, 'M');
   if ( key > -1 )
   {
      if ((shmid = shmget(key, sizeof(shmem_t), IPC_CREAT|mode)) > -1)
      {
         if ( shmctl(shmid, IPC_STAT, &buf) > -1 )
         {
            /* if there are attached processes, die */
            if ( buf.shm_nattch )
            {
               fprintf(stderr,"attached processes\n");
               return -2;
            }

            buf.shm_perm.uid = uid;
            buf.shm_perm.gid = gid;
            if ( shmctl(shmid, IPC_SET, &buf) == -1 )
            {
               perror("shmctl ");
            }
            else
            {
               shmem = (shmem_t*)shmat(shmid, NULL, 0);
               if ( shmem != NULL )
               {
                  shmem->ready = 0;
                  ret = 0;
               }
               else
               {
                  perror("shmat ");
               }
            }
         }
         else
         {
            perror("shmctl ");
            
         }
      }
      else
      {
         perror("shmget ");
      }
   }
   else
   {
      perror("ftok ");
   }

   if ( ret == -1 )
   {
      /* cleanup */
      if ( shmid > -1 )
      {
         shmctl(shmid, IPC_RMID, &buf);
      }
   }
   return ret;
}

/*******************************************************************/
/* Function readFromStdin()                                        */
/*        read the data which are to be printed out                */
/*                                                                 */
/* Input:  unsigned char *iBuf where to put the datas              */
/*         int            size of buffer                           */
/*                                                                 */
/* Output: int            *stop  if the stream is closed set to 1  */
/*                                                                 */
/* Return:  The number oc bytes read                               */
/*                                                                 */
/*******************************************************************/

static int readFromStdin(int in, unsigned char *iBuf, int size, int *stop)
{
   int rd = 0;
   int got;
   struct  pollfd pfd[1];
   int ret;

   *stop = 0;
   while ( rd < size )
   {
      pfd[0].fd      = in;
      pfd[0].events  = POLLIN|POLLHUP;
      pfd[0].revents = 0;

      ret = poll(pfd, 1, 1000);

      if ( ret == 0 )
      {
         return rd;
      }

      if ( ret == 1 )
      {
         got = read(in, iBuf+rd, size-rd);

         if ( got > 0 )
         {
            rd += got;
         }
      }

      if ( ret == -1 || (pfd[0].revents & POLLHUP) )
      {
         *stop = 1;
         break;
      }
   }

   return rd;
}

/*******************************************************************/
/* Function strCNCmp()                                             */
/*                                                                 */
/* compare the id string returned by the ioctl call with the name  */
/* provided by the user. the last cgaracter for the id string shall*/
/* be as ;                                                         */
/* input: char *id   id string from printer                        */
/*        char *t    name provided by the user                     */
/*        int   len  length of name                                */
/*                                                                 */
/* return: 0 if match, != 0 if no match                            */
/*                                                                 */
/*******************************************************************/

int strCNCmp(char *id, char *t, int len)
{
   int i;
   int ret = 0;
   for ( i = 0; i < len && ! ret; i++)
   {
      if ( id[i] == ' ' )
      {
         if ( t[i] == ' ' || t[i] == '_' )
            ret = 0;
         else
            ret = 1;
      }
      else
         ret = id[i] - t[i];
   }
   if ( !ret )
   {
      ret = id[len] - ';';
   }
   return ret;
}

/*******************************************************************/
/* Function checkPrinterDes()                                      */
/*                                                                 */
/* scan the id string against the passed name                      */
/*                                                                 */
/* input: char *id    id string from printer                       */
/*        char *type  wanted printer name                          */
/*                                                                 */
/* return: 0 if not found 1 if OK                                  */
/*                                                                 */
/*******************************************************************/

int checkPrinterDes(char *id, char *type)
{
   int len = 0;
   
   if ( debug && type )
   {
      fprintf(stderr,"Search <%s>\n",type);
   }
   if ( type  ) len = strlen(type);
   while ( type && (id = strchr(id, *type)) )
   {
      if ( strCNCmp(id,type,len) == 0 )
      {
         if ( debug )
         {
            fprintf(stderr,"Printer detected\n");
         }
         return 1;
      }
      id++;
   }
   if ( type == NULL )
      return 1;
   return 0;
}

#ifdef linux
/*******************************************************************/
/* Function checkPrinter()                                         */
/*                                                                 */
/* Ask the usb lp file for the id string                           */
/* This work only with recent linux kernel.                        */
/* If the wanted printer is detected set the device variable       */
/*                                                                 */
/* input: char *dir       directory containing the dev-file        */
/*        char *dev                                                */
/*        char *type                                               */
/* output char **device                                            */
/*                                                                 */
/* Return: 0 not found, 1 found, -1 ioctl not supported            */
/*                                                                 */
/*******************************************************************/

/* based on code from Till Kamppeter */
#define IOCNR_GET_DEVICE_ID	1
#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)	/* get device_id string */

int checkPrinter(char *dir, char *dev, char *type, char **device)
{
   char *fn;
   int fd;
   unsigned char argp[1024];
   int length;

   /* build file name */
   if ((fn = calloc(strlen(dir) + strlen(dev) + 2, 1)) == NULL )
   {
      /* no memory */
      terminatePrg(1);
   }

   sprintf(fn,"%s/%s", dir, dev);
   if ( debug )
   {
      fprintf(stderr, "Check %s\n",fn);
   }

   /* open device file */
   fd = open (fn, O_RDWR);

   if (fd < 0)
   {
      if ( debug )
      {
         fprintf(stderr,"can't open %s\n",fn);
         perror("open");
      }
      free(fn);
      return 0;
   }
   
   /* ask the kernel for the id string */
   if (ioctl (fd, LPIOC_GET_DEVICE_ID(sizeof(argp)-1), argp) < 0)
   {
      /* failed, assume ioctl function not supported */
      close(fd);
      free(fn);
      fprintf(stderr,"mtinkd: IOCTL not supported\n");
      return -1;
   }
   close(fd);

   /* check the is string against the wanted printer type */
   length = (argp[0] << 8) + argp[1];
   
   /* make sure that we will not write behind the end of buffer */
   if ( length  > sizeof(argp)-1)
   {
      length = sizeof(argp)-1;
   }
   argp[length] = '\0';

   if (checkPrinterDes((char*)argp+1, type) == 1)
   {
      *device = fn;
      return 1;
   }

   free(fn);
   return 0;
}

/*******************************************************************/
/* Function waitForPrinter()                                       */
/*                                                                 */
/* scan the device directory for printer device files              */
/*                                                                 */
/* input:  char  *base   directory +base name of printer dev-file  */
/*         char  *type   wanted printer type                       */
/* output: char **device filled with device name                   */
/*                                                                 */        
/* return: 0 fatal error, 1 printer found, -1 ioctl not OK         */
/*                                                                 */
/*******************************************************************/

static int waitForPrinter(char *base, char *type, char **device)
{
   DIR           *dir;
   struct dirent *ent;
   char          *s;
   int            ret;

   /* get dir and name part */
   if ( base )
   {
      s = strrchr(base,'/');
      if ( s == NULL )
         return 0;
      *s = '\0';
   }
   else
   {
      return 0;
   }
   
   /* open the directory */
   dir = opendir(base);

   if ( dir == NULL )
   {
      *s = '/';
      return 0;
   }

   /* and scan the directory against wanted printer type */
   for (;;)
   {
      while ( (ent = readdir(dir)) != NULL )
      {
         /* device name match with the given name prefix ? */
         if ( strncmp(s+1, ent->d_name, strlen(s+1)) == 0 )
         {
            /* test for given printer type             */
            /* if printer found close dir and return 1 */
            /* and set also device to the propervalue  */
            if ( (ret = checkPrinter(base, ent->d_name, type, device)) )
            {
               /* at this stage 2 possibilities: 1 -> OK -1 -> ioctl failed */
               *s = '/';
               closedir(dir);
               return ret;
            }
         }
      }
      sleep(2);
      rewinddir(dir);
   }
}
#endif

/*******************************************************************/
/* Function main()                                                 */
/*        The begin of the world                                   */
/*                                                                 */
/* Syntax: d4Print [device file]                                   */
/*                                                                 */
/*******************************************************************/

int main(int argc, char **argv)
{
   int             fd;
   unsigned char   iBuf[0xffff];
   int             sockId    = 0;
   int             len;
   int             sndSize   = 0x1006; /* 8K buffer + prot data */
   int             i;
   int             in = fileno(stdin);
   int             stop = 0;
   char           *device    = DEVICE;
   int             daemon    = 1;
   struct          pollfd pollfd[2];
   char           *s;
   struct          stat sBuf;
   int             um = 0;
   char           *model = NULL;
   int             state;
   char           *retBuf       = NULL;
   char            printerFound = 0;
   int             d4Entered    = 0;
   int             block;
   int             restart      = 0;
   int             mode         = 0660;
   int             detectCount  = 0;
   int             startMode    = 1;
   char           *type         = NULL; /* option name */
#ifdef linux
   char           *usbbase      = NULL;
   char           *oldDevice    = NULL;
#endif

   prgName = strchr(argv[0], '/');
   if ( prgName != NULL )
   {
      prgName++;
   }
   else
   {
      prgName = argv[0];
   }

   argc--;
   argv++;
   while ( argc )
   {
      if ( strcmp(argv[0], "start") == 0 )
      {
         startMode = 1;
      }
      else if ( strcmp(argv[0], "stop") == 0 )
      {
         startMode = 0;
      }
      else if ( strcmp(argv[0], "status") == 0 )
      {
         startMode = 2;
      }
      else if ( strcmp(argv[0], "-debug") == 0 )
      {
         debug++;
      }
      else if ( strcmp(argv[0], "-nodaemon") == 0 )
      {
         daemon = 0;
      }
      else if ( strcmp(argv[0], "-L") == 0 )
      {
         debugD4 = 1;
      }
      else if ( strcmp(argv[0], "-dev") == 0 )
      {
         argc--;
         argv++;
         if ( argc )
         {
            device = argv[0];
         }
         else
         {
            fprintf(stderr,"argument expected for option -dev\n");
            exit(1);
         }
      }
      else if ( strcmp(argv[0], "-model") == 0 )
      {
         argc--;
         argv++;
         if ( argc )
         {
            model = argv[0];
         }
         else
         {
            fprintf(stderr,"argument expected for option -model\n");
            exit(1);
         }
      }
      else if ( strcmp(argv[0], "-name") == 0 )
      {
         argc--;
         argv++;
         if ( argc )
         {
            type = argv[0];
         }
         else
         {
            fprintf(stderr,"argument expected for option -name\n");
            exit(1);
         }
      }
#ifdef linux
      else if ( strcmp(argv[0], "-usbbase") == 0 )
      {
         argc--;
         argv++;
         if ( argc )
         {
            usbbase = argv[0];
         }
         else
         {
            fprintf(stderr,"argument expected for option -usbbase\n");
            exit(1);
         }
      }
#endif
      argc--;
      argv++;
   }

   /* build fifo name */
#ifdef linux
   if ( usbbase && type )
   {
      oldDevice = device;
      device    = NULL;
   }
#endif
   if ( type )
   {
      fifoName  = (char*)calloc(strlen(type)+ 20, 1);
      sprintf(fifoName, "/var/mtink/%s",type);
   }
   else
   {
#ifdef linux
      if ( usbbase )
         if ( waitForPrinter(usbbase, type, &device) == -1 )
            device = oldDevice;
#endif
      fifoName = calloc(strlen(device)+ 7, 1);
      sprintf(fifoName, "/var/mtink/%s",device+5);
   }
   
   /* replace / with _ and also blancks */
   s = fifoName + 11;
   while ( *s )
   {
      if ( *s == '/' ) *s = '_';
      if ( *s == ' ' ) *s = '_';
      s++;
   }
   if ( debug )
   {
      fprintf(stderr,"Fifo name is <%s>\n",fifoName);
   }
   
   if ( startMode != 1 )
   {
      makeRunFile(fifoName, 0);
      if ( startMode == 0 )
      {
         i = killServer(fifoName);
      }
      else
      {
         i = getServerState(fifoName);
      }
      exit(i);
   }
   
   /* become a daemon */
   if ( daemon )
   {
      if ( fork() ) exit (0);
      setsid ();   
      if ( fork() ) exit (0);
      close (0); open ("/dev/null", O_RDONLY);
      close (1); open ("/dev/null", O_WRONLY);

      if ( debug )
      {
         fprintf(stderr,"%s running as daemon\n", prgName);
      }
   }

   /* create our directory if not existing */
   if ( access("/var/mtink", O_RDONLY) == -1 )
   {
      um = umask(0);
      if ( mkdir("/var/mtink", 0700) == -1 )
      {
         perror("mkdir ");
         if ( debug )
         {
            fprintf(stderr,"can't create directory /var/mtink\n");
         }
         exit(1);
      }
      umask(um);
      chown(fifoName, 0, 0);
   }

   /* create write our pid to the /var/run/mtink/<fifo> file */
   makeRunFile(fifoName, 1);

   /* if we are  killed, cleanup first */
   signal(SIGTERM, terminatePrg);
   signal(SIGINT,  terminatePrg);
   signal(SIGQUIT, terminatePrg);

   len = mknod(fifoName, mode|S_IFIFO, 0);
   if ( len == -1 )
   {
      if ( errno != EEXIST )
      {
         perror("mknod ");
         if ( debug )
         {
            fprintf(stderr, "can't create pipe %s\n",fifoName);
         }
         exit(1);
      }
   }
   /* create the shared memory */
   if ( (i = createIpc(fifoName, sBuf.st_mode,sBuf.st_uid, sBuf.st_gid)) <= -1 )
   {
      perror("create IPC");
      terminatePrg(i);
   }
   
#ifdef linux
   /* look for all device usbbase'*' */
   /* if the type is OK continue     */

   if ( usbbase && type )
   {
      if ( device )
         free(device);
      device = NULL;

      if ( debug )
      {
         fprintf(stderr,"wait for printer on %s*\n",usbbase);
      }
      if ( ! waitForPrinter(usbbase, type, &device) )
      {
         terminatePrg(1);
      }
   }
   
   /* at this stage device may be NULL (ioctl call not supported */
   if ( oldDevice )
   {
      if ( device == NULL )
      {
         device = strdup(oldDevice);
         usbbase = NULL; /* ioctl don't work */
      }
   }
   
   /* device must have a correct value */
   if ( ! device )
   {
      if ( type )
      {
         fprintf(stderr,"mtinkd: device not found or kernel to old\n");
      }
      terminatePrg(1);
   }
#endif
 
   /* wait for device file, may be it is created dynamically */
   /* Linux dev FS                                           */
   while ( stat(device, &sBuf) == -1 )
   {
      if ( debug )
      {
         perror("stat ");
      }
      sleep(2);
   }

   if ( debug )
   {
       fprintf(stderr,"found device file %s\n",device);
   }

   /* device file is present, get owner, permission */
   /* and create our input pipe                     */
   mode = sBuf.st_mode &(S_IRWXO|S_IRWXG|S_IRWXU);
   um = umask(0);
   umask(um);
   chown(fifoName, sBuf.st_uid, sBuf.st_gid);


   if ( debug )
   {
      fprintf(stderr,"Fifo <%s> created and rigths set\n",fifoName);
   }

   /* open the pipe */
   in = open(fifoName, O_RDONLY|O_NONBLOCK);
   if ( in == -1 )
   {
      perror("open ");
      unlink(fifoName);
      if ( debug )
      {
         fprintf(stderr, "can't open pipe %s\n",fifoName);
      }
      terminatePrg(1);
   }

   detectCount = 0;
   /* enter the overall loop */
   while (!terminate)
   {
      shmem->block=2;
      if ( debug )
      {
         fprintf(stderr,"TOP OF LOOPS\n");
      }

#ifdef linux
      /* look for all device usbbase'*' */
      /* if the type is OK continue     */

      if  ( usbbase && type )
      {
         if ( debug )
         {
            fprintf(stderr, "Linux: usbbase && type \n");
         }
         shmem->block=2;
         if ( device )
         {
            printerFound     = 0;
#if 0 /* this may cause problems if zhe printer has low ink */
            d4Entered        = 0;
            shmem->mode      = NO_PROT;
            shmem->command   = TEST_DEV;
#else
            shmem->mode      = PROT_D4;
            shmem->command   = TEST_DEV;
#endif
            strcpy(shmem->name, device);
            *shmem->function = '\0';
            shmem->choice    = 0;
            shmem->pass      = 0;
            retBuf           = shmem->buf;

            doCommand(-1);
            free(device);
            device = NULL;
         }

         shmem->block=2;
         if ( waitForPrinter(usbbase, type, &device) != 1 )
         {
            terminatePrg(1);
         }
      }
      else
#endif
      {
          if ( debug )
          {
             fprintf(stderr, "!Linux || ! (usbbase && type) \n");
          }
          shmem->block=2;
          printerFound     = 0;
#if 0 /* this may cause problems if zhe printer has low ink */
          d4Entered        = 0;
          shmem->mode      = NO_PROT;
          shmem->command   = TEST_DEV;
#else
          shmem->mode	 = PROT_D4;
          shmem->command   = TEST_DEV;
#endif
          strcpy(shmem->name, device);
          *shmem->function = '\0';
          shmem->choice    = 0;
          shmem->pass      = 0;
          retBuf           = shmem->buf;
          doCommand(-1);
          shmem->block=2;

          /* wait for device */
          if ( debug )
          {
             fprintf(stderr,"Wait for device %s\n",device);
          }

          errno = 0;
          do
          {
             shmem->block=2;
#if 0 /* this may cause problems if zhe printer has low ink */
             shmem->mode      = NO_PROT;
             shmem->command   = TEST_DEV;
#else
             shmem->mode      = PROT_D4;
             shmem->command   = TEST_DEV;
#endif
             strcpy(shmem->name, device);
             *shmem->function = '\0';
             shmem->choice    = 0;
             shmem->pass      = 0;
             retBuf           = shmem->buf;
             /* close the device file if opened */
             doCommand(-1);
             shmem->block=2;
             errno = 0;
             state = doCommand(2);
             shmem->block=2;
             if ( state == -1 )
                usleep(1000000);
          }
          while ( state  == -1 );
      }

      if ( debug )
      {
         fprintf(stderr,"Device %s is available\n",device);
      }

      /* try to get printer model name */
      if ( model == NULL )
      {
         shmem->block=2;
         /* try to detect the printer model */
         errno = 0;
#if 0 /* this may cause problems if the printer has low ink */
         shmem->mode      = NO_PROT;
         shmem->command   = TEST_DEV;
#else
         shmem->mode      = PROT_D4;
         shmem->command   = TEST_DEV;
#endif
         strcpy(shmem->name, device);
         *shmem->function = '\0';
         shmem->choice    = 0;
         shmem->pass      = 0;
         retBuf           = shmem->buf;
         state = doCommand(2);
         shmem->block=2;

         if ( debug )
         {
            fprintf(stderr, "detect printer call 1 -> %d\n", state);
         }
         if ( errno == ENODEV )
         {
            if ( debug )
            {
               fprintf(stderr,"detect printer try 1 error ENODEV\n");
            }
            restart = 1;
            continue;
         }

         /* look for the operation result */
         if ( state == -1 )
         {
            fprintf(stderr,"%s: no access to printer\n", prgName);
            if ( debug )
            {
               fprintf(stderr,"Device %s not available, terminate\n",device);
            }
#ifdef linux
            if ( !usbbase)
#endif
            terminatePrg(1);
         }
         else if ( state == 0 && retBuf)
         {
            /* may be OK, check for model name */
            state = decodePrinterType(retBuf, strlen(retBuf));
            if ( debug )
            {
               fprintf(stderr, "decode printer call 1 -> %d\n", state);
            }
            shmem->ready    = 0;
            if ( state )
            {
               printerFound = 1;
            }
            else
            {
               /* test with D4 exit */
               printerFound = 0;
            }
         }
         else if ( state == 1 )
         {
            /* may be OK but no answer from device */
            printerFound = 2;
         }
         else if ( state == 2 )
         {
            /* may be OK and a D4 device */
            printerFound = 2;
         }
      }
      else
      {
         printerFound = 1;
         printerName  = strdup(model);
         errno        = 0;
         shmem->block=2;
         state = doCommand(2);
         shmem->block=2;
         if ( errno == ENODEV )
         {
            if ( debug )
            {
               fprintf(stderr,"detect printer try 1b error ENODEV\n");
            }
            restart = 1;
            continue;
         }
      }
 
      if ( printerFound == 0 || printerFound == 2 )
      {
         if ( debug )
         {
            fprintf(stderr, "enter detect printer call 2 printerFound == %d\n", printerFound);
         }
         shmem->block=2;
         errno = 0;
         shmem->mode = PROT_EXIT|PROT_OLD;
         shmem->command = GET_ID;

         state = doCommand(2);
         shmem->block=2;

         if ( debug )
         {
            fprintf(stderr, "detect printer call 2 -> %d\n", state);
         }
         if ( errno == ENODEV )
         {
            if ( debug )
            {
               fprintf(stderr, "detect printer call 2 ENODEV ERROR\n");
            }
            restart = 1;
            continue;
         }

         if ( state == 0 && retBuf )
         {
            /* may be OK, check for model name */
            state = decodePrinterType(retBuf, strlen(retBuf));
            if ( debug )
            {
               fprintf(stderr, "decode printer call 2 -> %d\n", state);
            }
            shmem->ready    = 0;
            if ( state )
            {
               printerFound = 1;
            }
            else
            {
               /* test with D4 */
               printerFound = 0;
            }
         }
         else
         {
            /* may be OK and a D4 device */
            printerFound    = 0;
         }
      }

      if ( printerFound == 0 )
      {
         if ( debug )
         {
            fprintf(stderr, "enter detect printer call 3 printerFound == %d\n", printerFound);
         }
         shmem->block=2;
         errno          = 0;
         shmem->mode    = PROT_D4;
         shmem->command = GET_ID;
         state = doCommand(2);
         shmem->block=2;

         if ( debug )
         {
            fprintf(stderr, "detect printer call 3 -> %d\n", state);
         }
         if ( errno == ENODEV )
         {
            if ( debug )
            {
               fprintf(stderr,"detect printer try 3 error ENODEV\n");
            }
            restart = 1;
            continue;
         }

         /* look for the operation result */
         if ( state == 0 && retBuf )
         {
            /* may be OK, check for model name */
            state = decodePrinterType(retBuf, strlen(retBuf));
            shmem->ready = 0;
            if ( debug )
            {
               fprintf(stderr, "decode printer call 3 -> %d\n", state);
            }
            if ( state )
            {
                printerFound = 1;
            }
            else
            {
               /* test with D4 */
               printerFound = 0;
            }
            d4Entered = 1;
         }
         else
         {
            /* not an Epson printer */
            printerFound = 0;
         }
      }

      if ( printerFound == 0 && ! restart )
      {
         if ( detectCount > 20 )
         {
            /* more as 3 minute elapsed, die */
            if ( debug )
            {
               fprintf(stderr,"Printer on %s not detected, terminate\n",device);
            }
#ifdef linux
            usleep(10000000);
            detectCount = 0;
            continue;
#endif
            terminatePrg(1);
         }
         else
         {
            if ( debug )
            {
               fprintf(stderr,"Printer on %s not detected, try again\n",device);
            }
            /* and wait for the next try */
            usleep(10000000);
            detectCount++;
            continue;
         }
      }

      if ( model )
      {
         setPrinterName(model);
         printerFound = 1;
      }
      else if ( type )
      {
         /* check that the printer found is OK */
         if ( !checkPrinterDes(retBuf, type) )
         {
#ifdef linux
            if ( !usbbase )
#endif
            {
               fprintf(stderr,"mtinkd: printer don't match required name\n");
               terminatePrg(1);
            }
         }
      }

      /* check if the model is OK for D4 */
      
      if ( !checkForD4able(model?model:retBuf) )
      {
         fprintf(stderr,"mtinkd: %s is not D4 able\n", model?model:retBuf );
         terminatePrg(1);
      }
      fd = shmem->fd;


      /* make shure that a job is aborted correctly in case */
      /* of "kill"                                          */

      if ( !restart )
      {
         /* may be we will et troubles with this */
         /* make shure that we will not only be able to read from */
         /* a pipe                                                */
         fcntl(in, F_SETFL, O_NONBLOCK);
      }
   
      /* initialize data channel */
      if ( errno != ENODEV && !d4Entered )
      {
         initializeD4();
      }

      shmem->ready = 1;
   
      terminate = 0;
      if (errno != ENODEV )
         restart   = 0;

      /* main loop */
      if ( debug )
      {
         fprintf(stderr,"enter wait for print job loop\n");
      }

      shmem->block = 0;
      while (!terminate && !restart)
      {
         /* At this place ask for incomming data. This may be    */
         /* from stdin or from the shmem.                        */
         /* if the input id from stdin, leave the wait loop      */
         /* and process the print job                            */
         /* If we have a command from the shared memory we will  */
         /* process the command and remain in this loop          */

         while(!terminate && !restart)
         {
            /* check for print job */
            pollfd[0].fd      = in;
            pollfd[0].events  = POLLIN|POLLHUP;
            pollfd[0].revents = 0;
            i = poll(pollfd,1,1000);
         
            if (pollfd[0].revents & POLLIN )
            {
               /* print job detected, initialize print environment */
               terminate  = 0;
               stop       = 0;
               sndSize    = writeD4Data(fd,(uc*)"",0,0);
               sockId     = shmem->dataSocketId;
               if ( sndSize > 0 )
               {
                  sockId = shmem->dataSocketId;
                  /* all is OK leave this loop */
                  break;
               }
            }

            /* some body has detached it from the pipe */
            /* reinitialize the pipe                   */
            if (pollfd[0].revents & POLLHUP )
            {
               close(in);
               in = open(fifoName, O_RDONLY|O_NONBLOCK);
               if ( in == -1 )
               {
                  perror("open ");
                  unlink(fifoName);
                  terminatePrg(1);
               }
               else
               {
                  stop = 0;
               }
            }

            if ( shmem && shmem->ready == 0 )
            {
               /* OK for all commands, a few commands        */
               /* require the completion of the whole        */
               /* sequence !                                 */
               for (block=1; block;)
               {
                   doCommand(0);
                   block = shmem->block;
                   if ( errno == ENODEV )
                   {
                      if ( debug )
                      {
                         fprintf(stderr,"wait for job loop error ENODEV\n");
                      }
                      restart = 1;
                      stop    = 1;
                      break;
                   }
               }

               /* tell that we are ready for processing */
               /* a new command                         */
                shmem->ready = 1;
            }
         } /* end loop process command, detect print job */

         /* print loop */
         if ( debug &&  !stop && ! restart )
         {
            fprintf(stderr,"enter  print job loop\n");
         }
         while ( !stop && ! restart )
         {
            /* get data from  the pipe */
            len = readFromStdin(in,iBuf,sndSize-6, &stop);

            /* write data */
            if ( len > 0 && (i = writeD4Data(fd, iBuf, len, stop)) != len )
            {
               fprintf(stderr,"%s: error while writing datas %d from %d\n",
                       prgName,i,len);
               if ( errno == ENODEV )
               {
                  if ( debug )
                  {
                     fprintf(stderr,"print jon loop error ENODEV\n");
                  }
                  /* printer powered of or diconnected */
                  restart = 1;
               }
               stop = 1; /* leave also the top loop */
            }

            /* if print job done, reinitialize the pipe */
            if ( stop )
            {
               close(in);

               /* avoid garbage if the device file was not OK */
               if ( restart )
               {
                  mode = sBuf.st_mode &(S_IRWXO|S_IRWXG|S_IRWXU);
                  errno = 0;
                  unlink(fifoName);
                  um = umask(0);
                  len = mknod(fifoName, mode|S_IFIFO, 0);
                  if ( len == -1 )
                  {
                     if ( errno != EEXIST )
                     {
                         perror("mknod ");
                         exit(1);
                     }
                  }
                  umask(um);
                  chown(fifoName, sBuf.st_uid, sBuf.st_gid);
                  if ( debug )
                  {
                     fprintf(stderr, "Pipe file %s recreated\n",fifoName);
                  }
              }

               in = open(fifoName, O_RDONLY|O_NONBLOCK);
               if ( in == -1 )
               {
                   perror("open ");
                   unlink(fifoName);
                   if ( debug &&  !stop && ! restart )
                   {
                      fprintf(stderr,"can't reopen pipe %s\n", fifoName);
                   }
                   terminatePrg(1);
               }
               else
               {
                   stop = 0;
               }
               break;
            }

            /* look for incoming request via shared memory */
            if ( shmem && shmem->ready == 0 )
            {
               /* only OK for getink command */
               doCommand(1);
               shmem->ready = 1;
               if ( errno == ENODEV )
               {
                  restart = 1;
               }
            }
         }
         if ( restart)
         {
            break;
         }
      } /* end of main loop */
   } /* end of overall loop, wait for device, open, ... */
   /* here end of the for ever loop */

   return 0; /* make gcc happy */
}
