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

#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/arrays/ar.h"
#include "tla/libfsutils/ensure-dir.h"
#include "tla/libfsutils/safety.h"
#include "tla/libfsutils/tmp-files.h"
#include "tla/libfsutils/read-line.h"
#include "tla/libarch/arch.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/cmd.h"
#include "tla/libarch/cmd-get.h"
#include "tla/libarch/configs.h"


/* __STDC__ prototypes for static functions */



t_uchar *
arch_config_path (t_uchar * tree_root, t_uchar * config_name)
{
  t_uchar * rel = 0;
  t_uchar * answer = 0;

  invariant (arch_valid_config_name (config_name));


  rel = file_name_in_vicinity (0, "configs", config_name);
  if (safe_access(rel, F_OK))
    {
      rel = file_name_in_vicinity (0, "", config_name);
    }
  answer = file_name_in_vicinity (0, tree_root, rel);

  lim_free (0, rel);
  return answer;
}


rel_table
arch_read_config (t_uchar * tree_root, t_uchar * config_name)
{
  t_uchar * config_path = 0;
  int in_fd;
  rel_table answer = rel_table_nil;

  invariant (arch_valid_config_name (config_name));

  config_path = arch_config_path (tree_root, config_name);

  in_fd = safe_open (config_path, O_RDONLY, 0);

  while (1)
    {
      t_uchar * line = 0;
      t_uchar * pos;

      line = read_line_from_fd (in_fd);

      if (!line)
        break;

      for (pos = line; char_is_space (*pos); ++pos)
        ;

      if (*pos && (*pos != '#'))
        {
          t_uchar * end;
          t_uchar * loc = 0;
          t_uchar * rev = 0;

          for (pos = line; char_is_space (*pos); ++pos)
            ;

          for (end = pos; *end && !char_is_space (*end); ++end)
            ;

          if (end == pos)
            {
              safe_printfmt (2, "arch_read_config:  illegal config file (%s)\n", config_name);
              exit (2);
            }

          loc = pika_save_unescape_iso8859_1_n (0, 0, pos, end - pos );

          for (pos = end; char_is_space (*pos); ++pos)
            ;

          for (end = pos; *end && !char_is_space (*end); ++end)
            ;

          if (end == pos)
            {
              safe_printfmt (2, "arch_read_config:  illegal config file (%s)\n", config_name);
              exit (2);
            }

          rev = pika_save_unescape_iso8859_1_n (0, 0, pos, end - pos );

          if (!is_non_upwards_relative_path (loc) || !arch_valid_package_name (rev, arch_maybe_archive, arch_req_package, 1))
            {
              safe_printfmt (2, "arch_read_config:  illegal config file (%s)\n", config_name);
              exit (2);
            }

          rel_add_records (&answer, rel_make_record_2_taking (rel_make_field_str (loc), rel_make_field_str (rev)), rel_record_null);

          lim_free (0, loc);
          lim_free (0, rev);
        }

      lim_free (0, line);
    }

  lim_free (0, config_path);

  return answer;
}


rel_table
arch_config_from_tree (t_uchar * tree_root, rel_table config_in)
{
  int here_fd;
  int x;
  rel_table answer = rel_table_nil;

  here_fd = safe_open (".", O_RDONLY, 0);

  for (x = 0; x < rel_n_records (config_in); ++x)
    {
      t_uchar * subtree_path_spec = 0;
      t_uchar * subtree_path = 0;
      t_uchar * archive = 0;
      t_uchar * version = 0;
      t_uchar * subtree_root = 0;
      t_uchar * level = 0;
      t_uchar * revision = 0;
      t_uchar * fqr = 0;

      subtree_path_spec = file_name_in_vicinity (0, tree_root, rel_peek_str (config_in, x, 0));
      safe_chdir (subtree_path_spec);
      subtree_path = safe_current_working_directory ();
      safe_fchdir (here_fd);

      subtree_root = arch_tree_root (0, subtree_path, 0);
      invariant (subtree_root && !str_cmp (subtree_root, subtree_path));

      archive = arch_parse_package_name (arch_ret_archive, 0, rel_peek_str (config_in, x, 1));
      if (arch_valid_package_name (rel_peek_str (config_in, x, 1), arch_maybe_archive, arch_req_version, 1))
        version = arch_parse_package_name (arch_ret_package_version, 0, rel_peek_str (config_in, x, 1));
      else
        {
          t_uchar * package = 0;

          package = arch_parse_package_name (arch_ret_package, 0, rel_peek_str (config_in, x, 1));
          version = arch_latest_logged_version (subtree_root, archive, package);

          lim_free (0, package);
        }

      level = arch_highest_patch_level (subtree_root, archive, version);

      if (level)
        {
          revision = str_alloc_cat_many (0, version, "--", level, str_end);
          fqr = arch_fully_qualify (archive, revision);
        }
      else
        fqr = arch_fully_qualify (archive, version);

      rel_add_records (&answer, rel_make_record_2_taking (rel_get_field (config_in, x, 0), rel_make_field_str (fqr)), rel_record_null);

      lim_free (0, subtree_path_spec);
      lim_free (0, subtree_path);
      lim_free (0, archive);
      lim_free (0, version);
      lim_free (0, subtree_root);
      lim_free (0, level);
      lim_free (0, revision);
      lim_free (0, fqr);
    }


  safe_close (here_fd);

  return answer;
}


