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

#include "hackerlab/os/stdlib.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/vu/safe.h"
#include "tla/libfsutils/ensure-dir.h"
#include "tla/libfsutils/read-line.h"
#include "tla/libfsutils/file-contents.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/my.h"



t_uchar *
arch_my_arch_params (void)
{
  t_uchar * home;
  t_uchar * answer;

  home = getenv ("HOME");

  invariant (!!home);
  answer = file_name_in_vicinity (0, home, ".arch-params");

  return answer;
}


void
arch_ensure_my_arch_params (void)
{
  int errn;
  t_uchar * dir = 0;

  dir = arch_my_arch_params ();

  if (vu_mkdir (&errn, dir, 0700) && errn != EEXIST)
    {
      safe_printfmt (2, "arch: unable to create directory %s\n", dir);
      exit (2);
    }

  lim_free (0, dir);
}



t_uchar *
arch_my_hook_script (void)
{
  t_uchar * params_dir;
  t_uchar * hook_script;

  params_dir = arch_my_arch_params ();
  hook_script = file_name_in_vicinity (0, params_dir, "hook");

  {
    int euid;
    int egid;
    struct stat stat;
    int stat_ok;
    int errn;

    euid = geteuid ();
    egid = getegid ();

    stat_ok = vu_stat (&errn, hook_script, &stat);
    if (stat_ok
        || !(((euid == stat.st_uid) && (stat.st_mode & S_IXUSR))
             || ((egid == stat.st_gid) && (stat.st_mode & S_IXGRP))
             || (stat.st_mode & S_IXOTH)))
      {
        lim_free (0, hook_script);
        hook_script = 0;
      }
  }

  lim_free (0, params_dir);
  return hook_script;
}



static t_uchar * my_id = 0;

t_uchar *
arch_my_id_file (void)
{
  t_uchar * params_dir;
  t_uchar * id_file;

  params_dir = arch_my_arch_params ();
  id_file = file_name_in_vicinity (0, params_dir, "=id");
  lim_free (0, params_dir);

  return id_file;
}


void
arch_set_my_id (const t_uchar * id)
{
  t_uchar * id_file;
  int out_fd;

  if (my_id)
    lim_free (0, my_id);

  invariant (arch_valid_id (id));

  id_file = arch_my_id_file ();

  arch_ensure_my_arch_params ();

  out_fd = safe_open (id_file, O_WRONLY | O_CREAT, 0666);
  safe_ftruncate (out_fd, 0L);
  safe_printfmt (out_fd, "%s\n", id);
  safe_close (out_fd);
  lim_free (0, id_file);

  my_id = str_save (0, id);
}


t_uchar *
arch_my_id (void)
{
  int errn;
  t_uchar * id_file;
  int in_fd;
  t_uchar * id_contents;
  size_t len;
  t_uchar * eoid;

  if (my_id)
    return str_save (0, my_id);

  id_file = arch_my_id_file ();
  in_fd = vu_open (&errn, id_file, O_RDONLY, 0);
  if (in_fd < 0)
    {
      if (errn == ENOENT)
        safe_printfmt (2, "arch: no arch user id set\n");
      else
        safe_printfmt (2, "arch: i/o error for file %s (%s)\n", id_file, errno_to_string (errn));
      exit (2);
    }
  lim_free (0, id_file);

  id_contents = 0;
  len = 0;
  safe_file_to_string (&id_contents, &len, in_fd);
  safe_close (in_fd);

  eoid = str_chr_index_n (id_contents, len, '\n');
  if (!eoid)
    eoid = id_contents + len;

  my_id = str_save_n (0, id_contents, eoid - id_contents);

  lim_free (0, id_contents);

  return str_save (0, my_id);
}


t_uchar *
arch_my_id_uid (void)
{
  t_uchar * id = 0;
  t_uchar * lb;
  t_uchar * rb;
  t_uchar * answer = 0;

  id = arch_my_id ();
  if (!id)
    return 0;

  lb = str_chr_rindex (id, '<');
  rb = str_chr_rindex (id, '>');

  invariant (lb && rb && ((lb + 1) < rb));

  answer = str_save_n (0, (lb + 1), rb - (lb + 1));

  lim_free (0, id);

  return answer;
}




static t_uchar * my_home_archive = 0;

