/* 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
 *
 * dosave.c - functions for "save" command
 *
 * Authors: Axel.Mahler@cs.tu-berlin.de
 *	    Andreas.Lampen@cs.tu-berlin.de
 *
 * $Header: dosave.c[8.3] Mon Aug  8 13:35:52 1994 andy@cs.tu-berlin.de frozen $
 */

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

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

extern int af_errno;

extern char *aliasName;
extern char *attrFile;
extern int deltaFlag;
extern int forceFlag;
extern int fixType;
extern char *fixString;
extern int fixGen;
extern int fixRev;
extern time_t fixDate;
extern int newGenFlag;
extern int keepLockFlag;
extern char *historyLog;
extern int vnumGen;
extern int vnumRev;
extern int stdinFlag;

/*====================
 * local routines
 *====================*/

#define fail(x) ((x) == -1)

#define checkAtFSerr(obj) \
  if (af_errno != AF_ENOREV) { \
      stLog (af_errmsg (obj), ST_LOG_ERROR); \
      stAbortThis (TRUE); \
    }

LOCAL int changed (new, old, realtruth) Af_key *new, *old; int *realtruth; {
/*
 * Return true if new and old are actually different OR deposit is
 * forced OR if user says 'yes' when asked whether an unchanged version
 * shall be saved anyway.
 * As AtFS is still in the tests, this is programmed very defensively.
 */
  FILE   *newf, *oldf;
  size_t newSize = af_retnumattr (new, AF_ATTSIZE);
  size_t oldSize = af_retnumattr (old, AF_ATTSIZE);
  char   *news, *olds;

  *realtruth = TRUE;  /* initially assume that something has act. changed */
  if (af_rettimeattr (new, AF_ATTMTIME) == af_rettimeattr (old, AF_ATTMTIME)) {
    *realtruth = FALSE;
    if (forceFlag) {
      return TRUE;
    }
    if (stQuietFlag) {
      return FALSE;
    }
    if (isatty (fileno (stdin)) && isatty (fileno (stdout))) {
      sprintf (stMessage, "There are no changes with respect to the previously saved version.\nSave anyway ?");
      return (!stAskConfirm (stMessage, "no"));
    }
  }

  if (newSize != oldSize) {
    return (TRUE);
  }
  else { /* lets have a closer look at 'em */
    if ((news = malloc ((unsigned)newSize)) == NULL) {
      sprintf (stMessage, "Can't malloc %lu bytes.", newSize);
      stLog (stMessage, ST_LOG_MSG);
      stAbortThis (TRUE);
    }
    if ((olds = malloc ((unsigned)oldSize)) == NULL) {
      sprintf (stMessage, "Can't malloc %lu bytes.", oldSize);
      stLog (stMessage, ST_LOG_MSG);
      free (news);
      stAbortThis (TRUE);
    }
    if ((newf = af_open (new, "r")) == NULL) {
      stLog (af_errmsg ("busy-version"), ST_LOG_ERROR);
      free (news);
      free (olds);
      stAbortThis(TRUE);
    }
    if ((oldf = af_open (old, "r")) == NULL) {
      stLog (af_errmsg ("lastsaved-version"), ST_LOG_ERROR);
      free (news);
      free (olds);
      af_close (newf);
      stAbortThis(TRUE);
    }

    *news = *olds = '\0';
    
    if (fread (news, sizeof (char), (size_t)newSize, newf)
	  != newSize) 
      {
      stLog ("Couldn't read busy version.", ST_LOG_MSG);
      free (news);
      free (olds);
      af_close (newf);
      af_close (oldf);
      stAbortThis (TRUE);
    }
    if (fread (olds, sizeof (char), (size_t)oldSize, oldf)
	!= oldSize) 
      {
      stLog ("Couldn't read lastsaved version.", ST_LOG_MSG);
      free (news);
      free (olds);
      af_close (newf);
      af_close (oldf);
      stAbortThis (TRUE);
    }
    af_close (newf);
    af_close (oldf);
    if (memcmp (olds, news, (int)newSize)) {
      free (news); free (olds);
      return TRUE;
    }
    else {  /* Hmmm, looks like nothing has changed ... */
      free (news); free (olds);
      *realtruth = FALSE; /* return value may be a lie */
      if (forceFlag) {
	return TRUE;
      }
      if (stQuietFlag) {
	return FALSE;
      }
      if (isatty (fileno (stdin)) && isatty (fileno (stdout))) {
	sprintf (stMessage, "There are no changes with respect to the previously saved version.\nSave anyway ?");
	return (!stAskConfirm (stMessage, "no"));
      }
      else return FALSE;
    }
  }
}

