/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <opencm.h>
#include "../libgdiff/getopt.h"
#include <repos/Repository.h>
#include "opencmclient.h"

static int which_long_opt = 0;

const char *CurTmpFile = 0;

#define LOPT_REPOSITORY   1
#define LOPT_NAME         2
#define LOPT_AUTHOR       3
#define LOPT_FLUSHIO      4
#define LOPT_CONFIGDIR    5
#define LOPT_MSGFILE      6
#define LOPT_RANDOMFILE   7
#define LOPT_KEYTYPE      8
#define LOPT_PRIVATE      9
#define LOPT_FOREGROUND   10
#define LOPT_TRACEPROTO   11
#define LOPT_ALTPORT      12
#define LOPT_FORCE_HASH   13
#define LOPT_NOTIFY       14
#define LOPT_REPORT_TIME  15
#define LOPT_AGAINST      16
#define LOPT_RBTREE_DEBUG 17
#define LOPT_LOGDIR       18

struct option longopts[] = {
  { "author",               1,  &which_long_opt, LOPT_AUTHOR },
  { "against",              1,  &which_long_opt, LOPT_AGAINST },
  { "configdir",            1,  &which_long_opt, LOPT_CONFIGDIR },
  { "force-hash",           0,  &which_long_opt, LOPT_FORCE_HASH },
  { "foreground",           0,  &which_long_opt, LOPT_FOREGROUND },
  { "keytype",              1,  &which_long_opt, LOPT_KEYTYPE },
  { "logdir",               1,  &which_long_opt, LOPT_LOGDIR },
  { "messagefile",          1,  &which_long_opt, LOPT_MSGFILE },
  { "name",                 1,  &which_long_opt, LOPT_NAME },
  { "notify",               1,  &which_long_opt, LOPT_NOTIFY },
  { "port",                 1,  &which_long_opt, LOPT_ALTPORT },
  { "private",              0,  &which_long_opt, LOPT_PRIVATE },
  { "random",               1,  &which_long_opt, LOPT_RANDOMFILE },
  { "rbtree-debug",         0,  &which_long_opt, LOPT_RBTREE_DEBUG },
  { "report-time",          0,  &which_long_opt, LOPT_REPORT_TIME },
  { "repository",           1,  &which_long_opt, LOPT_REPOSITORY },
  { "traceproto",           0,  &which_long_opt, LOPT_TRACEPROTO },

  /* Options that do not take arguments: */
  { "flush-io",             0,  &which_long_opt, LOPT_FLUSHIO },
  
  /* Options that have short-form equivalents: */
  { "chdir",                1,  0, 'C' },
  { "compression-level",    1,  0, 'z' },
  { "readonly",             0,  0, 'n' },
  { "verbose",              0,  0, 'v' },
  { "force",                0,  0, 'f' },
  { "debug",                1,  0, 'd' },
  { "newbranch",            1,  0, 'B' },
  { "newproject",           1,  0, 'P' },
  { "type",                 1,  0, 't' },
  { "user",                 1,  0, 'u' },
  {0,                       0,  0, 0}
};



int opt_ReadOnly = 0;
int opt_Verbosity =  0;
extern int opt_ReportTime;	/* defined in common/Report.c */
int opt_Force = 0;
int opt_NewBranch = 0;
int opt_NewProject = 0;
int opt_FlushIO = 0;
char *opt_Editor = 0;
unsigned char opt_eType = '?';	/* until proven otherwise */
const char *opt_user  = 0;
char *opt_Author = 0;
char *opt_Days = 0;
int  opt_RBTreeDebug;	/* --rbtree-debug */
int  opt_HaveRepository = 0;
int  opt_HaveUser = 0;
char *opt_KeyType = 0;
char *opt_Name = 0;
char *opt_Message = 0;
char *opt_RandomFile = NULL;
char *opt_LogDir = NULL;
const char *opt_Chdir = NULL;
const char *opt_Against = NULL;
int opt_Foreground = 0;
int opt_LongListing = 0;
int opt_Private = 0;
int opt_TraceProtocol = 0;
int opt_ForceHash = 0;
int opt_CompressionLevel = 3;	/* default to -z3 */
const char *opt_Notify = "/dev/null";
char *opt_AlternatePort = 0;

int server_mode = 0;

const char *opt_ConfigDir = 0;

