/* cmd-undo.c
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "config-options.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/fs/file-names.h"
#include "tla/libfsutils/rmrf.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/local-cache.h"
#include "tla/libarch/proj-tree-lint.h"
#include "tla/libarch/inode-sig.h"
#include "tla/libarch/undo.h"
#include "tla/libarch/cmdutils.h"
#include "tla/libarch/cmd.h"
#include "tla/libarch/cmd-undo.h"


/* __STDC__ prototypes for static functions */


static t_uchar * usage = "[options] [revision]";
static t_uchar * version_string = (cfg__std__package " from regexps.com\n"
                                   "\n"
                                   "Copyright 2003 Tom Lord\n"
                                   "\n"
                                   "This is free software; see the source for copying conditions.\n"
                                   "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
                                   "PARTICULAR PURPOSE.\n"
                                   "\n"
                                   "Report bugs to " cfg__tla_bug_mail ".\n"
                                   "\n"
                                   cfg__std__release_id_string
                                   "\n");

#define OPTS(OP) \
  OP (opt_help_msg, "h", "help", 0, \
      "Display a help message and exit.") \
  OP (opt_long_help, "H", 0, 0, \
      "Display a verbose help message and exit.") \
  OP (opt_version, "V", "version", 0, \
      "Display a release identifier string\n" \
      "and exit.") \
  OP (opt_archive, "A", "archive", 1, \
      "Override `my-default-archive'") \
  OP (opt_dir, "d", "dir DIR", 1, \
      "cd to DIR first") \
  OP (opt_output_dir, "o", "output PATCH-DIR", 1, \
      "save changeset in PATCH-DIR") \
  OP (opt_no_output, "n", "no-output", 0, \
      "do not save the changeset") \
  OP (opt_forward, "N", "forward", 0, \
      "pass the --forward option to `patch'") \
  OP (opt_quiet, "q", "quiet", 0, \
      "no progress reports while computing changeset") \
  OP (opt_unescaped, 0, "unescaped", 0, \
      "show filenames in unescaped form")


t_uchar arch_cmd_undo_help[] = ("undo and save changes in a project tree\n"
                                "Compute a patch set describing the changes from REVISION\n"
                                "to the project tree containing DIR.  Save the patch in\n"
                                "PATCH-DIR (which must not already exist) and apply the patch\n"
                                "in reverse to the project tree containing DIR.\n"
                                "\n"
                                "The effect is to remove local changes in the DIR project tree\n"
                                "but save them in a convenient form.\n"
                                "\n"
                                "If REVISION is not specified, the latest ancestor of the\n"
                                "default version of project tree is used.\n"
                                "\n"
                                "If REVISION is specified as a VERSION, the latest ancestor\n"
                                "of the project tree in that VERSION is used.\n"
                                "\n"
                                "If DIR is not specified, \".\" is assumed.\n"
                                "\n"
                                "If PATCH-DIR is not specified, a temporary file-name of the\n"
                                "matching ,,undo-* is used.\n"
                                "\n"
                                "If --no-output is specified, the patch set is not saved.\n"
                                "\n"
                                "See also \"tla redo -H\" and \"tla changes -H\".\n"); 
enum options
{
  OPTS (OPT_ENUM)
};

static struct opt_desc opts[] =
{
  OPTS (OPT_DESC)
    {-1, 0, 0, 0, 0}
};



