/* archive.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/os/sys/types.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/os/signal.h"
#include "hackerlab/os/time.h"
#include "hackerlab/os/unistd.h"
#include "hackerlab/os/stdlib.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/hash/hash-utils.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "file-utils/libfsutils/tmp-files.h"
#include "file-utils/libfsutils/rmrf.h"
#include "file-utils/libarch/namespace.h"
#include "file-utils/libarch/my.h"
#include "file-utils/libarch/archives.h"
#include "file-utils/libarch/archive-version.h"
#include "file-utils/libarch/archive.h"
#include "file-utils/libarch/archive-fs.h"


/* __STDC__ prototypes for static functions */
static int invoke_tar_extract (int * pid_ret);
static int wait_for_tar (int pid);
static void ensure_writable (struct arch_archive * arch);



struct arch_archive *
arch_archive_connect (t_uchar * name)
{
  struct arch_archive * answer = 0;
  t_uchar * location = 0;

  answer = lim_malloc (0, sizeof (*answer));
  mem_set0 ((t_uchar *)answer, sizeof (answer));

  location = arch_archive_location (name, 0);

  answer->name = str_save (0, name);
  answer->location = location;

  if (!str_cmp_prefix ("http:", location))
    panic ("arch_archive_connect: webDAV and http/.listing support not done yet");
  else if (!str_cmp_prefix ("ftp:", location))
    panic ("arch_archive_connect: ftp support not done yet");
  else if (!str_cmp_prefix ("sftp:", location))
    panic ("arch_archive_connect: sftp support not done yet");
  else
    {
      arch_fs_archive_connect (&answer);
    }

  answer->version = answer->vtable->archive_version (answer);
  answer->access = arch_archive_access (answer->version);

  if (answer->access == arch_archive_incompatible)
    {
      safe_printfmt (2, "arch_archive_connect: attempt to connect to incompatible archive\n  archive: %s\n", name);
      exit (2);
    }

  return answer;
}


void
arch_archive_close (struct arch_archive * arch)
{
  if (!arch)
    return;

  arch->vtable->close (arch);
  lim_free (0, arch->name);
  lim_free (0, arch->location);
  lim_free (0, arch->version);
  lim_free (0, arch);
}


t_uchar *
arch_archive_version (struct arch_archive * arch)
{
  return arch->vtable->archive_version (arch);
}


rel_table
arch_archive_categories (struct arch_archive * arch)
{
  rel_table answer = 0;

  answer = arch->vtable->categories (arch);
  rel_sort_table_by_field (0, answer, 0);

  return answer;
}


rel_table
arch_archive_branches (struct arch_archive * arch, t_uchar * category)
{
  rel_table answer = 0;

  answer = arch->vtable->branches (arch, category);
  rel_sort_table_by_field (0, answer, 0);

  return answer;
}


rel_table
arch_archive_versions (struct arch_archive * arch, t_uchar * package)
{
  rel_table answer = 0;

  answer = arch->vtable->versions (arch, package);
  arch_sort_table_by_name_field (0, answer, 0);

  return answer;
}

rel_table
arch_archive_revisions (struct arch_archive * arch, t_uchar * version, int full)
{
  rel_table answer = 0;

  answer = arch->vtable->revisions (arch, version);
  arch_sort_table_by_patch_level_field (0, answer, 0);

  if (full)
    {
      t_uchar * fqv = 0;
      int x;

      if (full == 1)
        fqv = arch_fully_qualify (arch->name, version);
      else
        fqv = str_save (0, version);

      for (x = 0; x < rel_n_records (answer); ++x)
        {
          t_uchar * t = 0;

          t = answer[x][0];
          answer[x][0] = str_alloc_cat_many (0, fqv, "--", t, str_end);
          lim_free (0, t);
        }

      lim_free (0, fqv);
    }

  return answer;
}

t_uchar *
arch_archive_log (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * answer = 0;

  answer = arch->vtable->archive_log (arch, revision);

  return answer;
}


void
arch_revision_type (enum arch_revision_type * type, int * is_cached,
                   struct arch_archive * arch, t_uchar * revision)
{
  arch->vtable->revision_type (type, is_cached, arch, revision);
}

void
arch_get_patch (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_name = 0;
  t_uchar * patches_file_dest = 0;

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

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_name = str_alloc_cat (0, revision, ".patches");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_patch (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (patches_file_name, patches_file_dest);
  
  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_name);
  lim_free (0, patches_file_dest);
}


