/* Copyright 1994 NEC Corporation, Tokyo, Japan.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of NEC
 * Corporation not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  NEC Corporation makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * NEC CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 
 * NO EVENT SHALL NEC CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 
 * OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
 * PERFORMANCE OF THIS SOFTWARE. 
 */

/*LINTLIBRARY*/

#include	"RKintern.h"

#include	<stdio.h>

#ifdef __CYGWIN32__
#include <fcntl.h> /* for O_BINARY */
#endif

#ifdef sony_news
#include <sys/types.h>
#endif

#if defined(__STDC__) || defined(SVR4)
#include <time.h>
#define TIME_T time_t
#else
#define TIME_T long
#endif

#define	dm_td	dm_extdata.ptr

static void
freeTD(td)
     struct TD	*td;
{
  int	i;
  for (i = 0; i < (int)td->td_n; i++) {
    struct TN	*tn = &td->td_node[i];
    if (IsWordNode(tn)) {
      (void)free((char *)tn->tn_word->word);
      (void)free((char *)tn->tn_word);
    } else
      freeTD(tn->tn_tree);
  }
  if (td) {
    if (td->td_node)
      (void)free((char *)td->td_node);
    (void)free((char *)td);
  }
}

/* newTD: allocates a fresh node */
static TD *
newTD()
{
  struct TD	*td;

  td = (struct TD *)malloc(sizeof(struct TD));
  if (td) {
    td->td_n = 0;
    td->td_max = 1;
    if (!(td->td_node = (struct TN *)calloc(td->td_max, sizeof(struct TN)))) {
      freeTD(td);
      return((struct TD *)0);
    }
  }
  return(td);
}

/*
 * INSERT
 */
static TN *
extendTD(tdic, key, tw)
     struct TD		*tdic;
     Wchar		key;
     struct TW		*tw;
{
  int		i, j;
  struct TN	*tp;
  struct TW	*ntw;
    
  if (!(ntw = RkCopyWrec(tw)))
    return (struct TN *)0;
  tp = tdic->td_node;
  if (tdic->td_n >= tdic->td_max) {
    if (!(tp = (struct TN *)calloc(tdic->td_max + 1, sizeof(struct TN)))) {
      (void)free((char *)ntw->word);
      (void)free((char *)ntw);
      return (struct TN *)0;
    }
    for (i = 0; i < (int)tdic->td_n; i++)
      tp[i] = tdic->td_node[i];
    (void)free((char *)tdic->td_node);
    tdic->td_max++;
    tdic->td_node = tp;
  }
  for (i = 0; i < (int)tdic->td_n; i++)
    if (key < tp[i].tn_key)
      break;
  for (j = tdic->td_n; j > i; j--)
    tp[j] = tp[j - 1];
  tp[i].tn_flags = TN_WORD|TN_WDEF;
  tp[i].tn_key = key;
  tp[i].tn_word = ntw;
  tdic->td_n++;
  return(tp + i);
}

static int
yomi_equal(x, y, n)
     Wrec *x, *y;
     int n;
{
  int l;

  if ((l = (*y >> 1) & 0x3f) == ((*x >> 1) & 0x3f)) {
    if (*y & 0x80)
      y += 2;
    if (*x & 0x80)
      x += 2;
    x += (n << 1) + 2;
    y += (n << 1) + 2;
    for (; n < l ; n++) {
      if (*x++ != *y++ || *x++ != *y++)
	return(0);
    }
    return(1);
  }
  return(0);
}

static
Wchar
nthKey(w, n)
     Wrec	*w;
     int	n;
{
  if (n < (int)((*w >> 1) & 0x3f)) {
    if (*w & 0x80)
      w += 2;
    w += (n << 1) + 2;
    return((Wchar)((w[0] << 8) | w[1]));
  } else
    return((Wchar)0);
}

/*
 * defineTD -- テキスト辞書に定義する
 *
 * 引数
 *    dm    辞書
 *    tab   テキスト辞書ポインタ
 *    n     何文字目か(単位:文字)
 *    newW  登録するワードレコード
 *    nlen  不明
 */