int
arch_begin_new_config (t_uchar * tree_root, t_uchar * name, int force)
{
  int ign;
  t_uchar * config_file = 0;
  t_uchar * config_dir = 0;
  t_uchar * name_tail = 0;
  t_uchar * tmp_tail = 0;
  t_uchar * config_tmp = 0;
  int answer;

  invariant (arch_valid_config_name (name));

  config_file = arch_config_path (tree_root, name);
  config_dir = file_name_directory_file (0, config_file);
  name_tail = file_name_tail (0, name);
  tmp_tail = str_alloc_cat (0, ",,", name_tail);
  config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail);

  if (!force && !safe_access (config_file, F_OK))
    {
      safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name);
      exit (2);
    }

  ensure_directory_exists (config_dir);

  vu_unlink (&ign, config_tmp);
  answer = safe_open (config_tmp, O_WRONLY | O_CREAT | O_EXCL, 0666);

  lim_free (0, config_file);
  lim_free (0, config_dir);
  lim_free (0, name_tail);
  lim_free (0, tmp_tail);
  lim_free (0, config_tmp);

  return answer;
}


void
arch_finish_new_config (int fd, t_uchar * tree_root, t_uchar * name, int force)
{
  t_uchar * config_file = 0;
  t_uchar * config_dir = 0;
  t_uchar * name_tail = 0;
  t_uchar * tmp_tail = 0;
  t_uchar * config_tmp = 0;

  invariant (arch_valid_config_name (name));

  config_file = arch_config_path (tree_root, name);
  config_dir = file_name_directory_file (0, config_file);
  name_tail = file_name_tail (0, name);
  tmp_tail = str_alloc_cat (0, ",,", name_tail);
  config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail);

  safe_close (fd);

  if (!force && !safe_access (config_file, F_OK))
    {
      safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name);
      exit (2);
    }

  safe_rename (config_tmp, config_file);

  lim_free (0, config_file);
  lim_free (0, config_dir);
  lim_free (0, name_tail);
  lim_free (0, tmp_tail);
  lim_free (0, config_tmp);
}