void
arch_get_cached_revision (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_dest = 0;

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

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_cached (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (revision, patches_file_dest);
  
  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_dest);
}

void
arch_get_import_revision (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_dest = 0;

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

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_import (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (revision, patches_file_dest);
  
  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_dest);
}


t_uchar *
arch_get_continuation (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * raw_data = 0;
  t_uchar * start;
  t_uchar * end;
  t_uchar * answer = 0;

  raw_data = arch->vtable->get_continuation (arch, revision);

  start = raw_data;
  while (*start && char_is_space (*start))
    ++start;

  end = start;
  while (*end && !char_is_space (*end))
    ++end;

  answer = str_save_n (0, start, end - start);

  lim_free (0, raw_data);

  return answer;
}


int
arch_make_category (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * category)
{
  ensure_writable (arch);
  return arch->vtable->make_category (errstr, arch, category);
}

int
arch_make_branch (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * branch)
{
  ensure_writable (arch);
  return arch->vtable->make_branch (errstr, arch, branch);
}

int
arch_make_version (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * version)
{
  ensure_writable (arch);
  return arch->vtable->make_version (errstr, arch, version);
}




int
arch_archive_lock_revision (t_uchar ** errstr, struct arch_archive * a,
                            t_uchar * version,
                            t_uchar * prev_level,
                            t_uchar * uid,
                            t_uchar * txn_id)
{
  ensure_writable (a);
  return a->vtable->lock_revision (errstr, a, version, prev_level, uid, txn_id);
}

int 
arch_archive_finish_revision (t_uchar ** errstr, struct arch_archive * a,
                              t_uchar * version,
                              t_uchar * prev_level,
                              t_uchar * uid,
                              t_uchar * txn_id,
                              t_uchar * new_level)
{
  ensure_writable (a);
  return a->vtable->finish_revision (errstr, a, version, prev_level, uid, txn_id, new_level);
}

enum arch_revision_lock_state
arch_archive_revision_lock_state (t_uchar ** prev_level_ret,
                                  t_uchar ** uid_ret,
                                  t_uchar ** txn_id_ret, 
                                  struct arch_archive * a,
                                  t_uchar * version)
{
  ensure_writable (a);
  return a->vtable->lock_state (prev_level_ret, uid_ret, txn_id_ret, a, version);
}

int 
arch_archive_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
                                  t_uchar * version,
                                  t_uchar * prev_level,
                                  t_uchar * uid,
                                  t_uchar * txn_id)
{
  ensure_writable (a);
  return a->vtable->break_revision_lock (errstr, a, version, prev_level, uid, txn_id);
}




int 
arch_archive_put_log (t_uchar ** errstr, struct arch_archive * a,
                      t_uchar * version,
                      t_uchar * prev_level,
                      t_uchar * uid,
                      t_uchar * txn_id,
                      t_uchar * log_text)
{
  ensure_writable (a);
  return a->vtable->put_log (errstr, a, version, prev_level, uid, txn_id, log_text);
}



int 
arch_archive_put_continuation (t_uchar ** errstr, struct arch_archive * a,
                               t_uchar * version,
                               t_uchar * prev_level,
                               t_uchar * uid,
                               t_uchar * txn_id,
                               t_uchar * continuation)
{
  ensure_writable (a);
  return a->vtable->put_continuation (errstr, a, version, prev_level, uid, txn_id, continuation);
}


int
arch_archive_put_changeset (t_uchar ** errstr, struct arch_archive * a,
                            t_uchar * version,
                            t_uchar * prev_level,
                            t_uchar * uid,
                            t_uchar * txn_id,
                            t_uchar * level,
                            t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * desired_name = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  desired_name = str_alloc_cat_many (0, version, "--", level, ".patches", str_end);
  invariant (!str_cmp (dir_tail, desired_name));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_changeset (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, desired_name);
  lim_free (0, tar_file_path);

  return answer;
}


int 
arch_archive_put_import (t_uchar ** errstr, struct arch_archive * a,
                         t_uchar * version,
                         t_uchar * prev_level,
                         t_uchar * uid,
                         t_uchar * txn_id,
                         t_uchar * level,
                         t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * desired_name = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  desired_name = str_alloc_cat_many (0, version, "--", level, str_end);
  invariant (!str_cmp (dir_tail, desired_name));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_import (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, desired_name);
  lim_free (0, tar_file_path);

  return answer;
}