static TN *
defineTD(dm, tab, n, newTW, nlen)
     struct DM	*dm;
     struct TD	*tab;
     int	n;
     struct TW	*newTW;
     int	nlen;
{
  int		i;
  Wchar		key;
  struct TN	*tn;
  struct ncache	*cache;
  struct TW	*mergeTW, *oldTW;
  Wrec		*oldW, *newW = newTW->word;

  key = nthKey(newW, n); n++;
  tn = tab->td_node;
  for (i = 0; i < (int)tab->td_n && tn->tn_key <= key; i++, tn++) {
    if (key == tn->tn_key) {
      if (IsWordNode(tn)) {
	struct TD	*td;

	oldTW = tn->tn_word;
	oldW = oldTW->word;
	if (!key|| yomi_equal(newW, oldW, n)) {
	  if ((cache = _RkFindCache(dm, (long)oldTW)) && cache->nc_count > 0)
	    return((struct TN *)0);
	  if (!(mergeTW = RkUnionWrec(newTW, oldTW)))
	    return((struct TN *)0);
	  tn->tn_word = mergeTW;
	  (void)free((char *)oldW);
	  (void)free((char *)oldTW);
	  tn->tn_flags |= TN_WDEF;
	  if (cache) {
	    _RkRehashCache(cache, (long)tn->tn_word);
	    cache->nc_word = mergeTW->word;
	  }
	  return tn;
	}
	if (!(td = newTD()))
	  return((struct TN *)0);
	td->td_n = 1;
	key = nthKey(oldW, n);
	td->td_node[0].tn_key = key;
	td->td_node[0].tn_flags |= TN_WORD;
	td->td_node[0].tn_word = oldTW;
	tn->tn_flags &= ~TN_WORD;
	tn->tn_tree = td;
      }
      return defineTD(dm, tn->tn_tree, n, newTW, nlen);
    }
  }
  return extendTD(tab, key, newTW);
}

static int
enterTD(dm, td, gram, word)
     struct DM		*dm;
     struct TD		*td;
     struct RkKxGram	*gram;
     Wchar		*word;
{
  struct TW	tw;
  int ret = -1;
#ifndef USE_MALLOC_FOR_BIG_ARRAY
  Wrec		wrec[RK_LINE_BMAX*10];
#else
  Wrec *wrec = (Wrec *)malloc(sizeof(Wrec) * RK_LINE_BMAX * 10);
  if (!wrec) {
    return ret;
  }
#endif

  if (word[0] == (Wchar)'#') {
    ret = 1;
  }
  else if (RkParseOWrec(gram, word, wrec, RK_LINE_BMAX * 10, tw.lucks)) {
    struct TN *tn;
    
    tw.word = wrec;
    tn = defineTD(dm, td, 0, &tw, RK_LINE_BMAX * 10);
    if (tn) {
      tn->tn_flags &= ~(TN_WDEF|TN_WDEL);
      ret = 0;
    } else {
      ret = -1;
    }
  }
  else {
    ret = 1;
  }
#ifdef USE_MALLOC_FOR_BIG_ARRAY
  (void)free((char *)wrec);
#endif
  return ret;
}

/*
 * DELETE
 */
static void
shrinkTD(td, key)
     struct TD *td;
     Wchar key;
{
  int		i;
  struct TN	*tn = td->td_node;
    
  for (i = 0; i < (int)td->td_n; i++) {
    if (key == tn[i].tn_key) {
      while (++i < (int)td->td_n) tn[i - 1] = tn[i];
      td->td_n--;
      break;
    }
  }
}

/*
 * deleteTD -- テキスト辞書から単語定義を取り除く
 *
 * 引数
 *    dm    辞書
 *    tab   テキスト辞書
 *    n     何文字目か
 *    newW  定義するワードレコード
 */
