/*
** Free LaserJet 4 Utility V1.1 (patchlevel 1)
**
** Programm zum Einstellen der Laserdrucker der Familie
** HP LaserJet 4.
**
** program for controlling the functions of the HP LaserJet 4 family
**
** Copyright (C) 1994  F. Tegtmeyer, Rostock (Germany)
**                     E-mail:  fte@gecko.de (Internet)
**
**  Für Deutschland (for Germany):
**
**  Dieses Programm ist freie Software. Sie können das Programm
**  kopieren, vertreiben und verschenken, vorausgesetzt Sie erfüllen
**  die Bestimmungen der "Deutschen Free Software Lizenz Version 1.0",
**  die als Datei dem Programm beiliegen muß.
**
**  Da es sich um freie Software handelt, wird vom Autor keine
**  Garantie für die Funktionsfähigkeit übernommen. Ebenso wird
**  jede Haftung für Schäden, die aus der Anwendung des Programms
**  resultieren ausgeschlossen.
**
**  Sie sollten eine Kopie der Deutschen Free Software Lizenz mit
**  dem Programm erhalten haben. Falls nicht, wenden Sie sich bitte
**  an die FSAG:
**        Free Software Association of Germany
**        Heimatring 19
**        60596 Frankfurt
**
**
**  For all other countries:
**
**  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; (version 2 of the License)
** 
**  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., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "ui_text.h"

#define TRUE  1
#define FALSE 0

/* define a more pragmatic string compare */
#define EQUAL_STRINGS(s1,s2)   ( strcmp ( (s1), (s2) ) == 0 )

/* prototypes */
int int_f  ( char *arg, char **arg_list );
int floatf ( char *arg, char **arg_list );
int listf  ( char *arg, char **arg_list );


/*
** the data section
*/

char *terargs[] =   { "UNIX", "DOS", NULL, "\033&k2G", "\033&k0G" };
char *symargs[] =
   {
   "DESKTOP", "ISO4", "ISO6", "ISO11", "ISO15", "ISO17", "ISO21",
   "ISO60", "ISO69", "ISOL1", "ISOL2", "ISOL5", "LEGAL", "MATH8",
   "MSPUBL", "PC8", "PC8DN", "PC850", "PC852", "PC8TK", "PIFONT",
   "PSMATH", "PSTEXT", "ROMAN8", "VNINTL", "VNMATH", "VNUS",
   "WIN30", "WINL1", "WINL2", "WINL5",
   NULL
   };

char *onoff[]   =   { "ON", "OFF", NULL };
char *onauto[]  =   { "ON", "OFF", "AUTO", NULL };
char *fonargs[] =   { "0", "70" };
char *pitargs[] =   { "0.44", "99.99" };
char *ptsargs[] =   { "0.1", "999.75" };
char *forargs[] =   { "5", "128" };
char *papargs[] =   { "LETTER", "LEGAL", "EXECUTIVE", "COM10",
                      "B5", "A4", "C5", "DL", "MONARCH", NULL };
char *oriargs[] =   { "PORTRAIT", "LANDSCAPE", NULL };
char *copargs[] =   { "1", "999" };
char *retargs[] =   { "OFF", "LIGHT", "MEDIUM", "DARK", NULL };
char *denargs[] =   { "1", "5" };
char *rsnargs[] =   { "300", "600", NULL };
char *outargs[] =   { "UPPER", "LOWER", NULL };

struct sCmd
  {
  char *cpCommand;           /* abbreviated command string */
  int   args;                /* number of required arguments (0, 1) */
  char **arg_list;           /* possible arguments, if given values */
  int (*f) (char *, char **);/* check function for the parameters */
  char *cpData;              /* data for output to printer */
  char *cpFullname;          /* the long name of the command */
  };

static char *cpPCL = "\033%-12345X";

