Logo Search packages:      
Sourcecode: aegis version File versions

aecp.c

/*
 *    aegis - project change supervisor
 *    Copyright (C) 1991-1999, 2001-2003 Peter Miller;
 *    All rights reserved.
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to implement copy file
 */

#include <ac/stdio.h>
#include <ac/stdlib.h>
#include <ac/unistd.h>
#include <ac/libintl.h>

#include <aecp.h>
#include <ael/project/files.h>
#include <arglex2.h>
#include <commit.h>
#include <change/branch.h>
#include <change/file.h>
#include <error.h>
#include <file.h>
#include <gettime.h>
#include <help.h>
#include <lock.h>
#include <log.h>
#include <os.h>
#include <progname.h>
#include <project.h>
#include <project/file.h>
#include <project/file/roll_forward.h>
#include <project/history.h>
#include <sub.h>
#include <trace.h>
#include <undo.h>
#include <user.h>
#include <str_list.h>


static void
copy_file_usage(void)
{
    char            *progname;

    progname = progname_get();
    fprintf
    (
      stderr,
      "usage: %s -CoPy_file [ <option>... ] <filename>...\n",
      progname
    );
    fprintf
    (
      stderr,
      "       %s -CoPy_file -INDependent [ <option>... ] <filename>...\n",
      progname
    );
    fprintf(stderr, "       %s -CoPy_file -List [ <option>... ]\n", progname);
    fprintf(stderr, "       %s -CoPy_file -Help\n", progname);
    quit(1);
}


static void
copy_file_help(void)
{
    help("aecp", copy_file_usage);
}


static void
copy_file_list(void)
{
    string_ty       *project_name;
    long            change_number;

    trace(("copy_file_list()\n{\n"));
    arglex();
    project_name = 0;
    change_number = 0;
    while (arglex_token != arglex_token_eoln)
    {
      switch (arglex_token)
      {
      default:
          generic_argument(copy_file_usage);
          continue;

      case arglex_token_change:
          if (arglex() != arglex_token_number)
            option_needs_number(arglex_token_change, copy_file_usage);
          /* fall through... */

      case arglex_token_number:
          if (change_number)
            duplicate_option_by_name(arglex_token_change, copy_file_usage);
          change_number = arglex_value.alv_number;
          if (change_number == 0)
            change_number = MAGIC_ZERO;
          else if (change_number < 1)
          {
            sub_context_ty *scp;

            scp = sub_context_new();
            sub_var_set_long(scp, "Number", change_number);
            fatal_intl(scp, i18n("change $number out of range"));
            /* NOTREACHED */
            sub_context_delete(scp);
          }
          break;

      case arglex_token_project:
          if (arglex() != arglex_token_string)
            option_needs_name(arglex_token_project, copy_file_usage);
          if (project_name)
            duplicate_option_by_name(arglex_token_project, copy_file_usage);
          project_name = str_from_c(arglex_value.alv_string);
          break;
      }
      arglex();
    }
    list_project_files(project_name, change_number);
    if (project_name)
      str_free(project_name);
    trace(("}\n"));
}


#define NO_TIME_SET ((time_t)(-1))


