/*  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.
 */
/*
 * ShapeTools Version Control System
 *
 * vadm.c - main program for "vadm" command
 *
 * by Axel.Mahler@cs.tu-berlin.de and Andreas.Lampen@cs.tu-berlin.de
 *
 * $Header: vadm.c[10.0] Mon Jul 12 14:17:39 1993 andy@cs.tu-berlin.de frozen $
 */

#include <ctype.h>

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

#define MAX_ACTIONS 16
#define MAX_ALIASES 4

static int usage();

/*======================
 * Globals
 *======================*/

LOCAL  Af_user newAuthor = {"","",""}, newOwner = {"","",""};
LOCAL  mode_t  newMode = 0;
LOCAL  char    *newCommentLeader = NULL, *commentType = NULL;

EXPORT int    lockType = -1, lockGen = 0, lockRev;
EXPORT char   *lockString;
EXPORT time_t lockDate;
LOCAL  Af_set *lockArgSetPtr = NULL;


LOCAL  char *newAlias[MAX_ALIASES] = {"","","",""};
LOCAL  char *delAlias[MAX_ALIASES] = {"","","",""};
LOCAL  int  newAliasCount = 0, delAliasCount = 0;

LOCAL  char *newAttrs[MAX_ACTIONS] = {"","","","","","","","","","","","","","","","",};
LOCAL  int  newAttrCount = 0;

LOCAL  char *delAttrs[MAX_ACTIONS] = {"","","","","","","","","","","","","","","",""};
LOCAL  int  delAttrCount = 0;

LOCAL  int  cacheFlag = FALSE;
EXPORT int  forceFlag = FALSE;
EXPORT int  nomailFlag = FALSE;
EXPORT int  stdinFlag = FALSE;

/*=====================
 *  Action List
 *=====================*/