int 
arch_archive_put_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  invariant (!str_cmp (dir_tail, revision));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_cached (errstr, a, revision, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, tar_file_path);

  return answer;
}


int 
arch_archive_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
  ensure_writable (a);
  return a->vtable->delete_cached (errstr, a, revision);
}





t_uchar *
arch_generate_txn_id (void)
{
  time_t now;
  pid_t pid;
  char hostname[64];
  char number[64];
  t_uchar * answer;

  now = time (0);
  pid = getpid ();

  mem_set0 ((t_uchar *)hostname, sizeof (hostname));
  gethostname (hostname, sizeof (hostname));

  cvt_ulong_to_hex (number, hash_ul((t_ulong)now ^ (t_ulong)pid));
  answer = str_save (0, number);

  cvt_ulong_to_hex (number, hash_mem ((t_uchar *)hostname, sizeof (hostname)));
  answer = str_realloc_cat (0, answer, number);

  cvt_ulong_to_hex (number, hash_ul ((t_ulong)now ^ ~(t_ulong)pid));
  answer = str_realloc_cat (0, answer, number);

  return answer;
}


t_uchar *
arch_previous_revision (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * version = 0;
  t_uchar * patch_level = 0;
  enum arch_patch_level_type current_type;
  enum arch_patch_level_type next_type;
  t_ulong current_n;
  t_ulong next_n;
  t_uchar * next_patch_level = 0;
  t_uchar * answer = 0;


  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  patch_level = arch_parse_package_name (arch_ret_patch_level, 0, revision);

  current_type = arch_analyze_patch_level (&current_n, patch_level);

  if (current_type == arch_is_base0_level)
    {
      answer = 0;
    }
  else
    {
      if (((current_type == arch_is_patch_level) || (current_type == arch_is_versionfix_level)) && (current_n > 1))
        {
          next_type = current_type;
          next_n = current_n - 1;
        }
      else if (current_n == 1)
        {
          next_n = 0;
          if (current_type == arch_is_patch_level)
            next_type = arch_is_base0_level;
          else
            next_type = arch_is_version_level;
        }
      else
        {
          rel_table revisions = 0;
          int x;

          revisions = arch_archive_revisions (arch, version, 0);

          if (!revisions)
            {
              safe_printfmt (2, "version has no revisions (%s/%s)\n", arch->name, version);
              exit (2);
            }

          for (x = 0; x < rel_n_records (revisions); ++x)
            if (revisions[x][0][0] == 'v')
              break;

          invariant (x);

          if (x == rel_n_records (revisions))
            x = rel_n_records (revisions) - 1;

          if (x == 1)
            {
              next_type = arch_is_base0_level;
              next_n = 0;
            }
          else
            {
              next_type = arch_is_patch_level;
              next_n = x - 1;
            }

          rel_free_table (revisions);
        }
      
      next_patch_level = arch_form_patch_level (next_type, next_n);
      answer = str_alloc_cat_many (0, version, "--", next_patch_level, str_end);
    }

  lim_free (0, version);
  lim_free (0, patch_level);
  lim_free (0, next_patch_level);

  return answer;
}


t_uchar *
arch_ancestor_revision (struct arch_archive * arch, t_uchar * revision)
{
  enum arch_revision_type type;

  arch_revision_type (&type, 0, arch, revision);

  switch (type)
    {
    default:
      {
        panic ("arch_ancestor_revision: unrecognized revision type");
        return 0;
      }
    case arch_import_revision:
      {
        return 0;
      }
    case arch_simple_revision:
      {
        t_uchar * prev_revision = 0;
        t_uchar * answer = 0;

        prev_revision = arch_previous_revision (arch, revision);
        answer = arch_fully_qualify (arch->name, prev_revision);

        lim_free (0, prev_revision);

        return answer;
      }
    case arch_continuation_revision:
      {
        return arch_get_continuation (arch, revision);
      }
    }
}



t_uchar *
archive_tmp_file_name (t_uchar * dir, t_uchar * basename)
{
  t_uchar * my_uid = 0;
  t_uchar * tmp_name = 0;

  my_uid = arch_my_id_uid ();
  tmp_name = tmp_file_name (dir, basename);

  tmp_name = str_realloc_cat_many (0, tmp_name, ".", my_uid, str_end);

  lim_free (0, my_uid);

  return tmp_name;
}