int
arch_cmd_undo (t_uchar * program_name, int argc, char * argv[])
{
  int o;
  struct opt_parsed * option;
  t_uchar * dir = 0;
  t_uchar * output = 0;
  t_uchar * default_archive = 0;
  int forward = 0;
  int quiet = 0;
  int no_output = 0;
  int exit_status = 0;
  int escape_classes = arch_escape_classes;

  dir = str_save (0, ".");

  safe_buffer_fd (1, 0, O_WRONLY, 0);

  option = 0;

  while (1)
    {
      o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, version_string, arch_cmd_undo_help, opt_help_msg, opt_long_help, opt_version);
      if (o == opt_none)
        break;
      switch (o)
        {
        default:
          safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
          panic ("internal error parsing arguments");

        usage_error:
          opt_usage (2, argv[0], program_name, usage, 1);
          exit (1);

          /* bogus_arg: */
          safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string);
          goto usage_error;


        case opt_archive:
          {
            lim_free (0, default_archive);
            default_archive = str_save (0, option->arg_string);
            break;
          }

        case opt_dir:
          {
            lim_free (0, dir);
            dir = str_save (0, option->arg_string);
            break;
          }

        case opt_output_dir:
          {
            lim_free (0, output);
            output = str_save (0, option->arg_string);
            break;
          }

        case opt_no_output:
          {
            no_output = 1;
            break;
          }

        case opt_forward:
          {
            forward = 1;
            break;
          }

        case opt_quiet:
          {
            quiet = 1;
            break;
          }
        
        case opt_unescaped:
          {
            escape_classes = 0;
            break;
          }
        }
    }

  if (argc > 2)
    goto usage_error;

  {
    t_uchar * tree_root = 0;
    t_uchar * fqvsn = 0;
    t_uchar * tree_arch = 0;
    t_uchar * tree_version = 0;
    t_uchar * rvsnspec = 0;
    t_uchar * archive = 0;
    t_uchar * revision = 0;

    if (default_archive && !arch_valid_archive_name (default_archive))
      {
        safe_printfmt (2, "%s: invalid archive name: %s\n",
                       argv[0], default_archive);
        exit (2);
      }

    tree_root = arch_tree_root (0, dir, 0);

    if (!tree_root)
      {
        safe_printfmt (2, "%s: not in a project tree\n  dir: %s\n",
                       argv[0], dir);
        exit (2);
      }
    fqvsn = arch_tree_version (tree_root);
    if (!fqvsn)
      {
        safe_printfmt (2, "%s: no tree-version set\n  tree: %s\n",
                       argv[0], tree_root);
        exit (2);
      }
    
    tree_arch = arch_parse_package_name (arch_ret_archive, 0, fqvsn);
    tree_version = arch_parse_package_name (arch_ret_non_archive, 0, fqvsn);

    invariant (!!tree_arch);
    invariant (arch_valid_package_name (tree_version, arch_no_archive, arch_req_version, 0));

    if (argc == 2)
      {
        rvsnspec = arch_fqvsn_from_tree_and_input (argv[0], argv[1]);
        if (!rvsnspec)
          {
            safe_printfmt (2, "%s: illegal revision name: %s\n", argv[0], rvsnspec);
            exit (2);
          }
      }
    else
      {
        t_uchar * latest_log = 0;

        latest_log = arch_highest_patch_level (tree_root, tree_arch, tree_version);

        rvsnspec = str_alloc_cat_many (0, fqvsn, "--", latest_log, str_end);

        lim_free (0, latest_log);
      }

    lim_free (0, fqvsn);
    lim_free (0, tree_arch);
    lim_free (0, tree_version);

    archive = arch_parse_package_name (arch_ret_archive, default_archive, rvsnspec);
    revision = arch_parse_package_name (arch_ret_non_archive, 0, rvsnspec);

    if (!output)
      {
        output = arch_next_undo_changeset (0, tree_root);
      }


    {
      struct arch_tree_lint_result * lint = 0;

      if (!quiet)
        safe_printfmt (1, "* linting the source tree\n");
      lint = arch_tree_lint (tree_root);
      if (0 > arch_print_tree_lint_report ((quiet ? 2 : 1), lint, escape_classes))
        {
          exit (1);
        }
    }

    {
      t_uchar * orig_tree = 0;
      assoc_table inode_shortcut = 0;

      orig_tree = arch_find_or_make_local_copy ((quiet ? -1 : 1), tree_root, 0, 0, archive, revision);

      if (!orig_tree)
        {
          safe_printfmt (2, "%s: no local copies to compare to (%s/%s)\n  consider `add-pristine --help'\n",
                         argv[0], archive, revision);
          exit (2);
        }

      arch_read_inode_sig (0, &inode_shortcut, tree_root, archive, revision);

      arch_undo ((quiet ? -1 : 1), output, no_output,
                 tree_root, orig_tree, inode_shortcut, forward, escape_classes);

      lim_free (0, orig_tree);
      free_assoc_table (inode_shortcut);
    }


    lim_free (0, rvsnspec);
    lim_free (0, tree_root);
    lim_free (0, archive);
    lim_free (0, revision);
  }


  lim_free (0, dir);
  lim_free (0, output);
  lim_free (0, default_archive);

  return exit_status;
}



/* tag: Tom Lord Wed Jun  4 23:33:25 2003 (cmd-undo-changes.c)
 */
