/* $Id: backup.c,v 1.5 2002/03/10 21:26:49 richdawe Exp $ */

/*
 *  backup.c - Backup functions for zippo
 *  Copyright (C) 2001 by Richard Dawe
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "common.h"

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>

/* libzippo includes */
#include <libzippo/backup.h>
#include <libzippo/package.h>
#include <libzippo/util.h>

static char BACKUP_SUFFIX[] = ".old";

/* -------------------
 * - backup_get_path -
 * ------------------- */

/*
 * backup_get_path() returns a path for backing up package files. It is
 * a combination of the 'backup_prefix', the package DSM or manifest name
 * and 'BACKUP_SUFFIX'.
 *
 * On success, a pointer to a static buffer is returned - make a copy of
 * the string if you need to modify it. On failure, NULL is returned.
 *
 * The returned string is guaranteed to have a trailing forward slash.
 */

const char *
backup_get_path (PACKAGE_INFO *package, const char *backup_prefix)
{
  static char  buf[PATH_MAX];
  char        *name = NULL;
  size_t       len  = 0;

  /* Use DSM or manifest name? */
  if (strlen(package->dsm_name) != 0)
    name = package->dsm_name;
  else if (strlen(package->manifest) != 0)
    name = package->manifest;
  else
    return(NULL);

  /* Copy prefix for munging */
  strcpy(buf, backup_prefix);
  forwardslashify(buf);

  /* Calculate length of path */
  len = strlen(buf);
  if (!ends_with_slash(buf))
    len++;
  len += strlen(name);
  len += strlen(BACKUP_SUFFIX);
  len++; /* for trailing slash */

  /* Too long */
  if (len > PATH_MAX)
    return(NULL);

  /* Build string. */
  if (!ends_with_slash(buf))
    strcat(buf, "/");
  strcat(buf, name);
  strcat(buf, BACKUP_SUFFIX);
  strcat(buf, "/");
  forwardslashify(buf);

  return(buf);
}

/* ---------------
 * - backup_file -
 * --------------- */

/*
 * backup_package_file() copies the specified 'path' from 'prefix' into
 * 'backup_path'. 'prefix' is the directory the package was originally
 * installed in; 'path' is the relative path under 'prefix'; 'backup_path'
 * is the backup path for that package, as returned by backup_get_path().
 *
 * On success, 0 is returned. On failure, -1 is returned and 'errno' will
 * be set appropriately.
 */

int
backup_file (const char *path, const char *prefix, const char *backup_path)
{
  char    src_path[PATH_MAX];
  char    dest_path[PATH_MAX];
  char    dest_dir[PATH_MAX];
  mode_t  mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
  int     src_is_dir;
  int     ret  = 0;
  char   *p    = NULL;

  /* Make sure the backup prefix exists. */
  ret = recursive_mkdir(backup_path, mode);
  if ((ret != 0) && (errno != EEXIST)) {
    /* Pass on errno to caller */
    return(-1);
  }

  /* Build paths */
  strcpy(src_path, prefix);
  forwardslashify(src_path);
  if (!ends_with_slash(src_path))
    strcat(src_path, "/");
  strcat(src_path, path);

  strcpy(dest_path, backup_path);
  forwardslashify(dest_path);
  if (!ends_with_slash(dest_path))
    strcat(dest_path, "/");
  strcat(dest_path, path);

  /* Make sure source file exists. */
  if (access(src_path, R_OK) != 0) {
    errno = ENOENT;
    return(-1);
  }

  /* If the source is a directory, just create it in backup directory.
   * Otherwise copy source to dest. */
  src_is_dir = isdir(src_path);

  if (src_is_dir > 0) {
    /* Directory */
    ret = recursive_mkdir(dest_path, mode);
    if ((ret != 0) && (errno != EEXIST)) {
      /* Pass on errno to caller */
      return(-1);
    }

    /* OK */
    ret = 0;
  } else if (src_is_dir == 0) {
    /* File */

    /* Make the directory to store the file. */
    strcpy(dest_dir, dest_path);
    p = strrchr(dest_dir, '/');
    if (p != NULL) {
      *p = '\0';

      ret = recursive_mkdir(dest_dir, mode);
      if ((ret != 0) && (errno != EEXIST)) {
	/* Pass on errno to caller */
	return(-1);
      }
    }

    /* Copy the file */
    ret = copy_file(src_path, dest_path);
    if (!ret) {
      /* TODO: Error? EIO? Does 'errno' get set? If so, pass errno through. */
      ret = -1;
    } else {
      /* OK */
      ret = 0;
    }
  } else {
    /* stat() failed - pass on errno to caller */
    ret = -1;
  }

  return(ret);
}