static int
invoke_tar_extract (int * pid_ret)
{
  int pipe_fds[2];
  int pid;

  if (pipe (pipe_fds))
    panic ("unable to create pipe fds for tar");

  pid = fork ();

  if (pid == -1)
    panic ("unable to fork for patch");

  if (pid)
    {
      *pid_ret = pid;
      safe_close (pipe_fds[0]);
      return pipe_fds[1];
    }
  else
    {
      char ** argv;

      safe_close (pipe_fds[1]);

      argv = 0;

      *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "tar";
      *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-m";
      *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-zxf";
      *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-";
      *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = 0;

      safe_move_fd (pipe_fds[0], 0);

      execvp ("tar", argv);
      panic ("invoke_tar_extract: execvp for patch returned to caller");
      exit (2);
    }
  panic ("invoke_tar_extract: not reached");
  return -1;
}

static int
wait_for_tar (int pid)
{
  int status;
  int wait_pid;

  wait_pid = waitpid (pid, &status, 0);
  if (wait_pid < 0)
    {
      panic_msg ("error waiting for tar subprocess");
      kill (0, SIGKILL);
      panic ("error waiting for subprocess");
    }
  if (WIFSIGNALED (status))
    {
      safe_printfmt (2, "\n");
      safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
      safe_printfmt (2, "\n");
      exit (2);
      return -1;
    }
  else if (!WIFEXITED (status))
    {
      panic_msg ("waitpid returned for a non-exited process");
      kill (0, SIGKILL);
      panic ("waitpid returned for a non-exited process");
      return -1;
    }
  else
    {
      int exit_status;

      exit_status = WEXITSTATUS (status);
      return exit_status;
    }
}


t_uchar *
make_tmp_tar_archive (t_uchar * dir)
{
  t_uchar * dir_dir = 0;
  t_uchar * dir_tail = 0;
  t_uchar * tmp_stem = 0;
  t_uchar * tmp_in_cwd = 0;
  t_uchar * tmp_path = 0;

  dir_dir = file_name_directory_file (0, dir);
  if (!dir_dir)
    dir_dir = str_save (0, ".");

  dir_tail = file_name_tail (0, dir);

  tmp_stem = str_alloc_cat_many (0, ",,", dir_tail, ".tar.gz", str_end);
  tmp_in_cwd = tmp_file_name (".", tmp_stem);
  tmp_path = file_name_in_vicinity (0, dir_dir, tmp_in_cwd);

  {
    int pid;
    int dev_null_fd;

    dev_null_fd = safe_open ("/dev/null", O_WRONLY, 0);

    pid = fork ();

    if (pid == -1)
      panic ("unable to fork for patch");

    if (!pid)
      {
        char ** argv;

        safe_chdir (dir_dir);

        argv = 0;

        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "tar";
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-zcf";
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = tmp_in_cwd;
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = dir_tail;
        *(char **)ar_push ((void **)&argv, 0, sizeof (char *)) = 0;

        safe_move_fd (dev_null_fd, 1);
        safe_dup2 (1, 2);

        execvp ("tar", argv);
        panic ("make_tmp_tar_archive: execvp for patch returned to caller");
        exit (2);
      }
    else
      {
        int status;
        int wait_pid;
        
        safe_close (dev_null_fd);

        wait_pid = waitpid (pid, &status, 0);
        if (wait_pid < 0)
          {
            panic_msg ("error waiting for tar subprocess");
            kill (0, SIGKILL);
            panic ("error waiting for subprocess");
          }
        if (WIFSIGNALED (status))
          {
            safe_printfmt (2, "\n");
            safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
            safe_printfmt (2, "\n");
            exit (2);
            return 0;
          }
        else if (!WIFEXITED (status))
          {
            panic_msg ("waitpid returned for a non-exited process");
            kill (0, SIGKILL);
            panic ("waitpid returned for a non-exited process");
            return 0;
          }
        else
          {
            int exit_status;

            exit_status = WEXITSTATUS (status);

            if (exit_status)
              panic ("make_tmp_tar_archive: tar exitted with non-0 status");
          }
      }
  }
  
  lim_free (0, dir_dir);
  lim_free (0, dir_tail);
  lim_free (0, tmp_in_cwd);
  lim_free (0, tmp_stem);

  return tmp_path;
}



static void
ensure_writable (struct arch_archive * arch)
{
  if (arch->access != arch_archive_writable)
    {
      safe_printfmt (2, "attempt to modify archive that has only read-compatability\n    archive: %s\n", arch->name);
      exit (2);
    }
}




/* tag: Tom Lord Tue May 20 00:52:06 2003 (archive.c)
 */