struct sCmd saCmdArray [] =
  {
 /*--------------------------------------------------------------*/
  { "RES", 0, NULL,    NULL,   "RESET",
         "RESET" },
 /*--------------------------------------------------------------*/
  { "EJE", 0, NULL,    NULL,   "@PJL ENTER LANGUAGE = PCL\n\033E",
         "EJECT" },
 /*--------------------------------------------------------------*/
  { "TER", 1, terargs, listf,  NULL,
         "TERMINATION" },
 /*--------------------------------------------------------------*/
  { "SYM", 1, symargs, listf,  "LPARM : PCL SYMSET",
         "SYMSET" },
 /*--------------------------------------------------------------*/
  { "FON", 1, fonargs, int_f,  "LPARM : PCL FONTNUMBER",
         "FONTNUMBER" },
 /*--------------------------------------------------------------*/
  { "PIT", 1, pitargs, floatf, "LPARM : PCL PITCH",
         "PITCH" },
 /*--------------------------------------------------------------*/
  { "PTS", 1, ptsargs, floatf, "LPARM : PCL PTSIZE",
         "PTSIZE" },
 /*--------------------------------------------------------------*/
  { "FOR", 1, forargs, int_f,  "FORMLINES",
         "FORMLINES" },
 /*--------------------------------------------------------------*/
  { "PAP", 1, papargs, listf,  "PAPER",
         "PAPER" },
 /*--------------------------------------------------------------*/
  { "ORI", 1, oriargs, listf,  "ORIENTATION",
         "ORIENTATION" },
 /*--------------------------------------------------------------*/
  { "COP", 1, copargs, int_f,  "COPIES",
         "COPIES" },
 /*--------------------------------------------------------------*/
  { "MAN", 1, onoff,   listf,  "MANUALFEED",
         "MANUALFEED" },
 /*--------------------------------------------------------------*/
  { "AUT", 1, onoff,   listf,  "AUTOCONT",
         "AUTOCONT" },
 /*--------------------------------------------------------------*/
  { "RET", 1, retargs, listf,  "RET",
         "RET" },
 /*--------------------------------------------------------------*/
  { "PAG", 1, onauto,  listf,  "PAGEPROTECT",
         "PAGEPROTECT" },
 /*--------------------------------------------------------------*/
  { "IMA", 1, onauto,  listf,  "IMAGEADAPT",
         "IMAGEADAPT" },
 /*--------------------------------------------------------------*/
  { "DEN", 1, denargs, int_f,  "DENSITY",
         "DENSITY" },
 /*--------------------------------------------------------------*/
  { "RSN", 1, rsnargs, listf,  "RESOLUTION",
         "RESOLUTION" },
 /*--------------------------------------------------------------*/
  { "ECO", 1, onoff,   listf,  "ECONOMODE",
         "ECONOMODE" },
 /*--------------------------------------------------------------*/
  { "JOB", 1, onoff,   listf,  "JOBOFFSET",
         "JOBOFFSET" },
 /*--------------------------------------------------------------*/
  { "OUT", 1, outargs, listf,  "OUTBIN",
         "OUTBIN" },
 /*--------------------------------------------------------------*/
  { "DUP", 1, onoff,   listf,  "DUPLEX",
         "DUPLEX" }
 /*--------------------------------------------------------------*/
  };

#define CMD_NUMBER  (sizeof saCmdArray / sizeof ( struct sCmd ) )


/*
**  This is a very slow but also very portable possibility to
**  change all characters in a string to upper case.
**  Feel free to change it to a faster function on your system.
*/
void upstr ( char *cpString )
{
   static char *lowchars = "abcdefghijklmnopqrstuvwxyz";
   static char *upchars  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   char *pos_lower, *pos_upper;

   while ( *cpString != '\0' )
      {
      pos_lower = lowchars;  pos_upper = upchars;
      while ( *pos_lower != '\0' )
         {
         if ( *pos_lower == *cpString )
            {
            *cpString = *pos_upper;
            break;
            }
         pos_lower++;  pos_upper++;
         }
      cpString++;
      }
}


/*
** usage ()
** displays a message with the possible parameters in the case of error
** or if the program is called with parameter -? or -help
*/

#define FIRST_HELP    0    /* if called withaout parameters */
#define OVERVIEW_HELP 1    /* if called with -? or -help or -h */
#define DETAILED_HELP 2    /* if called with -detailed */