LOCAL char *getText (msgText) char *msgText; {
  static char *txt = AT_EMPTYLOG;
  FILE *txtFile;
  struct stat statbuf;
  static int firsttime = TRUE;

  if (firsttime) {
    firsttime = FALSE;
    if (!msgText || !*msgText) {
      stLog ("empty descriptive text", ST_LOG_WARNING);
      return txt;
    }
    if (*msgText == '@') { /* filename */
      if ((txtFile = fopen (&msgText[1], "r")) == NULL) {
	sprintf (stMessage, "cannot open '%s' -- empty descriptive text.", &msgText[1]);
	stLog (stMessage, ST_LOG_WARNING);
	return txt;
      }
      if (fstat (fileno(txtFile), &statbuf) == -1) {
	sprintf (stMessage, "cannot stat '%s' -- empty descriptive text.", &msgText[1]);
	stLog (stMessage, ST_LOG_WARNING);
	return txt;
      }
      if ((txt = malloc ((unsigned)(statbuf.st_size+1))) == NULL) {
	stLog ("not enough memory for descriptive text.", ST_LOG_WARNING);
	return txt;
      }
      memset (txt, 0, (int) statbuf.st_size+1);
      fread (txt, sizeof (char), (size_t)statbuf.st_size, txtFile);
      fclose (txtFile);
    }
    else { /* plain message text */
      txt = msgText;
    }
  }
  return txt;
}

#define NOSHELL 127