static int
deleteTD(dm, tab, n, newW)
     struct DM	*dm;
     struct TD	**tab;
     int	n;
     Wrec	*newW;
{
  struct TD	*td = *tab;
  int		i;
  Wchar		key;
    
  key = nthKey(newW, n); n ++;
  for (i = 0; i < (int)td->td_n; i++) {
    struct TN	*tn = &td->td_node[i];
    
    if (key == tn->tn_key ) {
      if (IsWordNode(tn)) {
	struct TW	*oldTW = tn->tn_word;
	Wrec		*oldW = oldTW->word;
	
	if (!key || yomi_equal(newW, oldW, n)) {
	  struct ncache	*cache = _RkFindCache(dm, (long)oldTW);
	  
	  if (!cache || cache->nc_count <= 0) {
	    struct TW	*subW, newTW;

	    newTW.word = newW;
	    subW = RkSubtractWrec(oldTW, &newTW);
	    (void)free((char *)oldW);
	    (void)free((char *)oldTW);
	    if (subW) {
	      tn->tn_word = subW;
	      tn->tn_flags |= TN_WDEL;
	      if (cache) {
		_RkRehashCache(cache, (long)subW);
		cache->nc_word = subW->word;
	      }
	      return(0);
	    } else {
	      if (cache)
		_RkPurgeCache(cache);
	      shrinkTD(td, key);
	    }
	  }
	}
      } else
	if (deleteTD(dm, &tn->tn_tree, n, newW))
	  shrinkTD(td, key);
      if (td->td_n <= 0) {
	(void)free((char *)(td->td_node));
	(void)free((char *)td);
	*tab = (struct TD *)0;
	return(1);
      } else
	return(0);
    }
  }
  return(0);
}

/*
 * OPEN
 */
/*ARGSUSED*/
int
_Rktopen(dm, file, mode, gram)
     struct DM	*dm;
     char	*file;
     int	mode;
     struct RkKxGram	*gram;
{
  struct DF	*df = dm->dm_file;
  struct DD	*dd = df->df_direct;
  struct TD	*xdm;
  FILE		*f;
  long		offset = 0L;
  int		ecount = 0;
  int ret = -1;
#ifndef USE_MALLOC_FOR_BIG_ARRAY
  char		line[RK_LINE_BMAX*10];
  Wchar	wcline[RK_LINE_BMAX*10];
#else
  char *line = malloc(RK_LINE_BMAX * 10);
  Wchar *wcline = (Wchar *)malloc(sizeof(Wchar) * RK_LINE_BMAX * 10);
  if (!line || !wcline) {
    if (line) (void)free(line);
    if (wcline) (void)free((char *)wcline);
    return ret;
  }
#endif
    