void usage ( int text )
{
   struct sCmd *spCmd = saCmdArray;
   char **cpDescr;
   int i;

switch ( text )
  {
  case FIRST_HELP:
        printf ( TITLE1 );
        break;
  case OVERVIEW_HELP:
        printf ( "\n%s\n", OVERVIEW );
        cpDescr = DESCRIPTIONS;
        for ( i=0; i<CMD_NUMBER; i++, spCmd++, cpDescr++ )
           {
           printf ( "%3s %s\n", spCmd->cpCommand, *cpDescr );
           }
        break;
  case DETAILED_HELP:
        printf ( "\n%s\n", DETAILED );
        cpDescr = DESCRIPTIONS;
        for ( i=0; i<CMD_NUMBER; i++, spCmd++, cpDescr++ )
           {
           printf ("%s\n", DELIMITER);
           printf ( "%-14s (%s)\n", spCmd->cpFullname, spCmd->cpCommand );
           printf ( "%s%s\n", DESCRIPTION, *cpDescr );
           printf ( ARGUMENTS );
           if ( spCmd->f == NULL )    printf ( "%s\n", NO_ARGS );
           else if ( spCmd->f == int_f )
              {
              printf ( INTEGERS,
                       *(spCmd->arg_list), (spCmd->arg_list)[1] );
              printf ("\n");
              }
           else if ( spCmd->f == floatf )
              {
              printf ( FLOATS, *(spCmd->arg_list), (spCmd->arg_list)[1] );
              printf ("\n");
              }
           else if ( spCmd->f == listf )
              {
              char **val = spCmd->arg_list;
              int len, pos;

              printf ( "\n  " );  pos = 2;
              while ( *val != NULL )
                 {
                 len = strlen ( *val );
                 if ( pos + len  >  45 )
                    {
                    printf ( "\n  " );  pos = 2;
                    }
                 printf ( "%s ", *val );   pos += len+1;
                 len = 6 - ((pos-2) % 7); pos += len;
                 while ( len-- )     printf (" ");
                 val ++;
                 }
              printf ( "\n" );
              }
           }
        break;
  }
}

/*
** scan functions for int and float parameters
** they return TRUE in the case of success, FALSE on error
*/
int scan_int ( char *arg, int *result )
{
   char *p = arg;
   int len;

   len = strlen ( arg );
   if ( len < 1  ||  len > 3 )  return FALSE;

   while ( *p != '\0' &&  isdigit(*p) ) p++;

   /* non digit character detected? */
   if ( *p != '\0' )  return FALSE;

   *result = atoi ( arg );
   return TRUE;
}


int scan_float ( char *arg, float *result )
{
   char *p = arg;
   int len;

   len = strlen ( arg );
   if ( len < 1  ||  len > 6 )  return FALSE;

   while ( *p != '.'  &&  *p != '\0' )  p++;

   /* is there a point? */
   if ( *p != '.' )     return FALSE;

   /* are there one or two digits after the point? */
   len = strlen ( p );
   if ( len < 1  ||  len > 2 )  return FALSE;

   *result = atof ( arg );
   if ( *result <= 0.0 )   return FALSE;

   return TRUE;
}


/* check function for the integer parameters */
int int_f ( char *arg, char **arg_list )
{
   int value, low, high;

   if ( scan_int ( arg, &value ) == FALSE )
      return FALSE;
   low   = atoi ( *arg_list++ );
   high  = atoi ( *arg_list );
   return  value >= low  &&  value <= high;
}

/* check function for the float parameters */
int floatf ( char *arg, char **arg_list )
{
   float  value, low, high;

   if ( scan_float ( arg, &value ) == FALSE )
      return FALSE;
   low   = atof ( *arg_list++ );
   high  = atof ( *arg_list );
   return  value >= low  &&  value <= high;
}


/*
** parameter check function for commands with a list of
** possible values ( not value ranges )
** arg_list is a pointer field to the possible values. The
** last pointer must be NULL (like argv)
*/
int listf ( char *arg, char **arg_list )
{
   char **cpSearch = arg_list;

   /* all arguments are internally coded in upper case */
   upstr ( arg );
   while ( *cpSearch != NULL )
      {
      if ( EQUAL_STRINGS ( *cpSearch, arg ) )
         return TRUE;
      cpSearch++;
      }
   return FALSE;
}