LOCAL char *getnote (prompt, changeflg, force, xintent) 
     char *prompt, *xintent; 
     int changeflg, force; 
{
  char *tmpname = NULL, *edname, cmd[ST_MSGLEN], *intent;
  static char *notetxt, *rawnotetxt;
  FILE *tmpfil;
  struct stat statbuf;
  static int firsttime = TRUE;
  int istty;

  istty = isatty(fileno(stdin));

  if (xintent) {
    intent = (char *)malloc (strlen (xintent) +1);
    if (!intent) return (AT_EMPTYLOG);
    strcpy (intent, xintent);
  }
  else
    intent = "";

  if (stdinFlag) {
    char *edbuf;

    if (intent && istty && !stQuietFlag) {
      printf("%s\n", intent);
    }

    if (firsttime || (istty && !stQuietFlag && 
		      !stAskConfirm ("Use previous log message ?", "yes"))) {
      rawnotetxt = stGetFromStdin ('.');
      rawnotetxt = rawnotetxt ? rawnotetxt : "";

      if ((notetxt = malloc (strlen (rawnotetxt))) != (char *)NULL)
	strcpy (notetxt, rawnotetxt);
      else 
	notetxt = rawnotetxt; /* not nice but safe */
      firsttime = FALSE;
    }
    if ((edbuf = (char *)malloc (strlen (intent) + 
				 strlen (rawnotetxt) +1)) != (char *)NULL) {
      *edbuf = '\0';
      strcat (edbuf, intent);
      strcat (edbuf, rawnotetxt);
      notetxt = realloc (notetxt, strlen (edbuf));
      strcpy (notetxt, edbuf);
      free (edbuf);
    }
    return notetxt;
  }

  if (stQuietFlag ||
      (!istty)) {
    if (intent)
      goto retintent;
    else
      return AT_EMPTYLOG;
  }
  if ((!force) && (!firsttime) && notetxt) {
    if (stAskConfirm ("Use previous log message ?", "yes")) {
      return notetxt;
    }
    else {
      free (notetxt);
    }
  }
  firsttime = FALSE;
  if (changeflg) {
    if (!stAskConfirm (prompt, "yes"))
      if (intent)
	goto retintent;
      else
	return (AT_EMPTYLOG);
  }
  else {
    if (stAskConfirm ("The new version is unmodified. Comment it anyway ?", "no"))
      if (intent)
	goto retintent;
      else
	return (AT_EMPTYLOG);
  }
  
  tmpname = stTmpFile (NULL);
  stRegisterFile (tmpname);
  if (intent) {
    FILE *tfd;
    tfd = fopen (tmpname, "w");
    if (fwrite (intent, sizeof (char), strlen (intent), tfd) != strlen (intent)) {
      stLog ("write failure on tmp-file", ST_LOG_ERROR);
    }
    fclose (tfd);
  }
  edname = getenv ("EDITOR");
  if (!edname)
    edname = "vi";

  sprintf (cmd, "%s %s", edname, tmpname);
  sprintf (stMessage, "starting up %s ...", edname);
  stLog (stMessage, ST_LOG_MSG);
  if (system (cmd) == NOSHELL) {
    stLog ("couldn't execute shell", ST_LOG_ERROR);
    stUnRegisterFile (tmpname);
  }
  else {
    if ((tmpfil = fopen (tmpname, "r")) == NULL) {
      stLog ("empty logentry", ST_LOG_WARNING);
      stUnRegisterFile (tmpname);
      return AT_EMPTYLOG;
    }
    else {
      unlink (tmpname);
      stUnRegisterFile (tmpname);
      if (fstat (fileno(tmpfil), &statbuf) == -1) {
	sprintf (stMessage, "couldn't stat tmp file '%s'.", tmpname);
	stLog (stMessage, ST_LOG_WARNING);
	return AT_EMPTYLOG;
      }
      else {
	notetxt = malloc ((unsigned)(statbuf.st_size+1));
	if (!notetxt) {
	  stLog ("not enough memory for note text.", ST_LOG_WARNING);
	  return AT_EMPTYLOG;
	}
	memset (notetxt, 0, (int) statbuf.st_size+1);
	fread (notetxt, sizeof (char), (size_t)statbuf.st_size, tmpfil);
	fclose (tmpfil);
	stUnRegisterFile (tmpname);
	return notetxt;
      }
    }
  }
  stLog ("Couldn't start editor. Please check EDITOR environment variable.", ST_LOG_WARNING);
  stUnRegisterFile (tmpname);
  return AT_EMPTYLOG;   /* maybe we should try to read from stdin */
 retintent:
  if ((notetxt = malloc ((unsigned)(strlen (intent) + 1))) == (char *) NULL){
    stUnRegisterFile (tmpname);
    stLog ("not enough memory for note text.", ST_LOG_WARNING);
    return AT_EMPTYLOG;
  }
  strcpy (notetxt, intent);
  stUnRegisterFile (tmpname);
  return notetxt;
}

LOCAL void salvage (text) char *text; {
  static int serial = 0;
  FILE *fil;
  char fnam[ST_MSGLEN];

  sprintf (fnam, "%s%d-%d", stProgramName, getpid(), serial++);
  if (text) {
    if ((fil = fopen (fnam, "w")) != NULL) {
      if (fwrite (text, sizeof (char), strlen (text), fil) != strlen (text))
	stLog ("write failure on salvage file -- sorry!", ST_LOG_ERROR);
      else {
	sprintf (stMessage, "A temporary copy of your log can be found in %s.", fnam);
	stLog (stMessage, ST_LOG_MSG);
      }
    }
    fclose (fil);
  }
}

LOCAL int i_locked_gen (key, verbose)
     Af_key *key;
     int    verbose;
{
  Af_user *locker;
  uid_t myuid;

  myuid = (uid_t)geteuid();
  if (!atUserValid (locker = af_testlock(key))) {
    if (verbose) {
      sprintf (stMessage, "You must lock %s before saving.", af_retattr (key, AF_ATTBOUND));
      stLog (stMessage, ST_LOG_ERROR);
    }
    return FALSE;
  }
  else { /* there was some lock set */
    if (atUserUid (locker) != myuid) {
      if (verbose) {
	sprintf (stMessage, "%s already locked by %s.",
		 af_retattr (key, AF_ATTBOUND), atUserName (locker));
	stLog (stMessage, ST_LOG_ERROR);
      }
      return FALSE;
    }
    return TRUE;
  }
}

/*=====================
 *  exported routines
 *=====================*/

