/*
This product contains certain software code or other information
("AT&T Software") proprietary to AT&T Corp. ("AT&T").  The AT&T
Software is provided to you "AS IS".  YOU ASSUME TOTAL RESPONSIBILITY
AND RISK FOR USE OF THE AT&T SOFTWARE.  AT&T DOES NOT MAKE, AND
EXPRESSLY DISCLAIMS, ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WARRANTIES OF
TITLE OR NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS, ANY
WARRANTIES ARISING BY USAGE OF TRADE, COURSE OF DEALING OR COURSE OF
PERFORMANCE, OR ANY WARRANTY THAT THE AT&T SOFTWARE IS "ERROR FREE" OR
WILL MEET YOUR REQUIREMENTS.

Unless you accept a license to use the AT&T Software, you shall not
reverse compile, disassemble or otherwise reverse engineer this
product to ascertain the source code for any AT&T Software.

(c) AT&T Corp. All rights reserved.  AT&T is a registered trademark of AT&T Corp.

***********************************************************************

History:

      24/11/99  - initial release by Hartmut Liefke, liefke@seas.upenn.edu
                                     Dan Suciu,      suciu@research.att.com
*/

//***********************************************************************
//***********************************************************************

// This module contains the handling of options specified at the command line

#include "stdafx.h"

#include "Types.hpp"
#include "Input.hpp"
#include "VPathExprMan.hpp"

#ifdef XDEMILL
#include "XMLOutput.hpp"
#endif

//**********************************************************
//**********************************************************

// Several auxiliary function for managing 

// We keep a global state while traversing the sequence of options
char  **argstrings;        // The set of option strings
int   argnum,              // The number of option stringd
      curargidx;           // The index of the current option string
char  *curargptr;          // The current pointer into the current option string

char  *optfiledata;        // If we loaded a file, then this pointer refers to the file data
char  *curfileptr;         // The current pointer in the file

inline void InitArguments(char **argv,int argc)
   // Initializes the option reader state above
{
   argstrings=argv;
   argnum=argc;

   curargidx=0;
   curargptr=argstrings[curargidx];

   optfiledata=NULL;
   curfileptr=NULL;
}

inline char *GetNextArgument(int *len)
   // Reads the next argument using the option reader state
   // and stores the (maximal) length (to the next white space) in *len.
   // The actual length of an option can be smaller
{
   do
   {
      // We don't have a file loaded
      if(curfileptr==NULL)
      {
         // Let's skip white spaces
         while((*curargptr==' ')||(*curargptr=='\t')||(*curargptr=='\n')||(*curargptr=='\r'))
            curargptr++;

         // If we have some actual option strings left, then scan up to the
         // next white space
         if(*curargptr!=0)
         {
            char *endptr=curargptr+1;
            while((*endptr!=0)&&
                  (*endptr!=' ')&&(*endptr!='\t')&&
                  (*endptr!='\n')&&(*endptr!='\r'))
               endptr++;

            *len=(endptr-curargptr);
            return curargptr;
         }

         // We didn't find an option string ==> Go to next string
         curargidx++;
         if(curargidx==argnum)   // No more string? => Exit
            return NULL;

         curargptr=argstrings[curargidx];
      }
      else  // If we have a file, then we do something very similar
      {
         // Let's skip white spaces
         while((*curfileptr==' ')||(*curfileptr=='\t')||(*curfileptr=='\n')||(*curfileptr=='\r'))
            curfileptr++;

         if(*curfileptr!=0)
            return curfileptr;

         // Let's get rid of the file data
//         delete[] optfiledata;
         // We keep the file, since in VPathExprMan, the pathstrings
         // are direct pointers into the memory of the file data
         // This is a bad solution!

         curfileptr=NULL;
         curfileptr=NULL;
      }
   }
   while(1);
}

inline void SkipArgumentString(int len)
   // After parsing an option, this function is called to go to string
   // data after the option
{
   if(curfileptr!=NULL)
      curfileptr+=len;
   else
      curargptr+=len;
}

//*********************************************************************