  if (!df->df_rcount) {
    if (close(open(file, 0)))
      goto return_ret;
    df->df_flags |= DF_EXIST;
    dm->dm_flags |= DM_EXIST;
    if (!close(open(file, 2)))
      dm->dm_flags |= DM_WRITABLE;
  }
  if (!(dm->dm_flags & DM_EXIST))
    ;
#ifdef __EMX__
  else if (!(f = fopen(file, "rt"))) {
#else
  else if (!(f = fopen(file, "r"))) {
#endif
    df->df_flags &= ~DF_EXIST;
    dm->dm_flags &= ~DM_EXIST;
  }
  else if (!(xdm = newTD())) {
    fclose(f);
  }
  else {
    while (fgets((char *)line, RK_LINE_BMAX*10, f)) {
      int		sz;

      offset += strlen(line);
      sz = RkCvtWide(wcline, RK_LINE_BMAX*10, line, strlen(line));
      if (sz < RK_LINE_BMAX*10 - 1) {
	if ( enterTD(dm, xdm, gram, wcline) < 0 )
	  ecount++;
      } else
	ecount++;
    }
    (void)fclose(f);
    dm->dm_offset = 0L;
    df->df_size = offset;
    if (ecount) {		
      freeTD((struct TD *)xdm);
      dm->dm_td = (pointer)0;
      dm->dm_flags &= ~DM_EXIST;
      ret = -1;
    } else {
      dm->dm_td = (pointer)xdm;
      if (df->df_rcount++ == 0)
	dd->dd_rcount++;
      ret = 0;
    };
  }
 return_ret:
#ifdef USE_MALLOC_FOR_BIG_ARRAY
  (void)free((char *)wcline);
  (void)free(line);
#endif
  return ret;
}

/*
 * CLOSE
 */
static int writeTD pro((struct TD *, struct RkKxGram *, int));

static int
writeTD(td, gram, fdes)
     struct TD		*td;
     struct RkKxGram	*gram;
     int		fdes;
{
  int	i, tmpres;
  int	ecount = 0;
  Wchar *wcline = (Wchar *)0;

  wcline = (Wchar *)malloc(RK_WREC_BMAX * sizeof(Wchar));
  if (wcline) {
    for (i = 0; i < (int)td->td_n; i++) {
      struct TN		*tn = &td->td_node[i];
      unsigned char	*line;
      Wchar		*wc;
      int		sz;
    
      if (IsWordNode(tn)) {
        wc = _RkUparseWrec(gram, tn->tn_word->word, wcline,
                          RK_LINE_BMAX * sizeof(Wchar), tn->tn_word->lucks, 1);
        if (wc) {
          sz = RkCvtNarrow((char *)0, 9999, wcline, (int)(wc - wcline));
          if (sz > RK_LINE_BMAX
              && !(wc = _RkUparseWrec(gram, tn->tn_word->word, wcline,
                         RK_LINE_BMAX * sizeof(Wchar), tn->tn_word->lucks, 0)))
            ecount++;
          else {
	    line = (unsigned char *)malloc( RK_LINE_BMAX*3 );
	    if(line) { 
              sz = RkCvtNarrow((char *)line, RK_LINE_BMAX,
                                wcline, (int)(wc - wcline));
#ifdef USE_SJIS_TEXT_DIC
	      line[sz++] = '\r';
#endif
              line[sz++] = '\n';
              line[sz] = 0;
	      tmpres = write(fdes, line, sz);
              if (tmpres != sz)
                ecount++;
	      free(line);
	    } else
	      ecount++;
          }
        } else
          ecount++;
      } else
        ecount += writeTD(tn->tn_tree, gram, fdes);
    }
    free((char *)wcline);
    return(ecount);
  }
  return 0;
}

int	
_Rktclose(dm, file, gram)
     struct DM	*dm;
     char	*file;
     struct RkKxGram	*gram;
{
  struct DF	*df = dm->dm_file;
  struct TD	*xdm = (struct TD *)dm->dm_td;
  int		ecount;
  int		fdes;
  
#ifndef USE_MALLOC_FOR_BIG_ARRAY
  char	backup[RK_PATH_BMAX];
  char	header[RK_LINE_BMAX];
  char	whattime[RK_LINE_BMAX];
#else
  char *backup, *header, *whattime;
  backup = malloc(RK_PATH_BMAX);
  header = malloc(RK_LINE_BMAX);
  whattime = malloc(RK_LINE_BMAX);
  if (!backup || !header || !whattime) {
    if (backup) (void)free(backup);
    if (header) (void)free(header);
    if (whattime) (void)free(whattime);
    return 0;
  }
#endif
    