EXPORT void saveAFile (spath, busyAso) char *spath; Af_key *busyAso; {
/*
 * Save working file with the given name. Check for changes before saving.
 * Unless stQuietFlag is set, the user will be prompted for a note 
 * describing the changes to be saved.
 * On the locking strategy:
 * An author can save a busy version, only if he holds a lock (update
 * privilege) for the document history. When an archive is newly
 * created from a (busy) file, such a lock is assumed to be set.
 * Upon save operations - which are conceptually local to a user context -
 * the lock is released. 
 * Locks on certain documents can be obtained through means of 'reserve'
 * which is a User <--> Project transaction. Locally created documents
 * are placed under project discipline (or configuration control) the
 * first time they are submitted -- even if they have not been reserved
 * yet. Locks have token-character, i.e. they may be passed around, but
 * never ever be duplicated.
 */
 /*
  *  NOTE: use of variant attribute in af_getkey is not yet functional.
  *        The parameter, however, is necessary to occupy the slot.
  *        Implementation of variant selection may make it necessary
  *        to add another parameter to this procedure.
  */
  char *curName, afname[NAME_MAX], aftype[NAME_MAX], *note = NULL;
  int changedFlag = TRUE, busystate, have_lock = FALSE, fix_generation;
  int aliasInUse = FALSE;
  Af_key lastsave, skey, *newkey, *tmpkey;

  curName = stThisTransaction.tr_fname;

  strcpy (afname, af_afname (curName));
  strcpy (aftype, af_aftype (curName));
  /* give a little feedback ... */
  if (spath[0]) {
    sprintf (stMessage, "[%s]%s:", spath, curName);
  }
  else {
    sprintf (stMessage, "%s:", curName);
  }
  stLog (stMessage, ST_LOG_MSG);

  switch (fixType) {
  case AT_BIND_DATE:
    fixString = asctime (localtime (&fixDate));
  case AT_BIND_ALIAS:
    if ((tmpkey = atBindVersion (curName, fixString))) {
      fix_generation = af_retnumattr (tmpkey, AF_ATTGEN);
      af_dropkey (tmpkey);
    }
    else {
      sprintf (stMessage, "symbolic name %s not known for %s.", fixString, curName);
      stLog (stMessage, ST_LOG_ERROR);
      stAbortThis (FALSE);
    }
    break;
  case AT_BIND_VNUM:
    if (fixGen == AF_NOVNUM) {
      sprintf (stMessage, "invalid version number given with '-fix' option");
      stLog (stMessage, ST_LOG_ERROR);
      stAbortThis (FALSE);
    }
    else {
      fix_generation = fixGen;
    }
  }

  if (aliasName) {
    if ((tmpkey = atBindVersion (curName, aliasName))) {
      sprintf (stMessage, "symbolic name %s already in use by %s[%s] -- ignored.", 
	       aliasName, curName, af_retattr (tmpkey, AF_ATTVERSION));
      af_dropkey (tmpkey);
      stLog (stMessage, ST_LOG_WARNING);
      aliasInUse = TRUE;
    }
  }

  busystate = af_retnumattr (busyAso, AF_ATTSTATE);

  if (fail(af_getkey (spath, afname, aftype, fixType ? fix_generation : AF_LASTVERS,
		      AF_LASTVERS, &lastsave))) {
    if (fixType) {
      stLog ("No place to insert a version!", ST_LOG_ERROR);
      stAbortThis (FALSE);
    }
    checkAtFSerr(curName)
    stLog ("creating archive", ST_LOG_MSG);
    af_commit ();
    if (fail(af_saverev (busyAso, &skey, AF_LASTVERS, deltaFlag))) {
      stLog (af_errmsg (curName), ST_LOG_ERROR);
      stAbortThis(TRUE);
    }
    af_transaction ();
    newkey = &skey;
    stThisTransaction.tr_done = TRUE;
    if (historyLog) {
      note = getText (historyLog);
    }
    else {
      note = getnote ("How about describing the purpose of this document ?", 
		      changedFlag, TRUE, (char *)NULL);
    }
  }    /* This was handling of newly created archive */
  else {
    if ((busystate == AF_BUSY) &&
	(changed (busyAso, &lastsave, &changedFlag)) &&
	(have_lock = i_locked_gen (&lastsave, TRUE))) {
      Af_key previous_busy;

      if (fixType) {
	if (af_setbusy (busyAso, &lastsave, &previous_busy) < 0) {
	  sprintf (stMessage, "Can't set predecessor for fixed version of %s.", curName);
	  stLog (stMessage, ST_LOG_WARNING);
	  stLog (af_errmsg (curName), ST_LOG_ERROR);
	}
      }
      if (historyLog) {
	note = getText (historyLog);
      }
      else {
	char *intent = (char *)0;
	if (!keepLockFlag)
	  intent = af_retattr (&lastsave, AT_ATTINTENT);
	note = getnote ("Do you want to comment your modifications ?", changedFlag, 
			FALSE, intent);
	if (intent)
	  free (intent);
      }
      af_commit ();
      if (fail(af_saverev (busyAso, &skey, fixType ? fix_generation : AF_LASTVERS, deltaFlag))) {
	stLog (af_errmsg (curName), ST_LOG_ERROR);
	af_dropkey (&lastsave);
	salvage (note);
	stAbortThis (TRUE);
      }
      af_transaction ();
      if (fixType) {
	Af_key junk;

	if (af_setbusy (busyAso, &previous_busy, &junk) < 0) {
	  sprintf (stMessage, "WARNING: Can't reset original busy version link for %s.", curName);
	  stLog (stMessage, ST_LOG_ERROR);
	  stLog (af_errmsg (curName), ST_LOG_ERROR);
	}
      }
      newkey = &skey;
      af_setattr (newkey, AF_REMOVE, AT_ATTINTENT);

      /* if lock is kept, move intent to new "last saved" */
      if (keepLockFlag) {
	char *tmpAttr, *intent = NULL;
	if ((intent = af_retattr (&lastsave, AT_ATTINTENT))) {
	  if ((tmpAttr = malloc (strlen(intent)+strlen(AT_ATTINTENT)+2)) == NULL) {
	    stLog ("not enough memory.", ST_LOG_ERROR);
	    stAbortThis (TRUE);
	  }
	  strcpy (tmpAttr, AT_ATTINTENT);
	  strcat (tmpAttr, "=");
	  strcat (tmpAttr, intent);
	  af_setattr (newkey, AF_ADD, tmpAttr);
	  free (intent);
	}
      }
      af_setattr (&lastsave, AF_REMOVE, AT_ATTINTENT);
      atUnlock (&lastsave);
      af_lock (newkey, af_afuser (geteuid()));      
      stThisTransaction.tr_done = TRUE;
    }
    else { /* version is unchanged or not locked */
      sprintf (stMessage, "%s not saved. %s ", curName, curName);
      stLog (stMessage, ST_LOG_MSG | ST_LOG_NONL);
      (void) strcpy (stMessage, "left unchanged.");
      if (changedFlag) {
	stLog (stMessage, ST_LOG_MSG);
	af_dropkey (&lastsave);
	stAbortThis (FALSE);
      }
      if ((!keepLockFlag && atUserValid (af_testlock(&lastsave))) ||
	  !i_locked_gen (&lastsave, FALSE)) {
	if (busystate == AF_BUSY) {
	  Af_user *locker;
	  /* busy version shall be removed. check if it is locked */
	  if (atUserValid (locker = af_testlock (busyAso))) {
	    if (atUserUid (locker) != geteuid ()) {
	      /* locked by somebody else */
	      if (!stQuietFlag) {
		sprintf (stMessage, "No permission to delete %s (locked by %s).", curName, atUserName (locker));
		stLog (stMessage, ST_LOG_ERROR);
	      }
	    }
	  }
	  else {
	    /* the version is not locked. We must lock it to delete it. */
	    if (af_lock (busyAso, af_afuser (geteuid ())) == NULL) {
	      if (!stQuietFlag) {
		sprintf (stMessage, "Cannot lock %s for deletion -- not deleted.", curName);
		stLog (stMessage, ST_LOG_ERROR);
	      }
	    }
	  }
	  af_commit ();
	  if (fail(af_rm (busyAso))) {
	    if (!stQuietFlag) {
	      (void) strcpy (stMessage, "-- cannot remove.");
	      stLog (af_errmsg (stMessage), ST_LOG_ERROR);
	    }
	    strcpy (stMessage, "not removed.");
	  }
	  else { 
	    atUnlock (&lastsave);
	    strcpy (stMessage, "removed.");
	  }
	  af_transaction ();
	}
      }
      stLog (stMessage, ST_LOG_MSG);
      af_dropkey (&lastsave);
      stAbortThis(FALSE);
    }
  }
  /* If we get here, something has been saved -- set note */
  if (fail(af_snote (newkey, note))) {
    stLog (af_errmsg (curName), ST_LOG_ERROR);
    salvage (note);
  }
  if ((aliasName != NULL) && !aliasInUse)
    atSetVersAlias (newkey, aliasName);

  if (newGenFlag) {
    if (busystate != AF_BUSY) {
      Af_key ngkey;
      af_commit ();
      if (fail (af_newgen (newkey, &ngkey))) {
	stLog (af_errmsg (curName), ST_LOG_ERROR);
	af_dropkey (newkey);
        stAbortThis (TRUE);
      }
      af_transaction ();
      af_setattr (&ngkey, AF_REMOVE, AT_ATTALIAS);
      atUnlock (newkey); 
      af_dropkey (newkey);
      newkey = &ngkey;
    }
    else {
      vnumGen = af_retnumattr (&lastsave, AF_ATTGEN) + 1;
      vnumRev = 0;
    }
  }
  if (vnumGen != AF_NOVNUM && vnumRev != AF_NOVNUM) {
    af_lock (newkey, af_afuser(geteuid()));
    if (af_svnum (newkey, vnumGen, vnumRev) < 0) {
      sprintf (stMessage, "can't set version number %d.%d for %s", vnumGen, vnumRev, curName);
      stLog (af_errmsg (stMessage), ST_LOG_ERROR);
    }
  }
  sprintf (stMessage, "%s saved%s.", af_retattr (newkey, AF_ATTBOUND), changedFlag ? "" : " (no changes)");
  stLog (stMessage, ST_LOG_MSG);

  /* scan attribute file and set attributes */
  if (attrFile && *attrFile) {
    if (atSetAttrFile (newkey, attrFile) == FALSE)
      stLog (atErrMsg, ST_LOG_MSGERR);
  }

  /* Take care of old locks and new locks (in case they'll be kept */
  atUnlock (&lastsave); /* remove previous lock */
  if (!keepLockFlag) {
    atUnlock (newkey);
    if (busystate == AF_BUSY) {
      Af_user *locker;
      /* busy version shall be removed. check if it is locked */
      if (atUserValid (locker = af_testlock (busyAso))) {
	if (atUserUid (locker) != geteuid ()) {
	  /* locked by somebody else */
	  if (!stQuietFlag) {
	    sprintf (stMessage, "No permission to delete %s (locked by %s).", curName, atUserName (locker));
	    stLog (stMessage, ST_LOG_ERROR);
	  }
	}
      }
      else {
	/* the version is not locked. We must lock it to delete it. */
	if (af_lock (busyAso, af_afuser (geteuid ())) == NULL) {
	  if (!stQuietFlag) {
	    sprintf (stMessage, "Cannot lock %s for deletion -- not deleted.", curName);
	    stLog (stMessage, ST_LOG_ERROR);
	  }
	}
      }
      af_commit ();
      if (fail(af_rm (busyAso))) {
	if (!stQuietFlag) {
	  sprintf (stMessage, "cannot remove %s", curName);
	  stLog (af_errmsg (stMessage), ST_LOG_ERROR);
	}
      }
      else { 
	sprintf (stMessage, "%s removed.", curName);
	stLog (stMessage, ST_LOG_MSG);
      }
      af_transaction ();
    }
  }
  else { /* KEEPLOCK */
    /* if we keep the generation lock, the most recently
     * saved revision within the locked generation must be
     * version-locked. */
    uid_t myuid = (uid_t)geteuid ();

    if (atUserUid (af_lock (newkey, af_afuser(myuid))) != myuid) {
      if (fixType)
	sprintf (stMessage, "Cannot lock generation %d of %s.",
			fix_generation, curName);
      else 
	sprintf (stMessage, "Cannot lock %s-history.", curName);
      stLog (af_errmsg (stMessage), ST_LOG_ERROR);
    }
  }

  af_dropkey (newkey);
  af_dropkey (&lastsave);
}