inline void ParseOptionFile(Session *session, char *filename)
   // Loads the option file
{
   Input input(session);
   char  *ptr;
   int   len;

   // We exit, if there is already an option file
   if(optfiledata!=NULL)
   {
      throw new XMillException (XMILL_ERR_ARGUMENTS, "Only one option file allowed!");
   }

   if(input.OpenFile(filename)==0)
   {
      XMillException *e = new XMillException (XMILL_ERR_FILENOTFOUND, "Could not open parameter file '");
      e->ErrorCont(filename);
      e->ErrorCont("'!");
      throw e;
   }

   len=input.GetCurBlockPtr(&ptr);

   if(len>XMILL_MAXFILEBUFSIZE)  // Just to make sure that the file fits into one
                  // single buffer -- i.e. we don't read another block
   {
      XMillException *e = new XMillException (XMILL_ERR_FILETOOLARGE, "Input file '");
      e->ErrorCont(filename);
      e->ErrorCont("' is too large!");
      throw e;
   }

   // Let's allocate and copy the data
   optfiledata=new char[len+1];
   memcpy(optfiledata,ptr,len);
   optfiledata[len]=0;

   curfileptr=optfiledata;

   input.CloseFile();
}

//*********************************************************************

void Settings::InterpretOptionString(char *option)
   // Interprets a specific option
{
   int   len;

   switch(*option)
   {
      // Includes an option file
   case 'i':SkipArgumentString(1);
            option=GetNextArgument(&len);
            if(option[len]!=0)   // white space in file name?
            {
               throw new XMillException (XMILL_ERR_ARGUMENTS, "Invalid filename for option '-f'");
            }
            SkipArgumentString(len);
            ParseOptionFile(session, option);
            return;

   case 'v':   verbose=XMILL_VERBOSE_ALL;SkipArgumentString(1);return;
   case 't':   no_output=1;SkipArgumentString(1);return;
   case 'c':   usestdout=1;SkipArgumentString(1);return;   
//   case 'k':   delete_inputfiles=0;SkipArgumentString(1);return;
   case 'd':   delete_inputfiles=1;SkipArgumentString(1);return;
   case 'f':   overwrite_files=1;SkipArgumentString(1);return;
#ifdef TIMING
   case 'T':   timing=1;SkipArgumentString(1);return; 
#endif

#ifdef XMILL
   case 'z':   use_bzip=XMILL_GPC_GZIP;SkipArgumentString(1);return;
   case 'P':   use_bzip=XMILL_GPC_PPMDI;SkipArgumentString(1);return;
   case 'N':   use_bzip=XMILL_GPC_NONE;SkipArgumentString(1);return;
      // Sets the memory window size
   case 'm':SkipArgumentString(1);
            option=GetNextArgument(&len);
            SkipArgumentString(len);
            session->memory_cutoff=atoi(option);
            if(session->memory_cutoff<1)
            {
               throw new XMillException (XMILL_ERR_ARGUMENTS, "Option '-m' must be followed be a number >=1");
            }
            session->memory_cutoff*=XMILL_MEMORY_CUTOFF_MULT;
            return;

      // Reads a path expression
   case 'p':   SkipArgumentString(1);
               option=GetNextArgument(&len);
               {
               char *ptr=option;
               session->cpathexprman->AddNewVPathExpr(ptr,option+strlen(option));
                  // 'ptr' is moved to the characters after the path expression
               SkipArgumentString(ptr-option);
               }

               return;

      // Sets the left white space handling
   case 'l':   option++;
               switch(*option)
               {
               case 'i':   globalleftwhitespacescompress=WHITESPACE_IGNORE;SkipArgumentString(2);return;
               case 'g':   globalleftwhitespacescompress=WHITESPACE_STOREGLOBAL;SkipArgumentString(2);return;
               case 't':   globalleftwhitespacescompress=WHITESPACE_STORETEXT;SkipArgumentString(2);return;
               }
               break;

      // Sets the right white space handling
   case 'r':   option++;
               switch(*option)
               {
               case 'i':   globalrightwhitespacescompress=WHITESPACE_IGNORE;SkipArgumentString(2);return;
               case 'g':   globalrightwhitespacescompress=WHITESPACE_STOREGLOBAL;SkipArgumentString(2);return;
               case 't':   globalrightwhitespacescompress=WHITESPACE_STORETEXT;SkipArgumentString(2);return;
               }
               break;

      // Sets the full white space handling
   case 'w':   option++;
               switch(*option)
               {
               case 'i':   globalfullwhitespacescompress=WHITESPACE_IGNORE;SkipArgumentString(2);return;
               case 'g':   globalfullwhitespacescompress=WHITESPACE_STOREGLOBAL;SkipArgumentString(2);return;
               case 't':   globalfullwhitespacescompress=WHITESPACE_STORETEXT;SkipArgumentString(2);return;
               default:    
                           globalleftwhitespacescompress=WHITESPACE_STOREGLOBAL;
                           globalrightwhitespacescompress=WHITESPACE_STOREGLOBAL;
                           globalfullwhitespacescompress=WHITESPACE_STOREGLOBAL;
                           globalattribwhitespacescompress=WHITESPACE_STOREGLOBAL;
                           SkipArgumentString(1);return;
               }
               break;

      // Sets the attribute white space handling
   case 'a':   option++;
               switch(*option)
               {
               case 'i':   globalattribwhitespacescompress=WHITESPACE_IGNORE;SkipArgumentString(2);return;
               case 'g':   globalattribwhitespacescompress=WHITESPACE_STOREGLOBAL;SkipArgumentString(2);return;
               }
               break;

      // Sets the handling of special sections (comment, CDATA, DOCTYPE, PI)
   case 'n':   option++;
               switch(*option)
               {
               case 'c':   ignore_comment=1;SkipArgumentString(2);return;
               case 't':   ignore_doctype=1;SkipArgumentString(2);return;
               case 'p':   ignore_pi=1;SkipArgumentString(2);return;
               case 'd':   ignore_cdata=1;;SkipArgumentString(2);return;
               }
               break;
#endif
#ifdef XDEMILL
      // All options for output formatting
   case 'o':   option++;
               switch(*option)
               {
               case 's':   // Use space indentation
               {
                  SkipArgumentString(2);
                  option=GetNextArgument(&len);
                  SkipArgumentString(len);

                  int spccount=atoi(option);
                  if(spccount<=0)
                  {
                     throw new XMillException (XMILL_ERR_ARGUMENTS, "Option '-os' must be followed be a positive integer");
                  }
                  session->xmloutput->Init(XMLINDENT_SPACES,0,spccount);
                  session->output_initialized=1;
                  return;
               }
                  // Use tab indentation
               case 't':   SkipArgumentString(2);
                           session->xmloutput->Init(XMLINDENT_TABS);
                           session->output_initialized=1;
                           return;

                  // Use no indentation
               case 'n':   SkipArgumentString(2);
                           session->xmloutput->Init(XMLINDENT_NONE);
                           session->output_initialized=1;
                           return;

                  // Determines type of newline (DOS or UNIX)
               case 'd':   usedosnewline=1;
                           SkipArgumentString(2);
                           return;
               case 'u':   usedosnewline=0;
                           SkipArgumentString(2);
                           return;
/*
               case 'w':
               {
                  int wrapcount;
                  option+=2;
                  wrapcount=atoi(option);
                  if(wrapcount<=0)
                  {
                     Error("Option '-ow' must be followed be a positive integer");
                     Exit();
                  }
                  while((*option>='0')&&(*option<='9'))
                     option++;
                  output.Init(XMLINDENT_WRAP,0,wrapcount);
                  session->output_initialized=1;
                  return;
               }
*/
               }
#endif // XDEMILL
   }
#ifdef XMILL
   // Determines the compression rate index for zlib
   if((*option>='0')&&(*option<='9'))
   {
      compressidx=(unsigned char)(*option-'0');
      SkipArgumentString(1);
      return;
   }
#endif

   XMillException *e = new XMillException (XMILL_ERR_ARGUMENTS, "Invalid option '-");
   e->ErrorCont(option);
   e->ErrorCont("'");
   throw e;
}