t_uchar *
arch_my_default_archive (const t_uchar * default_archive)
{
  t_uchar * params_dir = 0;
  t_uchar * default_archive_file = 0;
  int in_fd;
  t_uchar * default_archive_file_contents = 0;
  size_t archive_contents_len;
  t_uchar * nl_pos;
  t_uchar * answer = 0;
  int errn;

  /* This does away with the arch conventions of using $ARCHIVE
   * and has no support for -R
   */

  if (default_archive)
    {
      if (!arch_valid_archive_name (default_archive))
        {
          safe_printfmt (2, "invalid archive name %s\n", default_archive);
          exit (2);
        }
      return str_save (0, default_archive);
    }

  if (my_home_archive)
    return str_save (0, my_home_archive);


  params_dir = arch_my_arch_params ();
  default_archive_file = file_name_in_vicinity (0, params_dir, "=default-archive");

  in_fd = vu_open (&errn, default_archive_file, O_RDONLY, 0);

  if (in_fd < 0)
    {
      if (errn != ENOENT)
        {
          safe_printfmt (2, "arch: error opening %s\n", default_archive_file);
          exit (2);
        }
      answer = 0;
    }
  else
    {
      safe_file_to_string (&default_archive_file_contents, &archive_contents_len, in_fd);

      safe_close (in_fd);

      nl_pos = str_chr_index_n (default_archive_file_contents, archive_contents_len, '\n');
      if (!nl_pos)
        nl_pos = default_archive_file_contents + archive_contents_len;

      answer = str_save_n (0, default_archive_file_contents, nl_pos - default_archive_file_contents);
    }

  lim_free (0, default_archive_file_contents);
  lim_free (0, default_archive_file);
  lim_free (0, params_dir);

  if (answer)
    {
      lim_free (0, my_home_archive);
      my_home_archive = str_save (0, answer);
    }

  return answer;
}


void
arch_set_my_default_archive (const t_uchar * archive)
{
  t_uchar * arch_params_dir;
  t_uchar * default_archive_file;
  int out_fd;

  invariant (arch_valid_archive_name (archive));

  if (my_home_archive)
    {
      lim_free (0, my_home_archive);
      my_home_archive = 0;
    }

  arch_ensure_my_arch_params ();
  arch_params_dir = arch_my_arch_params ();
  default_archive_file = file_name_in_vicinity (0, arch_params_dir, "=default-archive");

  out_fd = safe_open (default_archive_file, O_WRONLY | O_CREAT, 0666);

  safe_ftruncate (out_fd, (long)0);
  safe_write_retry (out_fd, archive, str_length (archive));
  safe_close (out_fd);

  lim_free (0, arch_params_dir);
  lim_free (0, default_archive_file);

  my_home_archive = str_save (0, archive);
}


void
arch_delete_my_default_archive (void)
{
  t_uchar * params_dir;
  t_uchar * default_archive_file;
  int errn;

  if (my_home_archive)
    {
      lim_free (0, my_home_archive);
      my_home_archive = 0;
    }

  params_dir = arch_my_arch_params ();
  default_archive_file = file_name_in_vicinity (0, params_dir, "=default-archive");

  if (vu_unlink (&errn, default_archive_file) && (errn != ENOENT))
    {
      panic ("unable to delete ~/.arch-params/=default-archive");
    }

  lim_free (0, params_dir);
  lim_free (0, default_archive_file);
}



t_uchar *
arch_my_archive_locations_dir (void)
{
  t_uchar * arch_params_path = 0;
  t_uchar * answer = 0;

  arch_params_path = arch_my_arch_params ();
  answer = file_name_in_vicinity (0, arch_params_path, "=locations");

  lim_free (0, arch_params_path);
  return answer;
}




static t_uchar *
arch_my_default_library_file (void)
{
  t_uchar * params_dir;
  t_uchar * lib_file;

  params_dir = arch_my_arch_params ();
  lib_file = file_name_in_vicinity (0, params_dir, "=revision-library");

  lim_free (0, params_dir);

  return lib_file;
}

static t_uchar *
arch_my_default_library_add_file (void)
{
  t_uchar * params_dir;
  t_uchar * lib_file;

  params_dir = arch_my_arch_params ();
  lib_file = file_name_in_vicinity (0, params_dir, "=revision-library-adds");

  lim_free (0, params_dir);

  return lib_file;
}


rel_table
arch_my_library_path (enum arch_library_path_type path_type)
{
  t_uchar * first_file = 0;
  t_uchar * second_file = 0;
  rel_table answer = rel_table_nil;

  switch (path_type)
    {
    case arch_library_path_search_only:
      {
        first_file = arch_my_default_library_file ();
        break;
      }        
    case arch_library_path_add_only:
      {
        first_file = arch_my_default_library_add_file ();
        break;
      }        
    case arch_library_path_search_order:
      {
        first_file = arch_my_default_library_file ();
        second_file = arch_my_default_library_add_file ();
        break;
      }        
    case arch_library_path_add_order:
      {
        second_file = arch_my_default_library_file ();
        first_file = arch_my_default_library_add_file ();
        break;
      }
    }

  if (first_file && !safe_access (first_file, F_OK))
    {
      int fd;

      fd = safe_open (first_file, O_RDONLY, 0);
      answer = rel_read_table (fd, 1, "tla", first_file);
      safe_close (fd);
    }

  if (second_file && !safe_access (second_file, F_OK))
    {
      int fd;
      rel_table answer2 = rel_table_nil;

      fd = safe_open (second_file, O_RDONLY, 0);
      answer2 = rel_read_table (fd, 1, "tla", second_file);
      safe_close (fd);
      rel_append_x (&answer, answer2);
      rel_free_table (answer2);
    }

  lim_free (0, first_file);
  lim_free (0, second_file);

  return answer;
}