void
process_options(int argc, char *argv[])
{
  int c;
  int longndx;
  
  while ((c = getopt_long(argc, argv, "C:P:B:flbo:nvd:m:e:p:u:D:z:",
			  longopts, &longndx)) >= 0) {
    switch(c) {    
    case 0: {
      /* Long option: */

      switch(which_long_opt) {
      case LOPT_REPOSITORY: {
	opt_HaveRepository = 1;
	RepositoryURI = optarg;
	break;
      }
      case LOPT_NAME: {
	opt_Name = optarg;
	break;
      }
      case LOPT_AGAINST: {
	opt_Against = optarg;
	break;
      }
      case LOPT_AUTHOR: {
	opt_Author = optarg;
	break;
      }
      case LOPT_CONFIGDIR: {
	opt_ConfigDir = optarg;
	break;
      }
      case LOPT_PRIVATE: {
	opt_Private = 1;
	break;
      }
      case LOPT_RBTREE_DEBUG: {
	opt_RBTreeDebug = 1;
	break;
      }
      case LOPT_TRACEPROTO: {
	opt_TraceProtocol++;
	break;
      }
      case LOPT_RANDOMFILE: {
	opt_RandomFile = optarg;
	break;
      }
      case LOPT_NOTIFY: {
	opt_Notify = optarg;
	break;
      }
      case LOPT_LOGDIR: {
	opt_LogDir = optarg;
	break;
      }
      case LOPT_ALTPORT: {
	opt_AlternatePort = optarg;
	break;
      }
      case LOPT_FORCE_HASH: {
	opt_ForceHash = 1;
	break;
      }
      case LOPT_KEYTYPE: {
	opt_KeyType = optarg;

	/* Check for recognized key types: */
	if ( ! (
		nmequal(opt_KeyType, "rsa:512") ||
		nmequal(opt_KeyType, "rsa:1024") ||
		nmequal(opt_KeyType, "rsa:2048") ) )
	  THROW(ExBadValue, "Unknown key type.");

	break;
      }
      case LOPT_FLUSHIO: {
	/* This is used by the testing subsystem to ensure that output
	   to stdout and output to stderr appear in proper sequence
	   for later comprehension. */
	opt_FlushIO = 1;

        /*
           #ifdef HAVE_SETLINEBUF
                setlinebuf(stdout);
                setlinebuf(stderr);
           #endif
         
           According to the Linux man page: "The setlinebuf function is exactly
           equivalent to the call: setvbuf(stream, (char *)NULL, _IOLBF, 0);"
           and it is more portable (setvbuf is ANSI C, setlinebuf is BSD)

           So:
        */
        setvbuf(stdout, (char*)NULL, _IOLBF, 0);
        setvbuf(stderr, (char*)NULL, _IOLBF, 0);
	break;
      }
      case LOPT_MSGFILE: {
	opt_Message = read_message_file(optarg, 0);
	break;
      }
      case LOPT_FOREGROUND: {
	opt_Foreground = 1;
	break;
      }
      default:
	usage(0);
      }
      break;
    }

    case 't':			/* file type */
      opt_eType = *optarg;
      break;
      
    case 'u':
      opt_HaveUser = 1;
      opt_user = optarg;
      break;
      
    case 'C':
      {
	if (path_exists(optarg) && !path_isdir(optarg))
	  THROW(ExNoAccess,
		format("\"%s\" exists and is not a directory\n", optarg));

	if (!path_exists(optarg)) {
	  log_trace(TRC_CHDIR, "Creating directory \"%s\"\n", optarg);
	  path_smkdir(optarg);
	}

	log_trace(TRC_CHDIR, "Changing directory to \"%s\"\n", optarg);
	chdir(optarg);

	break;
      }
      
    case 'B':
      opt_NewBranch = 'B';
      break;
      
    case 'P':
      opt_NewProject = 'P';
      opt_NewBranch = 'B';	/* new project always implies new branch */
      break;
      
    case 'd':
      log_set_debugging(optarg);

      /* Debugging implies foreground operation: */
      opt_Foreground = 1;
      break;
      
    case 'z':
      errno = 0;
      opt_CompressionLevel = strtol(optarg, 0, 0);
      if(errno)
        THROW(ExBadValue, format("strtol failed on %s (errno=%d)\n",
                                 optarg, errno));
      if (opt_CompressionLevel < 0 || opt_CompressionLevel > 9)
	THROW(ExBadValue, "Compression level must be between 0 and 8");
      break;

    case 'f':
      opt_Force = 1;
      break;
      
    case 'n':
      opt_ReadOnly = 1;
      break;

    case 'm':
      opt_Message = optarg;
      break;
      
    case 'e':
      opt_Editor = optarg;
      break;
      
    case 'v':
      opt_Verbosity++;
      log_set_verbosity(opt_Verbosity);
      break;

    case 'l':
      opt_LongListing = 1;
      break;

    default:
      usage(0);
    }
  }

  return;
}