int Settings::HandleAllOptions(char **argv,int argc)
   // Reads all the options from 'argv'.
   // It returns the index of the first non-option string
   // i.e. the string not starting with '-'
{
   char  *option;
   int   len;

   InitArguments(argv,argc);

   while((option=GetNextArgument(&len))!=NULL)
   {
      if(*option!='-')
         break;

      SkipArgumentString(1);
      option++;

      InterpretOptionString(option);
   }

   return curargidx;
}

//********************************************************************
//********************************************************************

void Session::PrintUsage(char showmoreoptions)
{
   printf("XMill 0.9.1 (Jan 29th 2003) - a compressor for XML\n");
   printf("Copyright (C) 1999-2003 AT&T Labs Research\n");
   printf("Extended by Hedzer Westra <hhwestra@cs.uu.nl>\n");

#ifdef XMILL

   if(showmoreoptions==0)
      printf("\nUsage:\n\n  xmill [-i file] [-v] [-p path] [-m num] [-1..9] [-c] [-z] [-P] [-N] [-d] [-r] [-w] [-h] file ...\n\n");
   else
   {
      printf("\nUsage:\n\n  xmill [-i file] [-v] [-p path] [-m num] [-1..9] [-c] [-z] [-P] [-N] [-d] [-r] [-w] [-h]\n");
      printf("        [-w(i|g|t)] [-l(i|g|t)] [-r(i|g|t)] [-a(i|g)] [-n(c|t|p|d)]  file ...\n\n");
   }

   printf(" -i file  - include options from file\n");
   printf(" -v       - verbose mode\n");
   printf(" -p path  - define path expression\n");
   printf(" -m num   - set memory limit\n");
   printf(" -0..9    - set the compression factor of zlib/ppmdi (default=6/0)\n");
//   printf(" -t       - test mode (no output)\n");
   printf(" -c       - write on standard output\n");
//   printf(" -k       - keep original files unchanged (default)\n");
   printf(" -d       - delete input files\n");
   printf(" -f       - force overwrite of output files\n");
   printf(" -w       - preserve white spaces\n");
   printf(" -z       - use gzip in stead of bzip2\n");
   printf(" -P       - use ppmdi in stead of bzip2\n");
   printf(" -N       - use nozip in stead of bzip2\n");
   printf(" -h       - show extended white space options and user compressors\n");

   if(showmoreoptions)
   {
      printf("\n  Extended options:\n\n");
      printf("    -wi      - ignore complete white spaces (default)\n");
      printf("    -wg      - store complete white spaces in global container\n");
      printf("    -wt      - store complete white spaces as normal text\n");
      printf("    -li      - ignore left white spaces (default)\n");
      printf("    -lg      - store left white spaces in global container\n");
      printf("    -lt      - store left white spaces as normal text\n");
      printf("    -ri      - ignore right white spaces (default)\n");
      printf("    -rg      - store right white spaces in global container\n");
      printf("    -rt      - store right white spaces as normal text\n");
      printf("    -ai      - ignore attribute white spaces (default)\n");
      printf("    -ag      - store attribute white spaces in global container\n");
      printf("\n");
      printf("    -nc      - ignore comments\n");
      printf("    -nt      - ignore DOCTYPE sections\n");
      printf("    -np      - ignore PI sections\n");
      printf("    -nd      - ignore CDATA sections\n");
      printf("\n");
      printf("\n  User compressors:\n\n");
      compressman->PrintCompressorInfo();
   }
#endif

#ifdef XDEMILL
   printf("Usage:\n\n\t xdemill [-i file] [-v] [-c] [-d] [-r] [-os num] [-ot] [-on] [-od] [-ou] file ...\n\n");
   printf(" -i file  - include options from file\n");
   printf(" -v       - verbose mode\n");
   printf(" -c       - write on standard output\n");
//   printf(" -k       - keep original files unchanged\n");
   printf(" -d       - delete input files\n");
   printf(" -f       - force overwrite of output files\n");
//   printf(" -t       - test mode (no output)\n");
   printf(" -os num  - output formatted XML with space indentation\n");
   printf(" -ot      - output formatted XML with tabular indentation\n");
   printf(" -on      - output unformatted XML (without white spaces)\n");
#ifdef WIN32
   printf(" -od      - uses DOS newline convention (default)\n");
   printf(" -ou      - uses UNIX newline convention\n");
#else
   printf(" -od      - uses DOS newline convention\n");
   printf(" -ou      - uses UNIX newline convention (default)\n");
#endif
//   printf(" -ow num     - wrap XML output after specified number of characters\n");
#endif
}