void
arch_set_my_library_path (enum arch_library_path_type path_type,
                          enum arch_library_edit_op op,
                          const t_uchar * dir_spec)
{
  t_uchar * dir = 0;
  t_uchar * lib_file = 0;
  rel_table old_path = rel_table_nil;
  rel_table new_path = rel_table_nil;
  int out_fd;

  if (file_name_is_absolute (dir_spec))
    {
      dir = str_save (0, dir_spec);
    }
  else if (dir_spec)
    {
      t_uchar * here = 0;
      here = safe_current_working_directory ();
      dir = file_name_in_vicinity (0, here, dir_spec);
      lim_free (0, here);
    }

  switch (path_type)
    {
    case arch_library_path_search_order:
    case arch_library_path_search_only:
      {
        lib_file = arch_my_default_library_file ();
        old_path = arch_my_library_path (arch_library_path_search_only);
        break;
      }
    case arch_library_path_add_order:
    case arch_library_path_add_only:
      {
        lib_file = arch_my_default_library_add_file ();
        old_path = arch_my_library_path (arch_library_path_add_only);
        break;
      }
    }


  /* install first if appropriate
   */
  if (dir_spec && (op == arch_library_first))
    {
      rel_add_records (&new_path, rel_singleton_record_taking (rel_make_field_str (dir)), rel_record_null);
    }

  /* copy the old path, removing dir_spec (if any)
   */
  if (dir_spec)
    {
      int x;

      for (x = 0; x < rel_n_records (old_path); ++x)
        {
          if (str_cmp (rel_peek_str (old_path, x, 0), dir))
            {
              rel_add_records (&new_path, rel_singleton_record_taking (rel_get_field (old_path, x, 0)), rel_record_null);
            }
        }
    }

  /* install last if appropriate
   */
  if (dir_spec && (op == arch_library_last))
    {
      rel_add_records (&new_path, rel_singleton_record_taking (rel_make_field_str (dir)), rel_record_null);
    }


  /* install the new path
   */
  arch_ensure_my_arch_params ();

  out_fd = safe_open (lib_file, O_WRONLY | O_CREAT, 0666);
  safe_ftruncate (out_fd, 0L);
  rel_print_table (out_fd, new_path);
  safe_close (out_fd);


  lim_free (0, dir);
  lim_free (0, lib_file);
  rel_free_table (old_path);
  rel_free_table (new_path);
}




t_uchar *
arch_my_util_path_file (void)
{
  t_uchar * params_dir;
  t_uchar * path_file;

  params_dir = arch_my_arch_params ();
  path_file = file_name_in_vicinity (0, params_dir, "path");
  lim_free (0, params_dir);

  return path_file;
}


t_uchar **
arch_my_util_path (void)
{
  t_uchar * path_file = 0;
  t_uchar ** answer = 0;

  path_file = arch_my_util_path_file ();
  if (!safe_access (path_file, F_OK))
    {
      t_uchar * contents = 0;

      contents = file_contents_trimming (path_file);
      answer = path_parse (0, contents);

      lim_free (0, contents);
    }

  lim_free (0, path_file);
  return answer;
}




t_uchar *
arch_my_metadata_rules_file (void)
{
  t_uchar * params_dir;
  t_uchar * path_file;

  params_dir = arch_my_arch_params ();
  path_file = file_name_in_vicinity (0, params_dir, "metadata-rules");
  lim_free (0, params_dir);

  return path_file;
}


int
arch_my_allow_full_meta (void)
{
  t_uchar * rules_file = 0;
  struct stat statb;
  int errn = 0;
  int answer;

  rules_file = arch_my_metadata_rules_file ();

  if (0 == vu_stat (&errn, rules_file, &statb))
    {
      if (statb.st_size != 0)
        {
          safe_printfmt (2, "illegal ~/.arch-params/metadata-rules file (non-empty)\n  file: %s\n", rules_file);
          panic ("abort");
        }

      answer = 1;
    }
  else
    {
      if (errn == ENOENT)
        answer = 0;
      else
        panic ("strange i/o error");
    }

  lim_free (0, rules_file);
  return answer;
}




/* tag: Tom Lord Mon May 12 15:01:17 2003 (my.c)
 */