LOCAL int     actionList[MAX_ACTIONS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
LOCAL int     actionCount = 0;

LOCAL int addToActionList (action, code)
     char *action;
     int  code;
{
  if (actionCount == MAX_ACTIONS) {
    sprintf (stMessage, "Too many actions (maximum number is %d) -- '%s' ignored.",
	     MAX_ACTIONS, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  actionList[actionCount++] = code;
  return (0);
}

LOCAL int actionsPlausible ()
{
  if (actionCount == 0) {
    stLog ("Action(s) missing.", ST_LOG_MSGERR);
    usage (); /* exit */
  }

  if (cacheFlag) {
    int i=0;
    while (i < actionCount) {
      switch (actionList[i++]) {
      case VADM_SBMT:
      case VADM_PUBL:
      case VADM_ACCS:
      case VADM_FRZE:
      case VADM_PROMOTE:
      case VADM_UNPROMOTE:
	stLog ("Cannot change status of cached objects.", ST_LOG_ERROR);
	return (FALSE);
      case VADM_NEWGEN:
	stLog ("Cannot open new generation for cached objects.", ST_LOG_ERROR);
	return (FALSE);
      case VADM_LOCK:
      case VADM_UNLOCK:
	stLog ("Cannot lock/unlock cached objects.", ST_LOG_ERROR);
	return (FALSE);
      case VADM_SET:
	stLog ("Cannot attach comment to cached objects.", ST_LOG_ERROR);
	return (FALSE);
      case VADM_CHOWN:
      case VADM_CHAUT:
	stLog ("Cannot change owner or author of cached objects.", ST_LOG_ERROR);
	return (FALSE);
      }
    }
  }
  return (TRUE);
}

/*========================
 *     Option handlers
 *========================*/

LOCAL int handleAlias (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No alias name specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }

  if (action[0] == 'u') { /* unalias */
    if (delAliasCount == MAX_ALIASES) {
      sprintf (stMessage, "Too many aliases (maximum number is %d) -- '%s %s' ignored.",
	       MAX_ALIASES, action, arg);
      stLog (stMessage, ST_LOG_WARNING);
      return (0);
    }
    delAlias[delAliasCount++] = arg;
    return (addToActionList (action, VADM_UNALIAS));
  }
  else {
    if (newAliasCount == MAX_ALIASES) {
      sprintf (stMessage, "Too many aliases (maximum number is %d) -- '%s %s' ignored.",
	       MAX_ALIASES, action, arg);
      stLog (stMessage, ST_LOG_WARNING);
      return (0);
    }
    newAlias[newAliasCount++] = arg;
    return (addToActionList (action, VADM_ALIAS));
  }
}

LOCAL int handleAttr (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No attribute specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  newAttrs[newAttrCount++] = arg;
  return (addToActionList (action, VADM_ATTR));
}

LOCAL int handleChaut (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No new author specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  if (atUserValid (&newAuthor)) {
    sprintf (stMessage, "New author already set to '%s' -- additional '%s' ignored.",
	     atUserName (&newAuthor), action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }

  atScanUser (arg, &newAuthor);

  if (!atUserValid (&newAuthor)) {
    sprintf (stMessage, "%s is not a valid user on this machine -- '%s' ignored.", arg, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  return (addToActionList (action, VADM_CHAUT));
}

LOCAL int handleChmod (action, arg)
     char *action, *arg;
{
  mode_t mode = 0;
  char   ciph, err = 0, *cp = arg;

  if (strlen (arg) > 4)
    err++;
  else 
    while ((ciph = *cp++)) { 
      ciph -= '0';
      if ((ciph > 7) || (ciph < 0)) err++;
      else {
	mode <<= 3;  
	mode += ciph;
      }
    }
  if (err) {
    sprintf (stMessage, "invalid mode '%s' -- '%s' ignored.", arg, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  newMode = mode;
  return (addToActionList (action, VADM_CHMOD));
}

LOCAL int handleChown (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No new owner specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  if (atUserValid (&newOwner)) {
    sprintf (stMessage, "New owner already set to '%s' -- additional '%s' ignored.",
	     atUserName (&newOwner), action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }

  atScanUser (arg, &newOwner);

  if (!atUserValid (&newOwner)) {
    sprintf (stMessage, "%s is not a valid user on this machine -- '%s' ignored.", arg, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  return (addToActionList (action, VADM_CHOWN));
}

LOCAL int handleDelete (action, arg)
     char *action, *arg;
{
  return (addToActionList (action, VADM_DELETE));
}

LOCAL int handleDelAttr (action, arg)
     char *action, *arg;
{
  char *attrNamePtr;
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No attribute name specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }

  attrNamePtr = atAttrName (arg);
  if ((delAttrs[delAttrCount] = malloc (strlen (attrNamePtr)+1)) == NULL) {
    stLog ("Not enough memory", ST_LOG_ERROR);
    return (1);
  }
  strcpy (delAttrs[delAttrCount++], attrNamePtr);
  return (addToActionList (action, VADM_DELATTR));
}

LOCAL int handleLock (action, arg)
     char *action, *arg;
{
  if (lockType != -1) {
    sprintf (stMessage, "Cannot handle multiple lock/unlock actions -- '%s' ignored.", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }

  lockType = AT_BIND_DEFAULT;
  if (arg && *arg) {
    /* check if argument may be a filename -- if so, ignore it */
    if ((lockArgSetPtr = atBindSet (arg, NULL, atBindModeOption ? atBindModeOption : AT_BIND_LAST)) && (af_nrofkeys (lockArgSetPtr) > 0)) {
      lockString = arg; /* special case: save -lock argument for use in main loop */
    }
    else {
      lockArgSetPtr = NULL;
      lockType = atScanBinding (arg, &lockString, &lockGen, &lockRev, &lockDate);

      /* assume generation number, when lockString contains only digits  */
      if (lockType == AT_BIND_ALIAS) {
	if (!strcmp (lockString, "last")) {
	  lockGen = AF_LASTVERS;
	  lockType = AT_BIND_VNUM;
	  lockString = NULL;
	}
	else {
	  int i=0, isGen = TRUE;
	  while (lockString[i])
	    if (!isdigit(lockString[i++]))
	      isGen = FALSE;
	  if (isGen) {
	    lockGen = atoi (lockString);
	    lockType = AT_BIND_VNUM;
	    lockString = NULL;
	  }
	}
      }
    }
  }
  if (action[0] == 'l')
    return (addToActionList (action, VADM_LOCK));
  else
    return (addToActionList (action, VADM_UNLOCK));
}

LOCAL int handleNewgen (action, arg)
     char *action, *arg;
{
  int i=0;
  while (i < actionCount) {
    if (actionList[i++] == VADM_NEWGEN) {
      sprintf (stMessage, "You already requested the new generation -- '%s' ignored.", action);
      stLog (stMessage, ST_LOG_WARNING);
      return (0);
    }
  }
  return (addToActionList (action, VADM_NEWGEN));
}

LOCAL int handlePromote (action, arg)
     char *action, *arg;
{
  int i=0;
  while (i < actionCount) {
    if (actionList[i++] == VADM_UNPROMOTE) {
      sprintf (stMessage, "You already requested the unpromote action -- '%s' ignored.", action);
      stLog (stMessage, ST_LOG_WARNING);
      return (0);
    }
  }
  return (addToActionList (action, VADM_PROMOTE));
}

LOCAL int handleSet (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "'%s' what ? No comment type specified -- '%s' ignored.\n",
	     action, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  if (commentType) {
    sprintf (stMessage, "Comment type already set to %s -- additional '%s' ignored.",
	     commentType, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  commentType = arg;
  return (addToActionList (action, VADM_SET));
}

LOCAL int handleSetc (action, arg)
     char *action, *arg;
{
  if (!arg || !(*arg)) {
    sprintf (stMessage, "No comment leader specified -- '%s' ignored.\n", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  if (newCommentLeader) {
    sprintf (stMessage, "Comment leader already set to '%s' -- additional '%s' ignored.",
	     newCommentLeader, action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  if (strlen (arg) > (AT_CLEADMAXLEN-(strlen(AT_ATTCLEAD)+2))) {
    sprintf (stMessage, "Specified comment leader is too long -- '%s' ignored.", action);
    stLog (stMessage, ST_LOG_WARNING);
    return (0);
  }
  newCommentLeader = arg;
  return (addToActionList (action, VADM_SETCLEAD));
}

LOCAL int handleUnpromote (action, arg)
     char *action, *arg;
{
  int i=0;
  while (i < actionCount) {
    if (actionList[i++] == VADM_PROMOTE) {
      sprintf (stMessage, "You already requested the promote action -- '%s' ignored.", action);
      stLog (stMessage, ST_LOG_WARNING);
      return (0);
    }
  }
  return (addToActionList (action, VADM_UNPROMOTE));
}

/*======================
 * Option Vector
 *======================*/

LOCAL int printVersion ()
{
  char *vadmversion();
  sprintf (stMessage, "This is %s", vadmversion());
  stLog (stMessage, ST_LOG_MSG);
  sprintf (stMessage, "  using %s,", atVersion());
  stLog (stMessage, ST_LOG_MSG);
  sprintf (stMessage, "        %s,", af_version());
  stLog (stMessage, ST_LOG_MSG);
  sprintf (stMessage, "    and %s.", stVersion());
  stLog (stMessage, ST_LOG_MSG);
  stExit (0);
  return (0);
}

LOCAL StOptDesc optDesc[] = {
  { "?",	PCALL,		usage,		NULL,		NULL },
  { "alias",	PCALL|PARG,	handleAlias,	NULL,		NULL },
  { "attr",	PCALL|PARG,	handleAttr,	NULL,		NULL },
  { "cache",	PSWITCH|PSET,	NULL,		&cacheFlag,	NULL },
  { "chaut",	PCALL|PARG,	handleChaut,	NULL,		NULL },
  { "chmod",	PCALL|PARG,	handleChmod,	NULL,		NULL },
  { "chown",	PCALL|PARG,	handleChown,	NULL,		NULL },
  { "d",	PCALL,		handleDelete,	NULL,		NULL },
  { "delattr",	PCALL|PARG,	handleDelAttr,	NULL,		NULL },
  { "delete",	PCALL,		handleDelete,	NULL,		NULL },
  { "f",	PSWITCH|PSET,	NULL,		&forceFlag,	NULL },
  { "force",	PSWITCH|PSET,	NULL,		&forceFlag,	NULL },
  { "help",	PCALL,		usage,		NULL,		NULL },
  { "l",	PCALL|POARG,	handleLock,	NULL,		NULL },
  { "lock",	PCALL|POARG,	handleLock,	NULL,		NULL },
  { "nomail",	PSWITCH|PSET,	NULL,		&nomailFlag,	NULL },
  { "newgen",	PCALL,		handleNewgen,	NULL,		NULL },
  { "promote",	PCALL,		handlePromote,	NULL,		NULL },
  { "q",	PSWITCH|PSET,	NULL,		&stQuietFlag,	NULL },
  { "quiet",	PSWITCH|PSET,	NULL,		&stQuietFlag,	NULL },
  { "set",	PCALL|PARG,	handleSet,	NULL,		NULL },
  { "setc",	PCALL|PARG,	handleSetc,	NULL,		NULL },
  { "stdin",	PSWITCH|PSET,	NULL,		&stdinFlag,	NULL },
  { "symbolic",	PCALL|PARG|PHIDDEN, handleAlias,NULL,		NULL },
  { "unalias",	PCALL|PARG,	handleAlias,	NULL,		NULL },
  { "unlock",	PCALL|POARG,	handleLock,	NULL,		NULL },
  { "unpromote",PCALL,		handleUnpromote,NULL,		NULL },
  { "version",	PCALL,		printVersion,	NULL,		NULL },
  { NULL,	0,	NULL,	NULL,	NULL }
};

LOCAL int usage()
{
  stLog ("Usage:", ST_LOG_MSGERR);
  stShortUsage (stProgramName, optDesc, "names...");
  atBindUsage ("");
  stExit (1);
  return (1);
}

/*===================
 *  perform actions
 *===================*/

LOCAL int doAction (path, aso)
     char   *path;
     Af_key *aso;
{
  int  i=0, retCode = 0;
  int  curNewAlias = 0, curDelAlias = 0, curNewAttr = 0, curDelAttr = 0;
  char asoName[PATH_MAX], commentSymAttr[AT_CLEADMAXLEN];

  /* build name for output */
  if (path && *path) {
    strcpy (asoName, path);
    strcat (asoName, "/");
  }
  else
    asoName[0] = '\0';

  if (af_retnumattr (aso, AF_ATTSTATE) == AF_BUSY)
    strcat (asoName, af_retattr (aso, AF_ATTUNIXNAME));
  else
    strcat (asoName, af_retattr (aso, AF_ATTBOUND));

  while (i < actionCount) {
    switch (actionList[i]) {
    case VADM_SBMT:
      retCode += doStatus (asoName, aso, AF_PROPOSED);
      break;
    case VADM_PUBL:
      retCode += doStatus (asoName, aso, AF_PUBLISHED);
      break;
    case VADM_ACCS:
      retCode += doStatus (asoName, aso, AF_ACCESSED);
      break;
    case VADM_FRZE:
      retCode += doStatus (asoName, aso, AF_FROZEN);
      break;
    case VADM_DELETE:
      retCode += doDelete (asoName, aso);
      break;
    case VADM_LOCK:
      retCode += doLock (path, asoName, aso, VADM_LOCK);
      break;
    case VADM_UNLOCK:
      retCode += doLock (path, asoName, aso, VADM_UNLOCK);
      break;
    case VADM_NEWGEN: {
      Af_key newKey;
      af_commit ();
      if (af_newgen (aso, &newKey) == -1) {
  	sprintf (stMessage, "Cannot open new generation for %s -- %s.",
		 asoName, af_errmsg ("af_newgen"));
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
      }
      af_setattr (&newKey, AF_REMOVE, AT_ATTALIAS);
      af_setattr (&newKey, AF_REMOVE, AT_ATTINTENT);
      af_transaction ();
      break;
    }
    case VADM_PROMOTE:
      retCode += doStatus (asoName, aso, VADM_STATUS_PROMOTE);
      break;
    case VADM_UNPROMOTE:
      retCode += doStatus (asoName, aso, VADM_STATUS_UNPROMOTE);
      break;
    case VADM_SET:
      retCode += doComment (path, asoName, aso, commentType);
      break;
    case VADM_SETCLEAD:
      sprintf (commentSymAttr, "%s=%s", AT_ATTCLEAD, newCommentLeader);
      if (!atSetAttr (aso, commentSymAttr, AF_REPLACE)) {
  	sprintf (stMessage, "Cannot set comment leader attribute '%s' for %s -- %s.",
		 commentSymAttr, asoName, atErrMsg);
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
      }
      break;
    case VADM_ATTR:
      /* check if attrs shall be read from file */
      if (newAttrs[curNewAttr][0] == '@') {
	if (!atSetAttrFile (aso, &(newAttrs[curNewAttr][1]))) {
	  sprintf (stMessage, "Cannot set attribute file '%s' for %s -- %s.",
		   newAttrs[curNewAttr], asoName, atErrMsg);
	  stLog (stMessage, ST_LOG_ERROR);
	  retCode++;
	}
      }
      else if (strchr (newAttrs[curNewAttr], AF_UDANAMDEL) == NULL) {
	/* if just an attribute name is given */
	stLog (atRetAttr (aso, newAttrs[curNewAttr]), ST_LOG_MSG);
      }
      else if (!atSetAttr (aso, newAttrs[curNewAttr], AF_REPLACE)) {
	sprintf (stMessage, "Attribute change '%s' for %s failed -- %s.",
		 newAttrs[curNewAttr], asoName, atErrMsg);
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
      }
      curNewAttr++;
      break;
    case VADM_DELATTR:
      if (!atSetAttr (aso, delAttrs[curDelAttr], AF_REMOVE)) {
	sprintf (stMessage, "Cannot delete attribute '%s' from %s -- %s.",
		 delAttrs[curDelAttr], asoName, atErrMsg);
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
      }
      curDelAttr++;
      break;
    case VADM_ALIAS:
      if (!atSetVersAlias (aso, newAlias[curNewAlias])) {
	  sprintf (stMessage, "Cannot set version alias '%s' for %s -- %s.",
		   newAlias[curNewAlias], asoName, atErrMsg);
	  stLog (stMessage, ST_LOG_ERROR);
	  retCode++;
	}
      curNewAlias++;
      break;
    case VADM_UNALIAS:
      if (!atDelVersAlias (aso, delAlias[curDelAlias])) {
	  sprintf (stMessage, "Cannot delete version alias '%s' for %s -- %s.",
		   delAlias[curDelAlias], asoName, atErrMsg);
	  stLog (stMessage, ST_LOG_ERROR);
	  retCode++;
	}
      curDelAlias++;
      break;
    case VADM_CHMOD:
      if (af_chmod (aso, newMode) < 0) {
	sprintf (stMessage, "Cannot change mode of %s -- %s.",
		 asoName, af_errmsg ("af_chmod"));
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
	break;
      }
      break;
    case VADM_CHOWN:
      if (af_chowner (aso, &newOwner) < 0) {
	stLog ("Sorry, the 'change owner' action is not available on your system.", ST_LOG_ERROR);
	retCode++;
	break;
      }
      break;
    case VADM_CHAUT:
      if (af_chauthor (aso, &newAuthor) < 0) {
	sprintf (stMessage, "Cannot change author of %s -- %s.",
		 asoName, af_errmsg ("af_chmod"));
	stLog (stMessage, ST_LOG_ERROR);
	retCode++;
	break;
      }
      break;
    }
    i++;
  }
  return (retCode);
}

/*======================
 * main and friends
 *======================*/

LOCAL Sigret_t interrupt_action ()
     /* is executed by appropriate signal handler */
{
  af_abort ();
  stExit (1);
}

EXPORT int main (argc, argv)
     int argc;
     char **argv;
{
  int  tmpArgc, newArgc, i, j, k, nameCount, retCode=0;
  char *cp, **tmpArgv, **newArgv, path[PATH_MAX];
  Af_key curKey;
  Af_set *setPtr;
  
  stProgramName = (cp = strrchr (argv[0], '/')) ? ++cp : argv[0];

  stInterruptAction = interrupt_action;
  stCatchSigs();

  if (atBindOptions (argc, argv, &tmpArgc, &tmpArgv))
    stExit (1);

  if (stParseArgs (tmpArgc, tmpArgv, &newArgc, &newArgv, optDesc))
    stExit (1);

  if (strcmp (stProgramName, "vadm")) {
    if (actionCount) {
      sprintf (stMessage, "You may not specify any action parameter with '%s'.", stProgramName);
      stLog (stMessage, ST_LOG_WARNING);
      stLog ("\t\tAll action parameters ignored.", ST_LOG_ERROR);
      actionList[0] = 0;
      actionCount = 0;
    }
    if (!(strcmp (stProgramName, "vrm")))
      addToActionList ("delete", VADM_DELETE);
    else if (!(strcmp (stProgramName, "vattr"))) {
      /* fetch off attribute from command line */
      newAttrs[0] = newArgv[1];
      newAttrCount = 1;
      for (i=2; i<=newArgc; i++)
	newArgv[i-1] = newArgv[i];
      if (newArgc >= 2)
	newArgc--;
      addToActionList ("attr", VADM_ATTR);
    }
    else if (!(strcmp (stProgramName, "sbmt")))
      addToActionList ("submit", VADM_SBMT);
    else if (!(strcmp (stProgramName, "publ")))
      addToActionList ("publish", VADM_PUBL);
    else if (!(strcmp (stProgramName, "accs")))
      addToActionList ("access", VADM_ACCS);
    else if (!(strcmp (stProgramName, "frze")))
      addToActionList ("freeze", VADM_FRZE);
  }

  if (!actionsPlausible ()) {
    stExit (1);
  }

  if (((nameCount = newArgc-1) == 0) && !lockArgSetPtr) {
    stLog ("Filename(s) missing.", ST_LOG_MSGERR);
    stExit (1);
  }

  k=0;
  stThisTransaction.tr_rc = 0;
  for (i = 0; i < newArgc; i++) {
    /* Normally, this loop should start with i=1 (first argument).
     * The first loop body execution (i==0) handles just the case that
     * "lockArgSetPtr" points to an ASO set. This happens, when the
     * evaluation of the argument of the -lock option resulted in a
     * hit set rather than a generation binding. "vadm" then assumes
     * "-lock" to be meant without argument, but stParseArgs did not
     * recognize this.
     * Example: "vadm -lock version.c"
     */
    if ((i == 0) && !lockArgSetPtr)
      continue;

    if (lockArgSetPtr) {
      setPtr = lockArgSetPtr;
      lockArgSetPtr = NULL;
      strcpy (path, af_afpath (lockString));
      lockString = NULL;
    }
    else {
      /* do version binding */
      if (cacheFlag)
	setPtr = atBindCache (newArgv[i], NULL);
      else
	setPtr = atBindSet (newArgv[i], NULL, atBindModeOption ?
			    atBindModeOption :AT_BIND_LAST);
      if (setPtr == NULL) {
	stLog (atBindErrorMsg, ST_LOG_ERROR);
	stExit (1);
      }
      if (af_nrofkeys (setPtr) == 0) {
	sprintf (stMessage, "%s: nothing found -- skipped.", newArgv[i]);
	stLog (stMessage, ST_LOG_MSGERR);
	retCode++;
	continue;
      }
      strcpy (path, af_afpath (newArgv[i]));
    }

    for (j = 0; j < af_nrofkeys (setPtr); j++) {
      af_setgkey (setPtr, j, &curKey);
      if (!setjmp (stThisTransaction.tr_env)) {
	stThisTransaction.tr_seqno = k++;
	strcpy (stThisTransaction.tr_fname, af_retattr (&curKey, AF_ATTBOUND));
	stThisTransaction.tr_done = FALSE;
        af_transaction ();
	retCode += doAction (path, &curKey);
        if (af_commit () < 0) {
	  sprintf (stMessage, "Cannot save changes for %s%s%s -- %s.",  path, path[0] ? "/" : "",
		   af_retattr (&curKey, AF_ATTUNIXNAME), af_errmsg ("af_commit"));
	  stLog (stMessage, ST_LOG_ERROR);
	  retCode++;
	}
      }
      else { /* stThisTransaction was aborted */
	retCode += stThisTransaction.tr_rc;
        af_abort ();
      }
    }
  }
  if (nameCount > 1)
    stLog ("done.", ST_LOG_MSG);
  return (retCode);
}