static void
copy_file_independent(void)
{
    string_ty       *dd;
    string_list_ty  wl;
    string_list_ty  wl2;
    string_list_ty  wl_in;
    string_list_ty  wl_out;
    string_ty       *s1;
    string_ty       *s2;
    int             j;
    int             k;
    string_ty       *project_name;
    project_ty      *pp;
    project_ty      *pp2;
    user_ty         *up;
    long            delta_number;
    long            delta_from_change;
    time_t          delta_date;
    char            *delta_name;
    int             number_of_errors;
    string_list_ty  search_path;
    char            *branch;
    int             trunk;
    change_ty       *cp_bogus;
    int             based;
    string_ty       *base;
    char            *output;

    trace(("copy_file_independent()\n{\n"));
    arglex();
    string_list_constructor(&wl);
    project_name = 0;
    delta_date = NO_TIME_SET;
    delta_number = -1;
    delta_from_change = 0;
    delta_name = 0;
    branch = 0;
    trunk = 0;
    output = 0;
    while (arglex_token != arglex_token_eoln)
    {
      switch (arglex_token)
      {
      default:
          generic_argument(copy_file_usage);
          continue;

      case arglex_token_directory:
          if (arglex() != arglex_token_string)
            option_needs_dir(arglex_token_directory, copy_file_usage);
          goto get_file_names;

      case arglex_token_file:
          if (arglex() != arglex_token_string)
            option_needs_files(arglex_token_file, copy_file_usage);
          /* fall through... */

      case arglex_token_string:
          get_file_names:
          s2 = str_from_c(arglex_value.alv_string);
          string_list_append(&wl, s2);
          str_free(s2);
          break;

      case arglex_token_project:
          if (arglex() != arglex_token_string)
            option_needs_name(arglex_token_project, copy_file_usage);
          if (project_name)
            duplicate_option_by_name(arglex_token_project, copy_file_usage);
          project_name = str_from_c(arglex_value.alv_string);
          break;

      case arglex_token_delta:
          if (delta_number >= 0 || delta_name)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_number(arglex_token_delta, copy_file_usage);
            /*NOTREACHED*/

          case arglex_token_number:
            delta_number = arglex_value.alv_number;
            if (delta_number < 0)
            {
                sub_context_ty *scp;

                scp = sub_context_new();
                sub_var_set_long(scp, "Number", delta_number);
                fatal_intl(scp, i18n("delta $number out of range"));
                /* NOTREACHED */
                sub_context_delete(scp);
            }
            break;

          case arglex_token_string:
            delta_name = arglex_value.alv_string;
            break;
          }
          break;

      case arglex_token_delta_date:
          if (delta_date != NO_TIME_SET)
            duplicate_option(copy_file_usage);
          if (arglex() != arglex_token_string)
          {
            option_needs_string(arglex_token_delta_date, copy_file_usage);
            /*NOTREACHED*/
          }
          delta_date = date_scan(arglex_value.alv_string);
          if (delta_date == NO_TIME_SET)
            fatal_date_unknown(arglex_value.alv_string);
          break;

      case arglex_token_delta_from_change:
          if (arglex() != arglex_token_number)
          {
            option_needs_number
            (
                arglex_token_delta_from_change,
                copy_file_usage
            );
          }
          if (delta_from_change)
          {
            duplicate_option_by_name
            (
                arglex_token_delta_from_change,
                copy_file_usage
            );
          }
          delta_from_change = arglex_value.alv_number;
          if (delta_from_change == 0)
            delta_from_change = MAGIC_ZERO;
          else if (delta_from_change < 1)
          {
            sub_context_ty *scp;

            scp = sub_context_new();
            sub_var_set_long(scp, "Number", delta_from_change);
            fatal_intl(scp, i18n("change $number out of range"));
            /* NOTREACHED */
            sub_context_delete(scp);
          }
          break;

      case arglex_token_branch:
          if (branch)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_number(arglex_token_branch, copy_file_usage);

          case arglex_token_number:
          case arglex_token_string:
            branch = arglex_value.alv_string;
            break;

          case arglex_token_stdio:
            branch = "";
            break;
          }
          break;

      case arglex_token_trunk:
          if (trunk)
            duplicate_option(copy_file_usage);
          ++trunk;
          break;

      case arglex_token_base_relative:
      case arglex_token_current_relative:
          user_relative_filename_preference_argument(copy_file_usage);
          break;

      case arglex_token_output:
          if (output)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_file(arglex_token_output, copy_file_usage);

          case arglex_token_stdio:
            output = "";
            break;

          case arglex_token_string:
            output = arglex_value.alv_string;
            break;
          }
          break;
      }
      arglex();
    }
    if (!wl.nstrings)
    {
      error_intl(0, i18n("no file names"));
      copy_file_usage();
    }
    if (trunk)
    {
      if (branch)
      {
          mutually_exclusive_options
          (
            arglex_token_branch,
            arglex_token_trunk,
            copy_file_usage
          );
      }
      branch = "";
    }
    if
    (
      (
          (delta_name || delta_number >= 0)
      +
          !!delta_from_change
      +
          (delta_date != NO_TIME_SET)
      )
    >
      1
    )
    {
      mutually_exclusive_options3
      (
          arglex_token_delta,
          arglex_token_delta_date,
          arglex_token_delta_from_change,
          copy_file_usage
      );
    }

    /*
     * make sure output is unambiguous
     */
    if (output)
    {
      if (wl.nstrings != 1)
      {
          sub_context_ty *scp;

          scp = sub_context_new();
          sub_var_set_long(scp, "Number", (long)wl.nstrings);
          sub_var_optional(scp, "Number");
          fatal_intl(scp, i18n("single file with -Output"));
          /* NOTREACHED */
          sub_context_delete(scp);
      }
    }

    /*
     * locate project data
     */
    if (!project_name)
      project_name = user_default_project();
    pp = project_alloc(project_name);
    str_free(project_name);
    project_bind_existing(pp);

    /*
     * locate which branch
     */
    if (branch)
      pp2 = project_find_branch(pp, branch);
    else
      pp2 = pp;

    /*
     * locate user data
     */
    up = user_executing(pp);

    /*
     * Take a read lock on the baseline, to ensure that it does
     * not change (aeip) for the duration of the copy.
     */
    if (!output)
    {
      project_baseline_read_lock_prepare(pp2);
      lock_take();
    }

    /*
     * it is an error if the delta does not exist
     */
    if (delta_name)
    {
      s1 = str_from_c(delta_name);
      delta_number = project_history_delta_by_name(pp2, s1, 0);
      str_free(s1);
    }
    if (delta_date != NO_TIME_SET)
    {
      time_t          now;

      /*
       * If the time is in the future, you could get a different
       * answer for the same input at some point in the future.
       *
       * This is the "time safe" quality first described by
       * Damon Poole <damon@ede.com>
       */
      time(&now);
      if (delta_date > now)
          project_error(pp2, 0, i18n("date in the future"));
    }
    if (delta_from_change)
    {
      delta_number =
          project_change_number_to_delta_number(pp2, delta_from_change);
    }
    if (delta_number >= 0)
    {
      delta_date = project_history_delta_to_timestamp(pp2, delta_number);
      if (delta_date == NO_TIME_SET)
      {
          sub_context_ty *scp;

          scp = sub_context_new();
          sub_var_set_long(scp, "Name", delta_number);
          project_fatal(pp2, scp, i18n("no delta $name"));
          /* NOTREACHED */
          sub_context_delete(scp);
      }
    }

    /*
     * build the list of places to look
     * when resolving the file name
     *
     * To cope with automounters, directories are stored as given,
     * or are derived from the home directory in the passwd file.
     * Within aegis, pathnames have their symbolic links resolved,
     * and any comparison of paths is done on this "system idea"
     * of the pathname.
     */
    os_become_orig();
    dd = os_curdir();
    os_become_undo();
    string_list_constructor(&search_path);
    project_search_path_get(pp2, &search_path, 1);

    /*
     * Find the base for relative filenames.
     */
    based =
      (
          search_path.nstrings >= 1
      &&
          (
            user_relative_filename_preference
            (
                up,
                uconf_relative_filename_preference_base
            )
          ==
            uconf_relative_filename_preference_base
          )
      );
    if (based)
      base = str_copy(search_path.string[0]);
    else
      base = dd;
    string_list_prepend(&search_path, dd);

    /*
     * resolve the path of each file
     * 1. the absolute path of the file name is obtained
     * 2. if the file is inside the search list
     * 3. if neither, error
     */
    string_list_constructor(&wl2);
    number_of_errors = 0;
    for (j = 0; j < wl.nstrings; ++j)
    {
      s1 = wl.string[j];
      if (s1->str_text[0] == '/')
          s2 = str_copy(s1);
      else
          s2 = str_format("%S/%S", base, s1);
      user_become(up);
      s1 = os_pathname(s2, 1);
      user_become_undo();
      str_free(s2);
      s2 = 0;
      for (k = 0; k < search_path.nstrings; ++k)
      {
          s2 = os_below_dir(search_path.string[k], s1);
          if (s2)
            break;
      }
      str_free(s1);
      if (!s2)
      {
          sub_context_ty *scp;

          scp = sub_context_new();
          sub_var_set_string(scp, "File_Name", wl.string[j]);
          project_error(pp, scp, i18n("$filename unrelated"));
          sub_context_delete(scp);
          ++number_of_errors;
          continue;
      }
      project_file_directory_query
      (
          pp2,
          s2,
          &wl_in,
          &wl_out,
          view_path_simple
      );
      if (delta_date != NO_TIME_SET)
          string_list_append_list(&wl_in, &wl_out);
      if (wl_in.nstrings)
      {
          if (output)
          {
            sub_context_ty *scp;

            scp = sub_context_new();
            sub_var_set_charstar
            (
                scp,
                "Name",
                arglex_token_name(arglex_token_output)
            );
            error_intl(scp, i18n("no dir with $name"));
            sub_context_delete(scp);
            ++number_of_errors;
          }

          string_list_append_list_unique(&wl2, &wl_in);
      }
      else
          string_list_append_unique(&wl2, s2);
      string_list_destructor(&wl_in);
      string_list_destructor(&wl_out);
      str_free(s2);
    }
    string_list_destructor(&search_path);
    string_list_destructor(&wl);
    wl = wl2;

    /*
     * ensure that each file
     * is in the baseline
     */
    for (j = 0; j < wl.nstrings; ++j)
    {
      fstate_src      src_data;

      s1 = wl.string[j];
      src_data = project_file_find(pp2, s1, view_path_simple);
      if
      (
          !src_data
      ||
          (delta_date == NO_TIME_SET && src_data->deleted_by)
      )
      {
          sub_context_ty *scp;

          scp = sub_context_new();
          src_data = project_file_find_fuzzy(pp2, s1, view_path_extreme);
          sub_var_set_string(scp, "File_Name", s1);
          if (src_data)
          {
            sub_var_set_string(scp, "Guess", src_data->file_name);
            project_error
            (
                pp2,
                scp,
                i18n("no $filename, closest is $guess")
            );
          }
          else
            project_error(pp2, scp, i18n("no $filename"));
          sub_context_delete(scp);
          ++number_of_errors;
          continue;
      }
      if (src_data && src_data->usage == file_usage_build)
      {
          sub_context_ty *scp;

          scp = sub_context_new();
          sub_var_set_string(scp, "File_Name", s1);
          project_error(pp, scp, i18n("$filename is built"));
          sub_context_delete(scp);
          ++number_of_errors;
      }
    }
    if (number_of_errors)
    {
      sub_context_ty *scp;

      scp = sub_context_new();
      sub_var_set_long(scp, "Number", number_of_errors);
      sub_var_optional(scp, "Number");
      project_fatal(pp, scp, i18n("no files copied"));
      sub_context_delete(scp);
    }

    /*
     * create a fake change,
     * so can set environment variables
     * for the test
     */
    cp_bogus = change_bogus(pp);

    /*
     * Copy each file into the destination directory.
     * Create any necessary directories along the way.
     */
    if (delta_date != NO_TIME_SET)
      project_file_roll_forward(pp2, delta_date, 0);
    for (j = 0; j < wl.nstrings; ++j)
    {
      string_ty      *from;
      string_ty      *to;
      int             from_unlink = 0;

      s1 = wl.string[j];
      if (delta_date != NO_TIME_SET)
      {
          file_event_ty  *fep;
          fstate_src      old_src;

          fep = project_file_roll_forward_get_last(s1);
          if (!fep)
          {
            /*
             * The file doesn't exist yet at this
             * delta.  Omit it.
             */
            continue;
          }

          old_src = change_file_find(fep->cp, s1);
          assert(old_src);
          if (old_src->action == file_action_remove)
          {
            /*
             * The file had been removed at this
             * delta.  Omit it.
             */
            continue;
          }
          from = project_file_version_path(pp2, old_src, &from_unlink);

          /*
           * figure where to send it
           */
          if (output)
            to = str_from_c(output);
          else
            to = str_format("%S/%S", dd, s1);

          /*
           * copy the file
           */
          os_become_orig();
          if (!output)
          {
            os_mkdir_between(dd, s1, 02755);
            if (os_exists(to))
                os_unlink(to);
          }
          copy_whole_file(from, to, 0);

          /*
           * clean up afterwards
           */
          if (from_unlink)
          {
            os_unlink_errok(from);
          }
          os_become_undo();
          str_free(from);
          str_free(to);
      }
      else
      {
          fstate_src      old_src;

          old_src = project_file_find(pp2, s1, view_path_extreme);
          if (!old_src)
            continue;

          from = project_file_path(pp2, s1);
          if (output)
            to = str_from_c(output);
          else
            to = str_format("%S/%S", dd, s1);

          /*
           * copy the file
           */
          os_become_orig();
          if (!output)
          {
            os_mkdir_between(dd, s1, 02755);
            if (os_exists(to))
                os_unlink(to);
          }
          copy_whole_file(from, to, 0);
          os_become_undo();

          /*
           * clean up afterwards
           */
          str_free(from);
          str_free(to);
      }
    }
    change_free(cp_bogus);

    /*
     * release the baseline lock
     */
    if (!output)
      lock_release();

    /*
     * verbose success message
     */
    for (j = 0; j < wl.nstrings; ++j)
    {
      sub_context_ty *scp;

      scp = sub_context_new();
      sub_var_set_string(scp, "File_Name", wl.string[j]);
      project_verbose(pp, scp, i18n("copied $filename"));
      sub_context_delete(scp);
    }

    string_list_destructor(&wl);
    project_free(pp);
    user_free(up);
    trace(("}\n"));
}