void
arch_build_config (t_uchar * tree_root,
                   t_uchar * config_name,
                   struct arch_build_config_params * params,
                   t_uchar * default_archive)
{
  rel_table config = rel_table_nil;
  int x;

  config = arch_read_config (tree_root, config_name);

  /* Ensure a shallowist to deepest sort
   */
  rel_sort_table_by_field (0, config, 0);

  /* move conflicting dirs and files
   */

  for (x = 0; x < rel_n_records (config); ++x)
    {
      int errn;
      t_uchar * path = 0;
      t_uchar * path_dir = 0;
      t_uchar * path_tail = 0;
      t_uchar * saved_tail = 0;
      t_uchar * saved_path = 0;

      path = file_name_in_vicinity (0, tree_root, rel_peek_str (config, x, 0));
      path_dir = file_name_directory_file (0, path);
      path_tail = file_name_tail (0, path);
      saved_tail = str_alloc_cat (0, "++saved.", path_tail);
      saved_path = tmp_file_name (path_dir, saved_tail);

      if (vu_rename (&errn, path, saved_path) && (errn != ENOENT))
        {
          safe_printfmt (2, "build-config: unable to set aside conflicting directory %s\n", path);
          exit (2);
        }

      lim_free (0, path);
      lim_free (0, path_dir);
      lim_free (0, path_tail);
      lim_free (0, saved_tail);
      lim_free (0, saved_path);
    }

  /* build desired trees.
   */

  for (x = 0; x < rel_n_records (config); ++x)
    {
      t_uchar * path_to_subtree = 0;
      t_uchar * path_to_subtree_dir = 0;
      const t_uchar * revspec;
      int status;

      path_to_subtree = file_name_in_vicinity (0, tree_root, rel_peek_str (config, x, 0));
      path_to_subtree_dir = file_name_directory_file (0, path_to_subtree);

      ensure_directory_exists (path_to_subtree_dir);

      revspec = rel_peek_str (config, x, 1);

      {
        char ** argv = 0;
        char * revspec_copy = 0;
        char * path_to_subtree_copy = 0;

        /* call `get' -- build an argv for it
         */

        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "get";
        if (default_archive)
          {
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-A";
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = default_archive;
          }
        if (params->no_pristines)
          {
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "--no-pristine";
          }
        if (params->hardlinks)
          {
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "--link";
          }
        if (params->library)
          {
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "--library";
          }
        if (params->sparse)
          {
            *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "--sparse";
          }
        revspec_copy = str_save (0, revspec);
        path_to_subtree_copy = str_save (0, path_to_subtree);
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = revspec_copy;
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = path_to_subtree_copy;
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = 0;

        status = arch_cmd_get ("get", (ar_size ((void *)argv, 0, sizeof (char *)) - 1), argv);

        ar_free ((void **)&argv, 0);
        lim_free (0, revspec_copy);
        lim_free (0, path_to_subtree_copy);
      }
        
      if (status)
        {
          safe_printfmt (2, "unable to build %s at %s\n", revspec, path_to_subtree);
          exit (status);
        }

      lim_free (0, path_to_subtree);
      lim_free (0, path_to_subtree_dir);
    }

  if (params->release_id)
    {
      int errn;
      t_uchar * tree_version = 0;
      t_uchar * tree_revision = 0;
      t_uchar * release_id_file = 0;
      rel_table snapped_config = rel_table_nil;
      int out_fd;

      tree_version = arch_tree_version (tree_root);
      if (tree_version)
        {
          t_uchar * archive = 0;
          t_uchar * version = 0;
          t_uchar * level = 0;

          archive = arch_parse_package_name (arch_ret_archive, 0, tree_version);
          version = arch_parse_package_name (arch_ret_non_archive, 0, tree_version);

          level = arch_highest_patch_level (tree_root, archive, version);

          tree_revision = str_alloc_cat_many (0, tree_version, "--", level, str_end);

          lim_free (0, archive);
          lim_free (0, version);
          lim_free (0, level);
        }

      snapped_config = arch_config_from_tree (tree_root, config);

      release_id_file = file_name_in_vicinity (0, tree_root, "=RELEASE-ID");

      invariant (!vu_unlink (&errn, release_id_file) || (errn == ENOENT));
      out_fd = safe_open (release_id_file, O_WRONLY | O_CREAT | O_EXCL, 0666);

      safe_printfmt (out_fd, "# automatically generated release id (by tla build-config)\n");
      safe_printfmt (out_fd, "#\n");
      safe_printfmt (out_fd, "\n");
      safe_printfmt (out_fd, "%s(%s)\n", (tree_revision ? tree_revision : (t_uchar *)"<no tree version set>"), config_name);
      safe_printfmt (out_fd, "\n");

      rel_print_pika_escape_iso8859_1_table (out_fd, arch_escape_classes, snapped_config);

      safe_close (out_fd);

      lim_free (0, tree_version);
      lim_free (0, tree_revision);
      lim_free (0, release_id_file);
      rel_free_table (snapped_config);
    }


  rel_free_table (config);
}



/* tag: Tom Lord Fri May 30 00:05:24 2003 (configs.c)
 */
