/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * AtFStk -- Attribute Filesystem Toolkit Library
 *
 * bind_options.c - AtFS toolkit library (Version Binding)
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: bind_options.c[7.0] Tue Dec 14 18:40:58 1993 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"
#include "sttk.h"
#include "atfstk.h"
#include "bind.h"

/*====================
 *  global variables
 *====================*/

EXPORT BindBaseline bindExact = {AT_BIND_DEFAULT, NULL, 0, 0, 0};
EXPORT BindBaseline bindBefore = {AT_BIND_DEFAULT, NULL, 0, 0, 0};
EXPORT BindBaseline bindSince = {AT_BIND_DEFAULT, NULL, 0, 0, 0};

EXPORT int atBindModeOption = 0;

/*========================
 *  command line parsing
 *========================*/

LOCAL int handleBaseline (option, arg)
     char *option, *arg;
{
  BindBaseline *bPtr;

  if (!arg || !(*arg)) {
    sprintf (stMessage, "No baseline specified -- '%s' ignored.", option);
    stLog (stMessage, ST_LOG_ERROR);
    return (1);
  }

  if (*option == 'b') { /* bind or before */
    if (option[1] == 'i')
      bPtr = &bindExact;
    else
      bPtr = &bindBefore;
  }
  else /* since */
    bPtr = &bindSince;

  bPtr->baselineType = atScanBinding (arg, &bPtr->baselineString, &bPtr->baselineGen,
				      &bPtr->baselineRev, &bPtr->baselineDate);

  if (bPtr->baselineType == AT_BIND_DEFAULT) {
    sprintf (stMessage, "cannot scan baseline -- '%s' option ignored.", option);
    stLog (stMessage, ST_LOG_ERROR);
    return (1);
  }    
  return (0);
}

LOCAL int handleMode (option, arg)
     char *option, *arg;
{
  if (*option == 'l') { /* last or lastsaved */
    if (option[4] == 's')
      atBindModeOption = AT_BIND_LASTSAVED;
    else
      atBindModeOption = AT_BIND_LAST;
  }
  else if (*option == 'u') /* uniq */
    atBindModeOption = AT_BIND_UNIQUE;
  else  /* nonuniq */
    atBindModeOption = AT_BIND_SET;
  return (0);
}

/*==========================
 *  rules
 *==========================*/

#define AT_CMDLINERULE "_CommandLineRule_"

LOCAL int handleRule (option, arg)
     char *option, *arg;
{
  int argLen;

  if (!arg || !(*arg)) {
    sprintf (stMessage, "No rule specified -- '%s' ignored.", option);
    stLog (stMessage, ST_LOG_ERROR);
    return (1);
  }

  /* If string ends with a priod (.), try to scan rule */
  argLen = strlen(arg);
  if (arg[argLen-1] == '.') {
    if (atBindAddRule (AT_CMDLINERULE, arg, "command line", 0)) {
      bindExact.baselineString = AT_CMDLINERULE;
      bindExact.baselineType = AT_BIND_RULE;
      return (0);
    }
    sprintf (stMessage, "Cannot recognize rule body -- taking '%s' argument as rule name", option);
    stLog (stMessage, ST_LOG_MSGERR);
  }
  bindExact.baselineString = arg;
  bindExact.baselineType = AT_BIND_RULE;
  return (0);
}
 
LOCAL int handleRuleFile (option, arg)
     char *option, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No rule file name specified -- '%s' ignored.", option);
    stLog (stMessage, ST_LOG_ERROR);
    return (1);
  }
  if (atBindRuleFile (arg) == FALSE) {
    sprintf (stMessage, "while loading %s: %s", arg, atBindErrorMsg);
    stLog (stMessage, ST_LOG_ERROR);
    return (1);
  }
  return (0);
}
 
/*==========================
 *  option vector
 *==========================*/