/*
** put_data ()    puts the data to stdout
*/
void put_data ( struct sCmd *spCmd, int argc, char *argv[] )
{
  int i;

  /* check the parameter values */
  for ( i=0; i<argc; i++ )
     {
     if ( (*(spCmd->f)) ( argv [i], spCmd->arg_list ) == FALSE )
        {
        printf ( WRONG_PARM, argv[i] );
        printf ( "\n" );
        exit (1);
        }
     }

  /* special cases for ordinary escape sequences */
  if ( spCmd->arg_list == terargs )  /* append more, if nessecary */
     {
     char **cppData = spCmd->arg_list;
     char **cppParm = cppData;
     while ( *cppData != NULL )  cppData++;
     cppData++;
     /* now cppData points to the first data entry */
     while ( ! EQUAL_STRINGS ( *cppParm, *argv ) )
        {
        /* get the next parameter entry */
        cppData++ ;    cppParm++ ;
        } 
     printf ( "%s", *cppData );
     return;
     }

  printf ( "%s@PJL DEFAULT ", cpPCL );
  printf ( argc>0 ? "%s = " : "%s", spCmd->cpData );

  /* print the parameter values */
  for ( i=0; i<argc; i++ )
     {
     printf ( "%s ", argv[i] );
     }

  printf ( "\n%s", cpPCL );
}


/*
**
**  The main program.
**
*/

#ifndef BINDIR
#define BINDIR ""
#endif

int main ( int argc, char *argv[] )
{
   int   iCmdFound;          /* logical value */
   struct sCmd *spCmd;
   char *argv1;
   char path[257];              /* the path for the data file */
                                /* malloc would be better */

   /*
   ** ---------------------------------------------------------
   **  First the construction of the path to the language file.
   **  Note that LINGUA allows language changes during running
   **  of the program. To save space on disk only one language
   **  (default "lj4") is supported during runtime of lj4.
   */

   strcpy ( path, BINDIR );
   strcat ( path, "/" );        /* the only real delimiter :-) */

   strcat ( path, "lj4" );      /* the name of the language file */

   /* load the default language file */
   if ( ui_loadtext ( path, "V1.1p1" ) == 0 )
      {
      printf ("Error loading textfile %s\n", path);
      return 1;
      }

   if ( argc == 1 )
      {
      usage ( FIRST_HELP );
      return 1;
      }

   /* access through a pointer is faster */
   argv1 = argv[1];

   if (                                             /* help */
      strstr ( argv1, "-h" ) == argv1   || 
      strstr ( argv1, "-?" ) == argv1
      )
      {
      usage ( OVERVIEW_HELP );
      return 1;
      }

   if ( strstr ( argv1, "-d" ) == argv1 )        /* detailed help */
      {
      usage ( DETAILED_HELP );
      return 1;
      }

   if ( strstr ( argv1, "-w" ) == argv1 )        /* warranty */
      {
      printf ( WARRANTY );
      return 1;
      }

   if ( strstr ( argv1, "-c" ) == argv1 )        /* conditions for use */
      {
      printf ( CONDITIONS );
      return 1;
      }

   /* for commands only 3 characters are needed */
   argv1[3] = 0;                /* cut the rest */

   spCmd     = saCmdArray;
   iCmdFound = FALSE;

   /* all commands are internally coded in upper case */
   upstr ( argv1 );

   while ( spCmd != ( saCmdArray + CMD_NUMBER ) )
     {
     if ( EQUAL_STRINGS ( spCmd->cpCommand, argv1 ) )
        {
        iCmdFound = TRUE;
        break;
        }
     spCmd++;
     }
   if ( iCmdFound == FALSE )
      {
      printf ( CMDNOTFOUND, argv1 );
      return 1;
      }

   if ( argc - 2  !=  spCmd->args )
      {
      printf ( WRONGNUM1, argv1 );
      printf ( WRONGNUM2, spCmd->args );
      return 1;
      }

   put_data ( spCmd, argc-2, argv+2 );

   return 0;
}