static fstate_src
fake_removed_file(project_ty *pp, string_ty *filename)
{
    fstate_src p_src_data;
    fstate_src old_src;

    p_src_data = project_file_find(pp, filename, view_path_simple);
    assert(p_src_data);
    old_src = fstate_src_type.alloc();
    old_src->action = file_action_remove;
    if (p_src_data && p_src_data->edit)
    {
      old_src->usage = p_src_data->usage;
      assert(p_src_data->edit);
      old_src->edit = history_version_copy(p_src_data->edit);
    }
    else
    {
      /* Should never happen.  Yeah, right. */
      old_src->usage = file_usage_source;
      old_src->edit = history_version_type.alloc();
      old_src->edit->revision = str_from_c("1.1");
    }
    return old_src;
}


static void
copy_file_main(void)
{
    string_ty       *dd;
    string_list_ty  wl;
    string_list_ty  wl2;
    string_list_ty  wl_in;
    string_list_ty  wl_out;
    string_ty       *s1;
    string_ty       *s2;
    int             stomp;
    cstate          cstate_data;
    int             j;
    int             k;
    string_ty       *project_name;
    project_ty      *pp;
    project_ty      *pp2;
    long            change_number;
    change_ty       *cp;
    log_style_ty    log_style;
    user_ty         *up;
    char            *output;
    time_t          delta_date;
    long            delta_number;
    char            *delta_name;
    long            delta_from_change;
    int             config_seen;
    int             number_of_errors;
    string_list_ty  search_path;
    char            *branch;
    int             trunk;
    int             read_only;
    int             mode;
    int             based;
    string_ty       *base;
    sub_context_ty  *scp;
    int             rescind;

    trace(("copy_file_main()\n{\n"));
    arglex();
    string_list_constructor(&wl);
    stomp = 0;
    project_name = 0;
    change_number = 0;
    log_style = log_style_append_default;
    output = 0;
    delta_date = NO_TIME_SET;
    delta_number = -1;
    delta_name = 0;
    delta_from_change = 0;
    branch = 0;
    trunk = 0;
    read_only = 0;
    rescind = 0;
    while (arglex_token != arglex_token_eoln)
    {
      switch (arglex_token)
      {
      default:
          generic_argument(copy_file_usage);
          continue;

      case arglex_token_overwriting:
          if (stomp)
            duplicate_option(copy_file_usage);
          stomp = 1;
          break;

      case arglex_token_directory:
          if (arglex() != arglex_token_string)
            option_needs_dir(arglex_token_directory, copy_file_usage);
          goto get_file_names;

      case arglex_token_file:
          if (arglex() != arglex_token_string)
            option_needs_files(arglex_token_file, copy_file_usage);
          /* fall through... */

      case arglex_token_string:
        get_file_names:
          s2 = str_from_c(arglex_value.alv_string);
          string_list_append(&wl, s2);
          str_free(s2);
          break;

      case arglex_token_change:
          if (arglex() != arglex_token_number)
            option_needs_number(arglex_token_change, copy_file_usage);
          /* fall through... */

      case arglex_token_number:
          if (change_number)
            duplicate_option_by_name(arglex_token_change, copy_file_usage);
          change_number = arglex_value.alv_number;
          if (change_number == 0)
            change_number = MAGIC_ZERO;
          else if (change_number < 1)
          {
            scp = sub_context_new();
            sub_var_set_long(scp, "Number", change_number);
            fatal_intl(scp, i18n("change $number out of range"));
            /* NOTREACHED */
            sub_context_delete(scp);
          }
          break;

      case arglex_token_project:
          if (arglex() != arglex_token_string)
            option_needs_name(arglex_token_project, copy_file_usage);
          if (project_name)
            duplicate_option_by_name(arglex_token_project, copy_file_usage);
          project_name = str_from_c(arglex_value.alv_string);
          break;

      case arglex_token_nolog:
          if (log_style == log_style_none)
            duplicate_option(copy_file_usage);
          log_style = log_style_none;
          break;

      case arglex_token_delta:
          if (delta_number >= 0 || delta_name)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_number(arglex_token_delta, copy_file_usage);
              /*NOTREACHED*/

          case arglex_token_number:
            delta_number = arglex_value.alv_number;
            if (delta_number < 0)
            {
                scp = sub_context_new();
                sub_var_set_long(scp, "Number", delta_number);
                fatal_intl(scp, i18n("delta $number out of range"));
                /* NOTREACHED */
                sub_context_delete(scp);
            }
            break;

          case arglex_token_string:
            delta_name = arglex_value.alv_string;
            break;
          }
          break;

      case arglex_token_delta_date:
          if (delta_date != NO_TIME_SET)
            duplicate_option(copy_file_usage);
          if (arglex() != arglex_token_string)
          {
            option_needs_string(arglex_token_delta_date, copy_file_usage);
            /*NOTREACHED*/
          }
          delta_date = date_scan(arglex_value.alv_string);
          if (delta_date == NO_TIME_SET)
          {
            scp = sub_context_new();
            sub_var_set_charstar(scp, "Name", arglex_value.alv_string);
            fatal_intl(scp, i18n("date $name unknown"));
            /* NOTREACHED */
            sub_context_delete(scp);
          }
          break;

      case arglex_token_delta_from_change:
          if (arglex() != arglex_token_number)
          {
            option_needs_number
            (
                arglex_token_delta_from_change,
                copy_file_usage
            );
          }
          if (delta_from_change)
          {
            duplicate_option_by_name
            (
                arglex_token_delta_from_change,
                copy_file_usage
            );
          }
          delta_from_change = arglex_value.alv_number;
          if (delta_from_change == 0)
            delta_from_change = MAGIC_ZERO;
          else if (delta_from_change < 1)
          {
            scp = sub_context_new();
            sub_var_set_long(scp, "Number", change_number);
            fatal_intl(scp, i18n("change $number out of range"));
            /* NOTREACHED */
            sub_context_delete(scp);
          }
          break;

      case arglex_token_output:
          if (output)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_file(arglex_token_output, copy_file_usage);

          case arglex_token_stdio:
            output = "";
            break;

          case arglex_token_string:
            output = arglex_value.alv_string;
            break;
          }
          break;

      case arglex_token_branch:
          if (branch)
            duplicate_option(copy_file_usage);
          switch (arglex())
          {
          default:
            option_needs_number(arglex_token_branch, copy_file_usage);

          case arglex_token_number:
          case arglex_token_string:
            branch = arglex_value.alv_string;
            break;

          case arglex_token_stdio:
            branch = "";
            break;
          }
          break;

      case arglex_token_trunk:
          if (trunk)
            duplicate_option(copy_file_usage);
          ++trunk;
          break;

      case arglex_token_read_only:
          if (read_only)
            duplicate_option(copy_file_usage);
          ++read_only;
          break;

      case arglex_token_wait:
      case arglex_token_wait_not:
          user_lock_wait_argument(copy_file_usage);
          break;

      case arglex_token_base_relative:
      case arglex_token_current_relative:
          user_relative_filename_preference_argument(copy_file_usage);
          break;

      case arglex_token_rescind:
          if (rescind)
            duplicate_option(copy_file_usage);
          rescind = 1;
          break;
      }
      arglex();
    }
    if (!wl.nstrings)
    {
      error_intl(0, i18n("no file names"));
      copy_file_usage();
    }
    if (trunk)
    {
      if (branch)
      {
          mutually_exclusive_options
          (
            arglex_token_branch,
            arglex_token_trunk,
            copy_file_usage
          );
      }
      branch = "";
    }
    if
    (
      (
          (delta_name || delta_number >= 0)
      +
          !!delta_from_change
      +
          (delta_date != NO_TIME_SET)
      )
    >
      1
    )
    {
      mutually_exclusive_options3
      (
          arglex_token_delta,
          arglex_token_delta_date,
          arglex_token_delta_from_change,
          copy_file_usage
      );
    }
    if
    (
      rescind
    &&
      !delta_name
    &&
      delta_number < 0
    &&
      !delta_from_change
    &&
      delta_date == NO_TIME_SET
    )
    {
      scp = sub_context_new();
      sub_var_set_charstar
      (
          scp,
          "Name1",
          arglex_token_name(arglex_token_rescind)
      );
      sub_var_set_charstar
      (
          scp,
          "Name2",
          arglex_token_name(arglex_token_delta)
      );
      fatal_intl(scp, i18n("$name1 needs $name2"));
      /* NOTREACHED */
      sub_context_delete(scp);
    }

    /*
     * make sure output is unambiguous
     */
    if (output)
    {
      if (wl.nstrings != 1)
      {
          scp = sub_context_new();
          sub_var_set_long(scp, "Number", (long)wl.nstrings);
          sub_var_optional(scp, "Number");
          fatal_intl(scp, i18n("single file with -Output"));
          /* NOTREACHED */
          sub_context_delete(scp);
      }
      stomp = 1;
    }

    /*
     * locate project data
     */
    if (!project_name)
      project_name = user_default_project();
    pp = project_alloc(project_name);
    str_free(project_name);
    project_bind_existing(pp);

    /*
     * locate which branch
     */
    if (branch)
      pp2 = project_find_branch(pp, branch);
    else
      pp2 = pp;

    /*
     * locate user data
     */
    up = user_executing(pp);

    /*
     * locate change data
     */
    if (!change_number)
      change_number = user_default_change(up);
    cp = change_alloc(pp, change_number);
    change_bind_existing(cp);

    /*
     * lock the change file
     *
     * Also take a read lock on the baseline, to ensure that it does
     * not change (aeip) for the duration of the build.
     */
    if (!output)
    {
      change_cstate_lock_prepare(cp);
      project_baseline_read_lock_prepare(pp2);
      lock_take();

      log_open(change_logfile_get(cp), up, log_style);
    }
    cstate_data = change_cstate_get(cp);

    /*
     * When there is no explicit output file:
     * It is an error if the change is not in the being_developed state.
     * It is an error if the change is not assigned to the current user.
     */
    if (output)
    {
      switch (cstate_data->state)
      {
      case cstate_state_being_developed:
      case cstate_state_awaiting_review:
      case cstate_state_being_reviewed:
      case cstate_state_awaiting_integration:
      case cstate_state_being_integrated:
          break;

      default:
        wrong_state:
          change_fatal(cp, 0, i18n("bad cp state"));
      }
    }
    else
    {
      if (cstate_data->state != cstate_state_being_developed)
          goto wrong_state;
      if (change_is_a_branch(cp))
          change_fatal(cp, 0, i18n("bad branch cp"));
      if (!str_equal(change_developer_name(cp), user_name(up)))
          change_fatal(cp, 0, i18n("not developer"));
    }

    /*
     * it is an error if the delta does not exist
     */
    if (delta_name)
    {
      s1 = str_from_c(delta_name);
      delta_number = project_history_delta_by_name(pp2, s1, 0);
      str_free(s1);
    }
    if (delta_date != NO_TIME_SET)
    {
      time_t          now;

      /*
       * If the time is in the future, you could get a different
       * answer for the same inpout at some point in the future.
       *
       * This is the "time safe" quality first described by
       * Damon Poole <damon@ede.com>
       */
      time(&now);
      if (delta_date > now)
          project_error(pp2, 0, i18n("date in the future"));
    }
    if (delta_from_change)
    {
      delta_number =
          project_change_number_to_delta_number(pp2, delta_from_change);
    }
    if (delta_number >= 0)
    {
      delta_date = project_history_delta_to_timestamp(pp2, delta_number);
      if (delta_date == NO_TIME_SET)
      {
          scp = sub_context_new();
          sub_var_set_long(scp, "Name", delta_number);
          change_fatal(cp, scp, i18n("no delta $name"));
          /* NOTREACHED */
          sub_context_delete(scp);
      }
      trace(("delta %ld -> delta date %ld\n", delta_number, delta_date));
    }

    /*
     * build the list of places to look
     * when resolving the file name
     *
     * To cope with automounters, directories are stored as given,
     * or are derived from the home directory in the passwd file.
     * Within aegis, pathnames have their symbolic links resolved,
     * and any comparison of paths is done on this "system idea"
     * of the pathname.
     */
    change_search_path_get(cp, &search_path, 1);

    /*
     * Find the base for relative filenames.
     */
    based =
      (
          search_path.nstrings >= 1
      &&
          (
            user_relative_filename_preference
            (
                up,
                uconf_relative_filename_preference_current
            )
          ==
            uconf_relative_filename_preference_base
          )
      );
    if (based)
      base = search_path.string[0];
    else
    {
      os_become_orig();
      base = os_curdir();
      os_become_undo();
    }

    /*
     * resolve the path of each file
     * 1. the absolute path of the file name is obtained
     * 2. if the file is inside the search list
     * 3. if neither, error
     */
    config_seen = 0;
    string_list_constructor(&wl2);
    number_of_errors = 0;
    for (j = 0; j < wl.nstrings; ++j)
    {
      s1 = wl.string[j];
      if (s1->str_text[0] == '/')
          s2 = str_copy(s1);
      else
          s2 = str_format("%S/%S", base, s1);
      user_become(up);
      s1 = os_pathname(s2, 1);
      user_become_undo();
      str_free(s2);
      s2 = 0;
      for (k = 0; k < search_path.nstrings; ++k)
      {
          s2 = os_below_dir(search_path.string[k], s1);
          if (s2)
            break;
      }
      str_free(s1);
      if (!s2)
      {
          scp = sub_context_new();
          sub_var_set_string(scp, "File_Name", wl.string[j]);
          change_error(cp, scp, i18n("$filename unrelated"));
          sub_context_delete(scp);
          ++number_of_errors;
          continue;
      }
      project_file_directory_query
      (
          pp2,
          s2,
          &wl_in,
          &wl_out,
          view_path_simple
      );
      if (delta_date != NO_TIME_SET)
          string_list_append_list(&wl_in, &wl_out);
      if (wl_in.nstrings)
      {
          int             used;

          /*
           * if the user named a directory,
           * add all of the source files in that directory,
           * provided they are not already in the change.
           */
          if (output)
          {
            scp = sub_context_new();
            sub_var_set_charstar
            (
                scp,
                "Name",
                arglex_token_name(arglex_token_output)
            );
            error_intl(scp, i18n("no dir with $name"));
            sub_context_delete(scp);
            ++number_of_errors;
          }
          used = 0;
          for (k = 0; k < wl_in.nstrings; ++k)
          {
            string_ty       *s3;

            s3 = wl_in.string[k];
            if (stomp || !change_file_find(cp, s3))
            {
                if (string_list_member(&wl2, s3))
                {
                  scp = sub_context_new();
                  sub_var_set_string(scp, "File_Name", s3);
                  change_error(cp, scp, i18n("too many $filename"));
                  sub_context_delete(scp);
                  ++number_of_errors;
                }
                else
                  string_list_append(&wl2, s3);
                if (change_file_is_config(cp, s3))
                  ++config_seen;
                ++used;
            }
          }
          if (!used)
          {
            scp = sub_context_new();
            if (s2->str_length)
                sub_var_set_string(scp, "File_Name", s2);
            else
                sub_var_set_charstar(scp, "File_Name", ".");
            sub_var_set_long(scp, "Number", (long)wl_in.nstrings);
            sub_var_optional(scp, "Number");
            change_error
            (
                cp,
                scp,
                i18n("directory $filename contains no relevant files")
            );
            sub_context_delete(scp);
            ++number_of_errors;
          }
      }
      else
      {
          if (string_list_member(&wl2, s2))
          {
            scp = sub_context_new();
            sub_var_set_string(scp, "File_Name", s2);
            change_error(cp, scp, i18n("too many $filename"));
            sub_context_delete(scp);
            ++number_of_errors;
          }
          else
            string_list_append(&wl2, s2);
          if (change_file_is_config(cp, s2))
            ++config_seen;
      }
      string_list_destructor(&wl_in);
      string_list_destructor(&wl_out);
      str_free(s2);
    }
    string_list_destructor(&search_path);
    string_list_destructor(&wl);
    wl = wl2;

    /*
     * ensure that each file
     * 1. is not already part of the change
     * 2. is in the baseline
     */
    for (j = 0; j < wl.nstrings; ++j)
    {
      fstate_src      src_data;

      s1 = wl.string[j];
      if (change_file_find(cp, s1) && !stomp && !output)
      {
          scp = sub_context_new();
          sub_var_set_string(scp, "File_Name", s1);
          change_error(cp, scp, i18n("bad cp, file $filename dup"));
          sub_context_delete(scp);
          ++number_of_errors;
          continue;
      }
      if (output)
      {
          fstate_src      c_src_data;

          /*
           * OK to use a file that "almost" exists
           * in combination with the -Output option
           */
          c_src_data = change_file_find(cp, s1);
          if (c_src_data && c_src_data->action == file_action_create)
            continue;
      }
      src_data = project_file_find(pp2, s1, view_path_simple);
      if
      (
          !src_data
      ||
          (delta_date == NO_TIME_SET && src_data->deleted_by)
      )
      {
          scp = sub_context_new();
          src_data = project_file_find_fuzzy(pp2, s1, view_path_extreme);
          sub_var_set_string(scp, "File_Name", s1);
          if (src_data)
          {
            sub_var_set_string(scp, "Guess", src_data->file_name);
            project_error
            (
                pp2,
                scp,
                i18n("no $filename, closest is $guess")
            );
          }
          else
            project_error(pp2, scp, i18n("no $filename"));
          sub_context_delete(scp);
          ++number_of_errors;
          continue;
      }
      if (src_data && src_data->usage == file_usage_build && !output)
      {
          scp = sub_context_new();
          sub_var_set_string(scp, "File_Name", s1);
          change_error(cp, scp, i18n("$filename is built"));
          sub_context_delete(scp);
          ++number_of_errors;
      }
    }
    if (number_of_errors)
    {
      scp = sub_context_new();
      sub_var_set_long(scp, "Number", number_of_errors);
      sub_var_optional(scp, "Number");
      change_fatal(cp, scp, i18n("no files copied"));
      sub_context_delete(scp);
    }

    /*
     * Copy each file into the development directory.
     * Create any necessary directories along the way.
     *
     * Add each file to the change file,
     * or update the edit number.
     */
    dd = change_development_directory_get(cp, 0);
    if (delta_date != NO_TIME_SET)
      project_file_roll_forward(pp2, delta_date, 0);
    for (j = 0; j < wl.nstrings; ++j)
    {
      string_ty       *from;
      string_ty       *to;
      fstate_src      old_src = 0;
      fstate_src      older_src = 0;

      s1 = wl.string[j];
      trace(("s1 = \"%s\";\n", s1->str_text));
      if (delta_date != NO_TIME_SET)
      {
          file_event_ty   *fep;
          int             from_unlink = 0;

          fep = project_file_roll_forward_get_last(s1);
          if (!fep)
          {
            /*
             * This file had not yet been created at
             * the time of the delta.  Arrange for
             * it to look like it's being removed.
             *
             * In the case of -rescind, it doesn't exist at the
             * previous delta, either, so remove it in this case, too.
             *
             * This is a memory leak.
             */
            old_src = fake_removed_file(pp2, s1);
            older_src = old_src;
          }
          else
          {
            old_src = change_file_find(fep->cp, s1);
            if (rescind)
            {
                fep = project_file_roll_forward_get_older(s1);
                trace(("fep = %lX\n", (long)fep));
                if (fep)
                  older_src = change_file_find(fep->cp, s1);
                else
                {
                  /* This is a memory leak. */
                  older_src = fake_removed_file(pp2, s1);
                }
            }
            else
                older_src = old_src;
          }
          assert(old_src);
          trace(("old_src = %lX\n", (long)old_src));
          assert(older_src);
          trace(("older_src = %lX\n", (long)older_src));
          if (older_src->action == file_action_remove)
          {
            /* Shouldn't we use whiteout like aerm? */
            from = str_from_c("/dev/null");
          }
          else
          {
            from = project_file_version_path(pp2, older_src, &from_unlink);
          }
          trace(("from = \"%s\";\n", from->str_text));

          /*
           * figure where to send it
           */
          if (output)
            to = str_from_c(output);
          else
            to = str_format("%S/%S", dd, s1);

          /*
           * copy the file
           */
          user_become(up);
          if (!output)
          {
            os_mkdir_between(dd, s1, 02755);
            if (os_exists(to))
                os_unlink(to);
          }
          copy_whole_file(from, to, 0);

          /*
           * set the file mode
           */
          mode = 0444;
          if (!read_only)
            mode |= 0600;
          if (older_src->executable)
            mode |= 0111;
          mode &= ~change_umask(cp);
          os_chmod(to, mode);

          /*
           * clean up afterwards
           */
          if (from_unlink)
            os_unlink_errok(from);
          user_become_undo();
          str_free(from);
          str_free(to);
      }
      else
      {
          if (cstate_data->state == cstate_state_being_integrated)
          {
            from =
                str_format
                (
                  "%S/%S",
                  change_integration_directory_get(cp, 0),
                  s1
                );
          }
          else
          {
            from = project_file_path(pp2, s1);
          }
          if (output)
            to = str_from_c(output);
          else
            to = str_format("%S/%S", dd, s1);

          /*
           * We need the file information for the execuable bit.
           */
          old_src = project_file_find(pp2, s1, view_path_simple);
          assert(old_src);
          older_src = old_src;

          /*
           * copy the file
           */
          user_become(up);
          if (!output)
          {
            os_mkdir_between(dd, s1, 02755);
            if (os_exists(to))
                os_unlink(to);
          }
          copy_whole_file(from, to, 0);

          /*
           * set the file mode
           */
          mode = 0444;
          if (!read_only)
            mode |= 0600;
          if (old_src->executable)
            mode |= 0111;
          mode &= ~change_umask(cp);
          os_chmod(to, mode);
          user_become_undo();

          /*
           * clean up afterwards
           */
          str_free(from);
          str_free(to);
      }

      if (!output)
      {
          fstate_src      c_src_data;
          fstate_src      p_src_data;

          assert(!old_src == !older_src);
          p_src_data = older_src;
          if (!p_src_data)
            p_src_data = project_file_find(pp2, s1, view_path_simple);
          assert(p_src_data);
          assert(p_src_data->edit);
          assert(p_src_data->edit->revision);
          c_src_data = change_file_find(cp, s1);
          if (!c_src_data)
          {
            c_src_data = change_file_new(cp, s1);
            c_src_data->action =
                (
                  p_src_data->action == file_action_remove
                ?
                  file_action_remove
                :
                  (read_only ? file_action_insulate : file_action_modify)
                );
            c_src_data->usage = p_src_data->usage;

            /*
             * Watch out for test times.
             */
            if (!read_only)
            {
                int             f_idx;
                int             more_tests;

                switch (c_src_data->usage)
                {
                case file_usage_test:
                case file_usage_manual_test:
                  /*
                   * The change now has at
                   * least one test, so cancel
                   * any testing exemption.
                   * (But test_baseline_exempt
                   * is still viable.)
                   */
                  change_rescind_test_exemption(cp);

                  /*
                     * If there are no more tests, then the change
                     *  must be made regression test exempt
                     */
                  more_tests = 0;
                  for (f_idx = 0; ; ++f_idx)
                  {
                      fstate_src      p_src_data_proj;

                      p_src_data_proj =
                        project_file_nth(pp2, f_idx, view_path_simple);
                      if (!p_src_data_proj)
                        break;
                      switch (p_src_data_proj->usage)
                      {
                      case file_usage_test:
                      case file_usage_manual_test:
                        more_tests = 1;
                        break;

                      case file_usage_source:
                      case file_usage_build:
                        continue;
                      }
                      break;
                  }
                  if (more_tests)
                      change_force_regression_test_exemption(cp);
                  break;

                case file_usage_source:
                case file_usage_build:
                  break;
                }
            }
          }
          if (old_src != older_src)
          {
            /*
             * In the case of -rescind, crank forward to the following
             * version.  That way we have copied the previous version,
             * but claim the following version.  This will have the
             * effect to backing out the delta specified.
             */
            assert(old_src);
            p_src_data = old_src;
            assert(p_src_data);
            assert(p_src_data->edit);
            assert(p_src_data->edit->revision);
          }

          /*
           * p_src_data->edit
           *      The head revision of the branch.
           * p_src_data->edit_origin
           *      The version originally copied.
           *
           * c_src_data->edit
           *      Not meaningful until after integrate pass.
           * c_src_data->edit_origin
           *      The version originally copied.
           * c_src_data->edit_origin_new
           *      Updates branch edit_origin on
           *      integrate pass.
           */
          if (c_src_data->edit)
          {
            assert(c_src_data->edit->revision);
            history_version_type.free(c_src_data->edit);
            c_src_data->edit = 0;
          }
          if (c_src_data->edit_origin)
          {
            assert(c_src_data->edit_origin->revision);
            history_version_type.free(c_src_data->edit_origin);
            c_src_data->edit_origin = 0;
          }
          if (c_src_data->edit_origin_new)
          {
            assert(c_src_data->edit_origin_new->revision);
            history_version_type.free(c_src_data->edit_origin_new);
            c_src_data->edit_origin_new = 0;
          }
          assert(p_src_data->edit);
          assert(p_src_data->edit->revision);
          c_src_data->edit_origin = history_version_copy(p_src_data->edit);

          /*
           * Copying the config file into a change
           * invalidates all of the file fingerprints.
           * This is because the diff command,
           * test_command, build_command, etc, could be
           * changed when the config file is edited.
           */
          if (config_seen && c_src_data->file_fp)
          {
            fingerprint_type.free(c_src_data->file_fp);
            c_src_data->file_fp = 0;
          }
      }

      /*
       * verbose progress message
       */
      scp = sub_context_new();
      sub_var_set_string(scp, "File_Name", s1);
      change_verbose(cp, scp, i18n("copied $filename"));
      sub_context_delete(scp);
    }

    if (!output)
    {
      /*
       * the number of files changed,
       * so stomp on the validation fields.
       */
      change_build_times_clear(cp);

      /*
       * update the copyright years
       */
      change_copyright_years_now(cp);
    }

    /*
     * release the locks
     */
    if (!output)
    {
      /*
       * run the change file command
       * and the project file command if necessary
       */
      change_run_copy_file_command(cp, &wl, up);
      change_run_project_file_command(cp, up);

      change_cstate_write(cp);
      commit();
      lock_release();
    }

    /*
     * verbose success message
     */
    scp = sub_context_new();
    sub_var_set_long(scp, "Number", (long)wl.nstrings);
    sub_var_optional(scp, "Number");
    change_verbose(cp, scp, i18n("copy file complete"));
    sub_context_delete(scp);

    /*
     * run the change file command
     */
    string_list_destructor(&wl);
    project_free(pp);
    change_free(cp);
    user_free(up);
    trace(("}\n"));
}


void
copy_file(void)
{
    static arglex_dispatch_ty dispatch[] =
    {
      { arglex_token_help,        copy_file_help, },
      { arglex_token_list,        copy_file_list, },
      { arglex_token_independent, copy_file_independent, },
    };

    trace(("copy_file()\n{\n"));
    arglex_dispatch(dispatch, SIZEOF(dispatch), copy_file_main);
    trace(("}\n"));
}

Generated by  Doxygen 1.6.0   Back to index