Logo Search packages:      
Sourcecode: aegis version File versions

send.c

/*
 *    aegis - project change supervisor
 *    Copyright (C) 2002 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 manipulate sends
 */

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

#include <arglex3.h>
#include <change.h>
#include <change/branch.h>
#include <change/file.h>
#include <error.h> /* assert */
#include <help.h>
#include <input/file.h>
#include <send.h>
#include <gettime.h>
#include <os.h>
#include <output/file.h>
#include <output/gzip.h>
#include <output/tar.h>
#include <progname.h>
#include <project.h>
#include <project/file.h>
#include <project/file/roll_forward.h>
#include <project/history.h>
#include <str_list.h>
#include <sub.h>
#include <user.h>


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


static void
usage(void)
{
    char            *progname;

    progname = progname_get();
    fprintf(stderr, "Usage: %s --send [ <option>... ]\n", progname);
    fprintf(stderr, "       %s --help\n", progname);
    exit(1);
}


void
send(void)
{
    string_ty       *project_name;
    long            change_number;
    char            *branch;
    int             grandparent;
    int             trunk;
    output_ty       *ofp;
    output_ty       *tar_p;
    input_ty        *ifp;
    project_ty      *pp;
    change_ty       *cp;
    user_ty         *up;
    cstate          cstate_data;
    string_ty       *output;
    time_t          now;
    size_t          j;
    int             baseline;
    int             entire_source;
    int             compress;
    long            delta_number;
    time_t          delta_date;
    char            *delta_name;
    string_list_ty  wl;

    branch = 0;
    change_number = 0;
    grandparent = 0;
    project_name = 0;
    trunk = 0;
    output = 0;
    baseline = 0;
    entire_source = -1;
    compress = -1;
    delta_date = NO_TIME_SET;
    delta_number = -1;
    delta_name = 0;
    arglex();
    while (arglex_token != arglex_token_eoln)
    {
      switch (arglex_token)
      {
      default:
          generic_argument(usage);
          continue;

      case arglex_token_baseline:
          if (baseline)
            duplicate_option(usage);
          baseline = 1;
          break;

      case arglex_token_entire_source:
          if (entire_source > 0)
            duplicate_option(usage);
          if (entire_source >= 0)
          {
            mutually_exclusive_options
            (
                arglex_token_entire_source,
                arglex_token_entire_source_not,
                usage
            );
          }
          entire_source = 1;
          break;

      case arglex_token_entire_source_not:
          if (entire_source == 0)
            duplicate_option(usage);
          if (entire_source >= 0)
          {
            mutually_exclusive_options
            (
                arglex_token_entire_source,
                arglex_token_entire_source_not,
                usage
            );
          }
          entire_source = 0;
          break;

      case arglex_token_change:
      case arglex_token_delta_from_change:
          if (arglex() != arglex_token_number)
            option_needs_number(arglex_token_change, usage);
          /* fall throught... */

      case arglex_token_number:
          if (change_number)
            duplicate_option_by_name(arglex_token_change, usage);
          change_number = arglex_value.alv_number;
          if (!change_number)
            change_number = MAGIC_ZERO;
          else if (change_number < 0)
          {
            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 */
          }
          break;

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

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

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

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

      case arglex_token_grandparent:
          if (grandparent)
            duplicate_option(usage);
          ++grandparent;
          break;

      case arglex_token_output:
          if (output)
            duplicate_option(usage);
          switch (arglex())
          {
          default:
            option_needs_file(arglex_token_output, usage);
            /* NOTREACHED */

          case arglex_token_stdio:
            output = str_from_c("");
            break;

          case arglex_token_string:
            output = str_from_c(arglex_value.alv_string);
            break;
          }
          break;

      case arglex_token_compress:
          if (compress > 0)
            duplicate_option(usage);
          else if (compress >= 0)
          {
              compress_yuck:
            mutually_exclusive_options
            (
                arglex_token_compress,
                arglex_token_compress_not,
                usage
            );
          }
          compress = 1;
          break;

      case arglex_token_compress_not:
          if (compress == 0)
            duplicate_option(usage);
          else if (compress >= 0)
            goto compress_yuck;
          compress = 0;
          break;

      case arglex_token_delta:
          if (delta_number >= 0 || delta_name)
            duplicate_option(usage);
          switch (arglex())
          {
          default:
            option_needs_number(arglex_token_delta, 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(usage);
          if (arglex() != arglex_token_string)
          {
            option_needs_string(arglex_token_delta_date, usage);
            /*NOTREACHED*/
          }
          delta_date = date_scan(arglex_value.alv_string);
          if (delta_date == NO_TIME_SET)
            fatal_date_unknown(arglex_value.alv_string);
          break;
      }
      arglex();
    }

    /*
     * reject illegal combinations of options
     */
    if (change_number && baseline)
    {
      mutually_exclusive_options
      (
          arglex_token_change,
          arglex_token_baseline,
          usage
      );
    }
    if (grandparent)
    {
      if (branch)
      {
          mutually_exclusive_options
          (
            arglex_token_branch,
            arglex_token_grandparent,
            usage
          );
      }
      if (trunk)
      {
          mutually_exclusive_options
          (
            arglex_token_trunk,
            arglex_token_grandparent,
            usage
          );
      }
      branch = "..";
    }
    if (trunk)
    {
      if (branch)
      {
          mutually_exclusive_options
          (
            arglex_token_branch,
            arglex_token_trunk,
            usage
          );
      }
      branch = "";
    }
    if
    (
      (
          (delta_name || delta_number >= 0)
      +
          !!change_number
      +
          (delta_date != NO_TIME_SET)
      )
    >
      1
    )
    {
      mutually_exclusive_options3
      (
          arglex_token_delta,
          arglex_token_delta_date,
          arglex_token_change,
          usage
      );
    }

    if (entire_source < 0)
      entire_source = 0;

    /*
     * 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 the other branch
     */
    if (branch)
      pp = project_find_branch(pp, branch);

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

    /*
     * it is an error if the delta does not exist
     */
    if (delta_name)
    {
      string_ty   *s1;

      s1 = str_from_c(delta_name);
      change_number = project_history_change_by_name(pp, s1, 0);
      str_free(s1);
      delta_name = 0;
    }
    time(&now);
    if (delta_date != NO_TIME_SET)
    {
      /*
       * 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>
       */
      if (delta_date > now)
          project_error(pp, 0, i18n("date in the future"));

      /*
       * Now find the change number corresponding.
       */
      change_number = project_history_change_by_timestamp(pp, delta_date);
    }
    if (delta_number >= 0)
    {
      /* does not return if no such delta number */
      change_number = project_history_change_by_delta(pp, delta_number);
      delta_number = 0;
    }

    /*
     * locate change data
     */
    if (baseline)
      cp = change_copy(project_change_get(pp));
    else
    {
      if (!change_number)
          change_number = user_default_change(up);
      cp = change_alloc(pp, change_number);
      change_bind_existing(cp);
    }

    /*
     * Check the change state.
     */
    cstate_data = change_cstate_get(cp);
    switch (cstate_data->state)
    {
    default:
      change_fatal(cp, 0, i18n("bad send state"));

    case cstate_state_completed:
      /*
       * Need to reconstruct the appropriate file histories.
       */
      project_file_roll_forward
      (
          pp,
          (
            (delta_date != NO_TIME_SET)
          ?
            delta_date
          :
            change_completion_timestamp(cp)
          ),
          0
      );
      break;

    case cstate_state_being_integrated:
    case cstate_state_awaiting_integration:
    case cstate_state_being_reviewed:
    case cstate_state_awaiting_review:
    case cstate_state_being_developed:
      break;
    }

    /*
     * open the output
     */
    os_become_orig();
    ofp = output_file_binary_open(output);
    if (compress)
      ofp = output_gzip(ofp);
    tar_p = output_tar(ofp);
    os_become_undo();

    /*
     * Scan for files to be added to the output.
     */
    string_list_constructor(&wl);
    for (j = 0;; ++j)
    {
      fstate_src      src_data;

      src_data = change_file_nth(cp, j);
      if (!src_data)
          break;
      if (src_data->action == file_action_insulate)
          continue;
      if (src_data->about_to_be_created_by)
          continue;
      if
      (
          src_data->usage == file_usage_build
      &&
          src_data->action == file_action_modify
      )
          continue;
      string_list_append(&wl, src_data->file_name);
    }
    if (entire_source)
    {
      /*
       * Actually, this list needs to be at the time of the delta.
       * (So, we keep almost all files, and toss them later.)
       */
      for (j = 0;; ++j)
      {
          fstate_src      src_data;

          src_data = project_file_nth(pp, j, view_path_extreme);
          if (!src_data)
            break;
          string_list_append_unique(&wl, src_data->file_name);
      }
    }
    if (!wl.nstrings)
      change_fatal(cp, 0, i18n("bad send no files"));

    /*
     * sort the files by name
     */
    string_list_sort(&wl);

    /*
     * add each of the relevant source files to the archive
     */
    for (j = 0; j < wl.nstrings; ++j)
    {
      string_ty   *filename;
      fstate_src      csrc = 0;
      long            len;
      string_ty       *abs_filename = 0;
      int             abs_filename_unlink = 0;

      filename = wl.string[j];

      ifp = 0;
      switch (cstate_data->state)
      {
          file_event_ty  *fep;

      case cstate_state_awaiting_development:
          assert(0);
          continue;

      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:
          /*
           * Get the input file.
           */
          csrc = change_file_find(cp, filename);
          if (!csrc)
            csrc = project_file_find(pp, filename, view_path_simple);
          if (csrc->action == file_action_remove)
            continue;
          if (csrc->action == file_action_insulate)
            continue;
          if (csrc->usage == file_usage_build)
            continue;
          if (csrc->deleted_by)
            continue;
          if (csrc->about_to_be_created_by)
            continue;

          abs_filename = change_file_path(cp, csrc->file_name);
          if (!abs_filename)
              abs_filename = project_file_path(pp, csrc->file_name);
          abs_filename_unlink = 0;

          /*
           * The executable field is only set by aeipass, so we need
           * to go and look at the file itself for accurate information
           * for changes which are not yet in the completed state.
           */
          os_become_orig();
          csrc->executable = os_executable(abs_filename);
          os_become_undo();
          break;

      case cstate_state_completed:
          /*
           * Extract the file from history.
           */
          fep = project_file_roll_forward_get_last(filename);
          if (!fep)
            continue;
          csrc = change_file_find(fep->cp, filename);
          assert(csrc);
          if (!csrc)
            continue;
          if (csrc->action == file_action_remove)
            continue;
          if (csrc->action == file_action_insulate)
            continue;
          if (csrc->usage == file_usage_build)
            continue;
          abs_filename =
            project_file_version_path
            (
                pp,
                csrc,
                &abs_filename_unlink
            );
          break;
      }

      /*
       * Put the whole file into the archive.
       */
      assert(csrc);
      switch (csrc->action)
      {
      case file_action_remove:
      case file_action_transparent:
          break;

      case file_action_create:
      case file_action_modify:
      case file_action_insulate:
          os_become_orig();
          ifp = input_file_open(abs_filename);
          assert(ifp);
          len = input_length(ifp);
          ofp = output_tar_child(tar_p, filename, len, csrc->executable);
          input_to_output(ifp, ofp);
          input_delete(ifp);
          output_delete(ofp);
          os_become_undo();
          break;
      }

      /*
       * Free a bunch of strings.
       */
      if (abs_filename_unlink)
      {
          os_become_orig();
          os_unlink_errok(abs_filename);
          os_become_undo();
      }
      str_free(abs_filename);
    }
    string_list_destructor(&wl);

    /*
     * finish writing the tar archive
     */
    os_become_orig();
    output_delete(tar_p);
    os_become_undo();

    /*
     * clean up and go home
     */
    change_free(cp);
    project_free(pp);
}

Generated by  Doxygen 1.6.0   Back to index