EXPORT StOptDesc bindOptions[] = {
  { "before",	 PCALL|PARG,	handleBaseline,	  NULL,		NULL },
  { "bind",	 PCALL|PARG,	handleBaseline,	  NULL,		NULL },
  { "since",	 PCALL|PARG,	handleBaseline,	  NULL,		NULL },
  { "uniq",	 PCALL,		handleMode,	  NULL,	  	NULL },
  { "nonuniq",	 PCALL,		handleMode,	  NULL,	  	NULL },
  { "last",	 PCALL,		handleMode,	  NULL,		NULL },
  { "lastsaved", PCALL,		handleMode,	  NULL,		NULL },
  { "rule",	 PCALL|PARG,	handleRule,	  NULL,		NULL },
  { "rulefile",	 PCALL|PARG,	handleRuleFile,	  NULL,		NULL },
  { "trace",	 PSWITCH|PSET,	NULL,		  &atBindTrace,	NULL },
  { NULL, 	 0, 		NULL, 		  NULL,		NULL },
};

/*============================
 *  Parse Options
 *============================*/

EXPORT int atBindOptions (argc, argv, newArgc, newArgv)
     int argc; char **argv;
     int *newArgc; char *(*newArgv[]);
{
  return (stParseArgs (argc, argv, newArgc, newArgv, bindOptions));
}

EXPORT void atBindUsage (extraText)
     char *extraText;
{
  stShortUsage ("version binding", bindOptions, extraText);
}

/*============================
 *  Expand Command Line
 *============================*/

EXPORT int atBindSetArgv (argc, argv)
     int *argc; char *(*argv[]);
{
  int  tmpArgc, newArgc, maxArgc = 256, pathLen, i, j;
  char **tmpArgv, **newArgv, *newArg, *path;
  Af_set *tmpSet;
  Af_key tmpKey;

  if (stParseArgs (*argc, *argv, &tmpArgc, &tmpArgv, bindOptions) == -1)
    return (ERROR);

  if ((newArgv = (char **)malloc (sizeof(char *) * maxArgc)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (ERROR);
  }
  newArgv[0] = tmpArgv[0];
  newArgc = 1;

  for (i = 1; i < tmpArgc; i++) {
    path = af_afpath (tmpArgv[i]);
    if ((pathLen = strlen (path)) > 0)
      pathLen ++;
    if ((tmpSet = atBindSet (tmpArgv[i], NULL, 0)) == NULL) {
      return (ERROR);
    }
    for (j=0; j<af_nrofkeys (tmpSet); j++) {
      af_setgkey (tmpSet, j, &tmpKey);
      if (af_retnumattr (&tmpKey, AF_ATTSTATE) == AF_BUSY)
	newArg = af_retattr (&tmpKey, AF_ATTUNIXNAME);
      else
	newArg = af_retattr (&tmpKey, AF_ATTBOUND);
      af_dropkey (&tmpKey);
      if ((newArgv[newArgc] = malloc (strlen (newArg)+pathLen+1)) == NULL) {
	atBindError = TRUE;
	strcpy (atBindErrorMsg, "not enough memory");
	af_dropset (tmpSet);
	return (ERROR);
      }
      if (pathLen) {
	strcpy (newArgv[newArgc], path);
	strcat (newArgv[newArgc], "/");
	strcat (newArgv[newArgc], newArg);
      }
      else
	strcpy (newArgv[newArgc], newArg);
      newArgc++;
      if (newArgc == maxArgc) {
	maxArgc += 256;
	if ((newArgv = (char **)realloc (newArgv, sizeof(char *) * maxArgc)) == NULL) {
	  atBindError = TRUE;
	  strcpy (atBindErrorMsg, "not enough memory");
	  af_dropset (tmpSet);
	  return (ERROR);
	}
      }
    }
    if (af_nrofkeys (tmpSet) == 0) {
      /* put back on command line */
      newArgv[newArgc] = tmpArgv[i];
      newArgc++;
    }
    af_dropset (tmpSet);
    if (newArgc == maxArgc) {
      maxArgc += 256;
      if ((newArgv = (char **)realloc (newArgv, sizeof(char *) * maxArgc)) == NULL) {
	atBindError = TRUE;
	strcpy (atBindErrorMsg, "not enough memory");
	return (ERROR);
      }
    }
  }
  newArgv[newArgc] = NULL;
  *argv = newArgv;
  *argc = newArgc;
  return (newArgc);
}