  _RkKillCache(dm);
  if (dm->dm_flags & DM_UPDATED) {
    char	*p = rindex(file, '/');
    
    if (p) {
      strcpy(backup, file);
      p++;
      backup[(int)(p - file)] = '#';
      strcpy(&backup[(int)(p-file) + 1], p);
    };
    
    fdes = creat(backup, (unsigned)0666);
#ifdef __CYGWIN32__
    setmode(fdes, O_BINARY);
#endif
    ecount = 0;
    if (fdes >= 0) {
      int	n;

      TIME_T	tloc;

      tloc = time(0);
      strcpy(whattime, ctime(&tloc));
      whattime[strlen(whattime)-1] = 0;
      (void)strcpy(header, "#*DIC ");
      (void)strcat(header, dm->dm_nickname);
      (void)strcat(header, " [");
      (void)strcat(header, whattime);
      (void)strcat(header, "]");
#ifdef USE_SJIS_TEXT_DIC
      (void)strcat(header, "\r\n");
#else
      (void)strcat(header, "\n");
#endif
      n = strlen(header);
      if (write(fdes, header, n) != n) {
	ecount++;
      }
      ecount += writeTD(xdm, gram, fdes);
      (void)close(fdes);
    } else
      ecount++;

    if (!ecount) {
#ifdef HAVE_RENAME
#ifdef __EMX__
      unlink(file);
#endif
      rename(backup, file);
#else
      unlink(file);
      if (link(backup, file) == 0) {
	unlink(backup);
      }
#endif
    }
    dm->dm_flags &= ~DM_UPDATED;
  };
  freeTD((struct TD *)xdm);
  --df->df_rcount;
#ifdef USE_MALLOC_FOR_BIG_ARRAY
  (void)free(backup);
  (void)free(header);
  (void)free(whattime);
#endif
  return 0;
}

int
_Rktsearch(cx, dm, key, n, nread, maxcache, cf)
     struct RkContext	*cx;
     struct DM		*dm;
     Wchar		*key;
     int		n;
     struct nread	*nread;
     int		maxcache;
     int		*cf;
{
  Wchar		uniqAlnum();
  struct TD	*xdm = (struct TD *)dm->dm_td;
  int		nc = 0;
  int		i, j;
    
  *cf = 0;
  for (j = 0; j < n;) {
    Wchar	k = uniqAlnum(key[j++]);
    struct TN	*tn;
    
    tn = xdm->td_node;
    for (i = 0; i < (int)xdm->td_n && tn->tn_key <= k; i++, tn++) {
      if (k == tn->tn_key) {
	if (IsWordNode(tn)) {
	  Wrec	*w;
	  int	l;

	  w = tn->tn_word->word;
	  l = (*w >> 1) & 0x3f;
	  if (*w & 0x80)
	    w += 2;
	  w += 2;
	  if (_RkEql(key, w, l)) {
	    if (l > n)
	      (*cf)++;
	    else if (nc < maxcache) {
	      nread[nc].cache = _RkReadCache(dm, (long)tn->tn_word);
	      if (nread[nc].cache) {
		nread[nc].nk = l;
		nread[nc].csn = 0L;
		nread[nc].offset = 0L;
		nc++;
	      } else
		(*cf)++;
	    } else 
	      (*cf)++;
	  }
	  return nc;
	} else {
	  struct TD	*ct = tn->tn_tree;
	  struct TN	*n0 = &ct->td_node[0];
	  
	  if (ct->td_n && !n0->tn_key) {
	    unsigned char	*w;
	    int			l;
	    w = n0->tn_word->word;
	    l = (*w >> 1) & 0x3f;
	    if (*w & 0x80)
	      w += 2;
	    w += 2;
	    if (_RkEql(key, w, l)) {
	      if (l > n)
		(*cf)++;
	      else if (nc < maxcache) {
		nread[nc].cache = _RkReadCache(dm, (long)n0->tn_word);
		if (nread[nc].cache) {
		  nread[nc].nk = l;
		  nread[nc].csn = 0L;
		  nread[nc].offset = 0L;
		  nc++;
		} else
		  (*cf)++;
	      } else
		(*cf)++;
	    }
	  }
	  xdm = ct;
	  goto	cont;
	}
      }
    }
    break;
  cont:
    ;
  }
  if (n <= 0)
    cx->poss_cont++;
  return nc;
}

/*
 * IO
 */
/*ARGSUSED*/
int	
_Rktio(dm, cp, io)
     struct DM		*dm;
     struct ncache	*cp;
     int		io;
{
  if (io == 0) {
    cp->nc_word = ((struct TW *)cp->nc_address)->word;
    cp->nc_flags |= NC_NHEAP;
  }
  return 0;
}

#define TEMPDIC_WRECSIZE 2048

/*
 * CTL
 */
int	
_Rktctl(dm, qm, what, arg, gram)
     struct DM	*dm;
     struct DM	*qm; /* no use : dummy*/
     int	what;
     Wchar	*arg;
     struct RkKxGram	*gram;
/* ARGSUSED */
{
  struct TD	*xdm = (struct TD *)dm->dm_td;
  int		status = 0;
  struct TW	tw;
  unsigned long	lucks[2];
#ifndef USE_MALLOC_FOR_BIG_ARRAY
  Wrec		wrec[TEMPDIC_WRECSIZE];
#else
  Wrec *wrec = (Wrec *)malloc(sizeof(Wrec) * TEMPDIC_WRECSIZE);
  if (!wrec) {
    return status;
  }
#endif
  
  switch(what) {
  case DST_DoDefine:
    if ((dm->dm_flags & (DM_WRITABLE | DM_WRITEOK)) !=
	(DM_WRITABLE | DM_WRITEOK)) /* !(writable and write ok) */
      status = -1;
    else if (!RkParseOWrec(gram, arg, wrec, TEMPDIC_WRECSIZE, lucks))
      status = -1;
    else {
      tw.word = wrec;
      status = defineTD(dm, xdm, 0, &tw, TEMPDIC_WRECSIZE) ? 0 : -1;
      dm->dm_flags |= DM_UPDATED;
    }
    break;
  case DST_DoDelete:
    if ((dm->dm_flags & (DM_WRITABLE | DM_WRITEOK)) !=
	(DM_WRITABLE | DM_WRITEOK)) /* !(writable and write ok) */
      status = -1;
    else if (!RkParseOWrec(gram, arg, wrec, TEMPDIC_WRECSIZE, lucks))
      status = -1;
    else if (deleteTD(dm, &xdm, 0, wrec)) {
      xdm = newTD();
      dm->dm_td = (pointer)xdm;
    }
    dm->dm_flags |= DM_UPDATED;
    break;
  }
#ifdef USE_MALLOC_FOR_BIG_ARRAY
  (void)free((char *)wrec);
#endif
  return status;
}

int
_Rktsync(cx, dm, qm)
     struct RkContext *cx;
     struct DM	*dm, *qm;
/* ARGSUSED */
{
  struct RkKxGram  *gram = cx->gram->gramdic;
  struct DF	*df_p;
  struct DD     *dd_p;
  struct TD	*xdm = (struct TD *)dm->dm_td;
  int		ecount;
  char	        *file;
  int ret = -1;
  int		fdes;

#ifndef USE_MALLOC_FOR_BIG_ARRAY
  char	backup[RK_PATH_BMAX];
  char	header[RK_LINE_BMAX];
  char	whattime[RK_LINE_BMAX];
#else
  char *backup, *header, *whattime;
  backup = malloc(RK_PATH_BMAX);
  header = malloc(RK_LINE_BMAX);
  whattime = malloc(RK_LINE_BMAX);
  if (!backup || !header || !whattime) {
    if (backup) (void)free(backup);
    if (header) (void)free(header);
    if (whattime) (void)free(whattime);
    return ret;
  }
#endif

  df_p = dm->dm_file;
  dd_p = df_p->df_direct;
  file  = _RkCreatePath(dd_p, df_p->df_link);
  if (file) {
    if (dm->dm_flags & DM_UPDATED) {
      char	*p = rindex(file, '/');
      
      if (p) {
	strcpy(backup, file);
	p++;
	backup[(int)(p - file)] = '#';
	strcpy(&backup[(int)(p-file) + 1], p);
      };

      fdes = creat(backup, (unsigned)0666);
#ifdef __CYGWIN32__
      setmode(fdes, O_BINARY);
#endif
      ecount = 0;
      if (fdes >= 0) {
	int	n;
	TIME_T	tloc;
	
	tloc = time(0);
	strcpy(whattime, ctime(&tloc));
	whattime[strlen(whattime)-1] = 0;
	(void)strcpy(header, "#*DIC ");
	(void)strcat(header, dm->dm_nickname);
	(void)strcat(header, " [");
	(void)strcat(header, whattime);
	(void)strcat(header, "]");
#ifdef USE_SJIS_TEXT_DIC
	(void)strcat(header, "\r\n");
#else
	(void)strcat(header, "\n");
#endif
	n = strlen(header);
	if (write(fdes, header, n) != n) { 
	  ecount++;
	}
	ecount += writeTD(xdm, gram, fdes);
	(void)close(fdes);
      } else
	ecount++;
      
      if (!ecount) {
#ifdef HAVE_RENAME
#ifdef __EMX__
	unlink(file);
#endif
	rename(backup, file);
#else
	unlink(file);
	if (link(backup, file) == 0) {
	  unlink(backup);
	}
#endif
	dm->dm_flags &= ~DM_UPDATED;
      } else {
	(void)free(file);
	ret = -1;
	goto return_ret;
      }
    };
    (void)free(file);
    ret = 0;
  } 
 return_ret:
#ifdef USE_MALLOC_FOR_BIG_ARRAY
  (void)free(backup);
  (void)free(header);
  (void)free(whattime);
#endif
  return ret;
}
