Logo Search packages:      
Sourcecode: fhist version File versions

update.c

/*
 *    fhist - file history and comparison tools
 *    Copyright (C) 1991-1994, 1998-2000, 2002 Peter Miller;
 *    All rights reserved.
 *
 *    Derived from a work
 *    Copyright (C) 1990 David I. Bell.
 *
 *    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 append the changes for a new edit to the history file.
 */

#include <ac/ctype.h>
#include <ac/errno.h>
#include <ac/pwd.h>
#include <ac/stdio.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>

#include <breaks.h>
#include <cmalloc.h>
#include <compare.h>
#include <error_intl.h>
#include <fcheck.h>
#include <fhist.h>
#include <fileio.h>
#include <input/file.h>
#include <input/file_text.h>
#include <input/quotprinenco.h>
#include <modlin.h>
#include <str.h>
#include <subroutine.h>
#include <trace.h>
#include <undo.h>
#include <update.h>


static char *
get_user_login_name(void)
{
    static char     buffer[20];
    struct passwd   *pw;
    int             uid;

    uid = geteuid();
    pw = getpwuid(uid);
    if (!pw)
      sprintf(buffer, "%d", uid);
    else
    {
      size_t          len;

      len = strlen(pw->pw_name);
      if (len >= sizeof(buffer))
          len = sizeof(buffer) - 1;
      memcpy(buffer, pw->pw_name, len);
      buffer[len] = 0;
    }
    return buffer;
}


/*
 * Add the lines in the history file for the specified new edit number.
 * This are the beginning and end lines, the file line, and the default
 * and user-supplied remark lines.  These lines are written to the current
 * position in the file.  Errors are remembered by the file structure.
 */

static void
addhistory(FILE *fp, char *remarks, long editnumber)
{
    time_t      bintime;      /* binary time of day */
    long        len;    /* length of remark line */

    time(&bintime);
    fprintf(fp, "%c %ld\n", T_BEGINEDIT, editnumber);
    fprintf(fp, "%c %s   %s", T_REMARK, get_user_login_name(), ctime(&bintime));
    while (*remarks)
    {
      len = strlen(remarks);
      fprintf(fp, "%c ", T_REMARK);
      writefx(fp, remarks, len, sc.historyname);
      remarks += (len + 1);
    }
    fprintf(fp, "%c 0\n", T_FILE);
    fprintf(fp, "%c %ld 0 0\n", T_ENDEDIT, editnumber);
}


/*
 * Describe help about entering remarks.
 */

static void
helpremarks(void)
{
    sub_context_ty  *scp;
    string_ty         *s;

    scp = sub_context_new();
    sub_var_set_charstar(scp, "Module", sc.modulename);
    sub_var_optional(scp, "Module");
    s = subst_intl(scp, i18n("remarks help here\n"));
    sub_context_delete(scp);
    printf("\n%s\n", s->str_text);
    str_free(s);
}


/*
 * Read remark lines from the user to describe the reason why an edit
 * was made.  This is made up of lines separated by nulls, with a double
 * null at the end.  The remarks can be from the terminal or from a file.
 *
 * Remark files are text.
 */

static char *
getremarks(char *modulename, char *remarkname, long editnumber)
{
    char        *buf;   /* buffer for input */
    FILE        *fp;    /* remark file */
    char        *cp;    /* current line for retyping */
    long        avail;  /* total characters available */
    long        used;   /* number of characters used */
    long        line;
    int               ch;
    sub_context_ty  *scp;
    string_ty         *s;

    if (remarkname && (*remarkname == '\0'))    /* no remarks at all */
      return "";
    used = 0;
    avail = 400;
    buf = cm_alloc_and_check(avail + 4);

    /*
     * If remark file name was supplied, read remarks from it.
     */
    if (remarkname)
    {
      fp = fopen_and_check(remarkname, "r");
      for (;;)
      {
          int bin = 0;
          cp = readlinef(fp, &line, 0, remarkname, &bin);
          if (!cp)
            break;
          if (bin)
            binary_fatal(remarkname);
          if ((used + line) >= avail)
          {
            avail = used + line + 200;
            buf = cm_realloc_and_check(buf, avail + 4);
          }
          strcpy(&buf[used], cp);
          used += (line + 1);
      }
      fclose_and_check(fp, remarkname);
      buf[used] = '\0';
      return buf;
    }

    /*
     * No remark file specified, so get remarks from user.
     */
    scp = sub_context_new();
    sub_var_set_charstar(scp, "Module", modulename);
    sub_var_set_long(scp, "Number", editnumber);
    s =
      subst_intl
      (
          scp,
i18n("please type remarks for edit $number of \"$module\" (type \"!h\" \
for help)")
      );
    sub_context_delete(scp);
    printf("\n%s\n", s->str_text);
    str_free(s);
    line = 1;
    for (;;)
    {
      if ((used + 200) > avail)
      {
          avail += 200;
          buf = cm_realloc_and_check(buf, avail + 4);
      }
      printf("%ld> ", line);
      fflush(stdout);
      if (fgets(&buf[used], 200, stdin) == NULL)
          break;
      ch = buf[used];
      if (ch == '\n')
            break;
      if (ch != '!')
      {
          /* normal line */
          used += strlen(&buf[used]) + 1;
          line++;
          continue;
      }
      buf[used] = '\0';
      ch = buf[used+1];       /* control line */
      if (isupper((unsigned char)ch))
          ch = tolower((unsigned char)ch);
      switch (ch)
      {
      case 'b':
          /* back up to previous line */
          if (line <= 1)
            break;
          line--;
          cp = buf;
          for (ch = 1; ch < line; ch++)
            cp += (strlen(cp) + 1);
          used = (cp - buf);
          break;

      case 'c':
          /* restart the remark */
          scp = sub_context_new();
          s =
            subst_intl
            (
                scp,
                i18n("remarks cleared, please type new remarks:")
            );
          sub_context_delete(scp);
          printf("\n%s\n", s->str_text);
          str_free(s);
          used = 0;
          line = 1;
          break;

      case 'h':
          /* help */
          helpremarks();
          break;

      case 'q':
          /* quit the command */
          fatal_intl(0, i18n("no action taken"));

      case 'r':
          /* retype remarks */
          printf("\n");
          for (ch = 0, cp = buf; *cp; cp += (strlen(cp) + 1))
            printf("%d> %s", ++ch, cp);
          break;

      default:
          /* unknown command */
          if (!isgraph((unsigned char)ch))
            ch = ' ';
          scp = sub_context_new();
          sub_var_set_format(scp, "Name", "!%c", ch);
          s = subst_intl(scp, i18n("unknown command \"$name\" - ignored"));
          sub_context_delete(scp);
          printf("%s\n", s->str_text);
          str_free(s);
          break;
      }
    }
    buf[used] = '\0';         /* double null to end string */
    return buf;
}


/*
 * Do the copy of the source file to the destination file, while
 * modifying the lines as necessary.  Returns nonzero on an error.
 */

static void
docopy(input_ty *ip, FILE *op, const char *ofn)
{
    char        *cp;    /* current line */
    long        linelen;      /* current line length */
    long        i;            /* line count */

    for (i = sc.modifylines; i > 0; i--)
    {
      int bin = 0;
      cp = input_readline(ip, &linelen, 0, &bin);
      if (cp == NULL)
          return;
      if (bin)
          binary_fatal(input_name(ip));
      cp = modifyline(cp, &linelen, (INFO *) NULL);
      writefx(op, cp, linelen, ofn);
    }
    for (;;)
    {
      int bin = 0;
      cp = input_readline(ip, &linelen, 0, &bin);
      if (cp == NULL)
          return;
      if (bin)
          binary_fatal(input_name(ip));
      writefx(op, cp, linelen, ofn);
    }
}


/*
 * Create a new module.
 * This creates the new history file, and the initial source file.
 * This will start as edit 1, with the given file as the original source.
 *
 * The history (.e) file is binary, because we need to seek in it.
 * The source (.s) file is text, because we don't need to seek in it.
 * The input files are text, by definition.
 * The output files are text, by definition.
 */

void
createhistory(char *inputname, char *editname)
{
    FILE        *fp;    /* history file */
    input_ty          *ip;    /* input filename */
    FILE        *up;    /* updated source file */
    long        pospos, editpos;
    char        *remarks;     /* remarks for new file */
    sub_context_ty  *scp;

    /*
     * Verify that the module doesn't already exist, and
     * open up the initial file for reading.
     */
    fp = fopen(sc.historyname, "rb");
    if (fp)
    {
      fclose(fp);
      scp = sub_context_new();
      sub_var_set_charstar(scp, "Module", sc.modulename);
      sub_var_set_charstar(scp, "File_Name", sc.basename);
      fatal_intl
      (
          scp,
          i18n("module \"$module\" already exists as \"$filename\"")
      );
    }
    if (errno != ENOENT)
    {
      scp = sub_context_new();
      sub_errno_set(scp);
      sub_var_set_charstar(scp, "File_Name", sc.historyname);
      fatal_intl(scp, i18n("open \"$filename\": $errno"));
    }
    up = fopen(sc.sourcename, "rb");
    if (up)
    {
      fclose(up);
      scp = sub_context_new();
      sub_var_set_charstar(scp, "Module", sc.modulename);
      sub_var_set_charstar(scp, "File_Name", sc.basename);
      fatal_intl
      (
          scp,
         i18n("source for module \"$module\" already exists as \"$filename\"")
      );
    }
    if (errno != ENOENT)
    {
      scp = sub_context_new();
      sub_errno_set(scp);
      sub_var_set_charstar(scp, "File_Name", sc.sourcename);
      fatal_intl(scp, i18n("open \"$filename\": $errno"));
    }

    if (fc.binary)
    {
          ip = input_file_open(inputname);
          ip = input_quoted_printable_encode(ip, 1);
    }
    else
          ip = input_file_text_open(inputname);

    /*
     * Try to create the history source file, and if successful, then
     * get the initial remarks for the module.
     */
    if (fc.verbosity)
    {
      scp = sub_context_new();
      sub_var_set_charstar(scp, "Module", sc.modulename);
      sub_var_set_charstar(scp, "File_Name", inputname);
      error_intl
      (
          scp,
         i18n("creating new module \"$module\" from initial file \"$filename\"")
      );
      sub_context_delete(scp);
    }
    undo_unlink(sc.sourcename);
    up = fopen_and_check(sc.sourcename, "wb");
    remarks = getremarks(sc.modulename, sc.remarkname, 1L);

    /*
     * Copy the original file into the history latest source file,
     * with the extra line at the front identifying it as edit 1.
     */
    breaksoff();
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
            error_raw("[Copying \"%s\" to \"%s\"]", inputname, sc.sourcename);
#endif
    fprintf(up, "%c 1\n", T_BEGINEDIT);
    if (ferror(up))
    {
      scp = sub_context_new();
      sub_errno_set(scp);
      sub_var_set_charstar(scp, "File_Name", sc.sourcename);
      fatal_intl(scp, i18n("write \"$filename\": $errno"));
    }
    docopy(ip, up, sc.sourcename);
    fflush_and_check(up, sc.sourcename);
    fclose_and_check(up, sc.sourcename);
    input_delete(ip);

    /*
     * Write the initial history file data
     *      create it binary
     */
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
      error_raw("[Creating history file \"%s\"]", sc.historyname);
#endif
    undo_unlink(sc.historyname);
    fp = fopen_and_check(sc.historyname, "w+b");
    fprintf(fp, HEADERFORMAT, T_HEADER, 1L, 1L, 0L, 0L);
    editpos = ftell(fp);
    addhistory(fp, remarks, 1L);
    pospos = ftell(fp);
    fprintf(fp, (editname ? "%c 1 %ld %s\n" : "%c 1 %ld\n"),
          T_POSITION, editpos, editname);
    fprintf(fp, "%c %ld\n", T_EOF, ftell(fp));
    seekf(fp, 0L, sc.historyname);
    fprintf(fp, HEADERFORMAT, T_HEADER, 1L, 1L, 0L, pospos);
    fflush_and_check(fp, sc.historyname);
    fclose_and_check(fp, sc.historyname);
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
      error_raw("[Creation complete]");
#endif
    breakson();
}


/*
 * Write the new source file into a temporary file in the proper directory.
 * This is essentially a copy, except for the initial line indicating the
 * edit number of the new source.  This copy is done to guarantee that there
 * is enough disk space in order to finish the update.  If there is not, then
 * nothing harmful will have been done to the old source or history files.
 * It is assumed that the history file has already been opened.
 *
 * The history (.e) file is binary, because we need to seek in it.
 * The source (.s) file is text, because we don't need to seek in it.
 * The input files are text, by definition.
 * The output files are text, by definition.
 */

static void
writesource(char *inputname)
{
    input_ty          *ip;          /* input file */
    FILE        *up;          /* output file */
    string_ty         *update_name;
    sub_context_ty  *scp;

    update_name = str_format("%s%s", sc.basename, EXT_NEW);
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
    {
      error_raw
      (
          "[Copying \"%s\" to temporary file \"%s\"]",
          inputname,
          updatename->str_text
      );
    }
#endif

    if (fc.binary)
    {
      ip = input_file_open(inputname);
      ip = input_quoted_printable_encode(ip, 1);
    }
    else
      ip = input_file_text_open(inputname);

    undo_unlink(update_name->str_text);
    up = fopen_and_check(update_name->str_text, "wb");
    fprintf(up, "%c %ld\n", T_BEGINEDIT, sc.lastedit + 1);
    if (ferror(up))
    {
      scp = sub_context_new();
      sub_errno_set(scp);
      sub_var_set_string(scp, "File_Name", update_name);
      fatal_intl(scp, i18n("write \"$filename\": $errno"));
    }
    docopy(ip, up, update_name->str_text);
    fflush_and_check(up, update_name->str_text);
    fclose_and_check(up, update_name->str_text);
    input_delete(ip);
    str_free(update_name);
}


/*
 * Compare two files, and return the changes into a snake list.
 * This works by calling the fcomp module which is loaded with us.
 * Does not return if an error ocurred.
 */

static void
comparefiles(char *file1, char *file2, long skip2)
{
    trace(("comparefiles(file1 = \"%s\", file2 = \"%s\", slip2 = %d)\n{\n",
      file1, file2, skip2));
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
            error_raw("[Comparing files \"%s\" and \"%s\"]", file1, file2);
#endif
    fc.fileBskip = skip2;
    fc.fileAmodify = sc.modifylines;
    fc.fileBmodify = sc.modifylines;
    fc.maxchanges = INFINITY;
    fcomp(file1, file2);
    trace(("}\n"));
}


/*
 * Swap the snake line numbers for two files to appear as if the comparison
 * was in the other order.  This is used when showing differences and then
 * updating, since showing differences needs the changes to the new version,
 * but updating needs the changes back to the older version.
 */

static void
swapfiles(void)
{
    SNAKE       *sp;    /* current snake */
    long        temp;   /* temporary for swapping */
    FILEINFO          tempfile;     /* temporary for swapping */

    for (sp = fc.snakelist; sp; sp = sp->next)
    {
      temp = sp->line1;
      sp->line1 = sp->line2;
      sp->line2 = temp;
    }
    tempfile = fc.fileA;
    fc.fileA = fc.fileB;
    fc.fileB = tempfile;
    temp = fc.deletes;
    fc.deletes = fc.inserts;
    fc.inserts = temp;
}


/*
 * Write out the edit script corresponding to the results of the
 * comparison between two files.  Errors cause premature finishing,
 * and are accessible by using ferror afterwards.  This routine knows
 * the data structures defined by the fcomp module.  This routine is
 * also called after the begin line and remark lines have been
 * skipped over.
 */

static void
writeedits(FILE *fp, long editnumber)
{
    SNAKE       *sp;    /* current snake */
    long        line1;
    long        line2;  /* current line numbers in files */
    long        inserts;
    long        deletes;      /* number of inserts and deletes needed */
    long        totalinserts;
    long        totaldeletes; /* total lines inserted or deleted */
    long        i;
    char        *cp;    /* line being inserted */

    totalinserts = 0;
    totaldeletes = 0;
    line1 = 0;
    line2 = 0;
    for (sp = fc.snakelist; sp; sp = sp->next)
    {
      deletes = sp->line1 - line1;
      inserts = sp->line2 - line2;
#if 0
      if (fc.debugflag)
      {
            error_raw
            (
          "[line1 %ld  line2 %ld  count %ld  insert %ld  delete %ld]",
                  sp->line1,
                  sp->line2,
                  sp->count,
                  inserts,
                  deletes
            );
}
#endif
      if (deletes || inserts)
      {
          fprintf(fp, "%c %ld %ld %ld\n", T_CHANGE, line1 + 1, inserts, deletes);
          if (ferror(fp))
                  goto bomb;
      }
      for (i = 0; i < inserts; i++)
      {
          fprintf(fp, "%c ", T_TEXT);
          cp = fc.fileB.f_lines[line2 + i]->l_data;
          writefx(fp, cp, (long) strlen(cp), sc.historyname);
      }
      totalinserts += inserts;
      totaldeletes += deletes;
      line1 = sp->line1 + sp->count;
      line2 = sp->line2 + sp->count;
    }
    fprintf(fp, "%c %ld %ld %ld\n", T_ENDEDIT,
          editnumber, totalinserts, totaldeletes);
    if (ferror(fp))
    {
      sub_context_ty    *scp;

      bomb:
      scp = sub_context_new();
      sub_errno_set(scp);
      sub_var_set_charstar(scp, "File_Name", sc.historyname);
      fatal_intl(scp, i18n("write \"$filename\": $errno"));
      /* NOTREACHED */
      sub_context_delete(scp);
    }
}


/*
 * Update the history file by appending the changes given by a file.
 */

void
updatehistory(char *inputname, char *editname)
{
    FILE        *fp;    /* history file */
    char        *cp;    /* current line of data */
    POS               *pp;    /* current entry in position table */
    POS               *postable;    /* position table */
    char        *remarks;     /* remarks */
    long        newtablepos; /* position of new position table */
    long        lasteditpos; /* position of beginning of last edit */
    long        appendpos;    /* position to append new changes to */
    long        tempedit;     /* edit number just scanned from line */
    long        newedit;      /* number of new edit */
    long        edit;   /* current edit number */
    int               saveerror;    /* saved error code on errors */
    sub_context_ty  *scp;

    /*
     * Make sure that the input file contains the right version number
     * before beginning the update, and if so, then get the remarks
     * for the edit.
     */
    trace(("updatehistory()\n{\n"));
    fp = openhistoryfile(OHF_WRITE);
    if (!sc.forceupdateflag)
    {
      tempedit = findedit(inputname, fc.binary);
      if ((tempedit > 0) && (tempedit != sc.lastedit))
      {
          scp = sub_context_new();
          sub_var_set_charstar(scp, "Module", sc.modulename);
          sub_var_set_long(scp, "Number1", tempedit);
          sub_var_set_long(scp, "Number1", sc.lastedit);
          fatal_with_filename
          (
            inputname,
            scp,
              i18n("edit $number1 instead of $number2 for module \"$module\"")
          );
      }
    }
    newedit = sc.lastedit + 1;
    if (fc.verbosity)
    {
      scp = sub_context_new();
      sub_var_set_charstar(scp, "File_Name", inputname);
      sub_var_set_charstar(scp, "Module", sc.modulename);
      sub_var_set_long(scp, "Number", newedit);
      error_intl
      (
          scp,
        i18n("installing \"$filename\" as edit $number of module \"$module\"")
      );
      sub_context_delete(scp);
    }
    remarks = getremarks(sc.modulename, sc.remarkname, newedit);

    /*
     * See if we are doing a straight update, or an update after a
     * display of the differences.  If the latter, then we already have
     * done the comparison, but we need to swap the differences.
     */
    if (fc.snakelist)
      swapfiles();
    else
      comparefiles(inputname, sc.sourcename, 1L);
    postable = readpostable(fp);

    /*
     * Now go back and verify that the last edit actually exists where it
     * is supposed to be.  We will overwrite it with the changes.
     * Skip over all remark lines to position to the place to append
     * the new changes.  This will be at the location of the file line
     * for the latest edit.
     */
    lasteditpos = postable[1].p_pos;
    cp = get_a_line(fp, lasteditpos, T_BEGINEDIT, sc.historyname);
    cp = getnumber(cp, &tempedit);
    if ((cp == NULL) || (tempedit != sc.lastedit))
    {
      fatal_with_filename
      (
          sc.historyname,
          0,
          i18n("bad begin edit line for latest edit")
      );
    }

    for (;;)
    {
      int bin = 0;
      appendpos = ftell(fp);
      cp = readlinef(fp, (long *) NULL, 0, sc.historyname, &bin);
      if (cp == NULL)
      {
          fatal_with_filename
          (
                  sc.historyname,
                  0,
                  i18n("error reading remark lines for latest edit")
          );
      }
      if (bin)
          binary_fatal(sc.historyname);
      if (*cp == T_FILE)      /* the file line, all done */
          break;
      if (*cp != T_REMARK)    /* error if not remark line */
      {
          fatal_with_filename
          (
                  sc.historyname,
                  0,
                  i18n("unexpected line type in latest edit")
          );
      }
    }

    /*
     * Now write the new version of the source file under a temporary name.
     * This is to prevent problems due to quota errors.  So if this fails
     * we have done nothing bad.
     */
    breaksoff();
    writesource(inputname);

    /*
     * Now position back to the file line in the latest edit, and
     * overwrite it with the changes for the new edit.  Errors will
     * be checked for later and handled there, so we can recover from
     * disk full or quota exceeded problems.
     */
#if 0
    if (fc.verbosity > VERBOSE_DEFAULT)
      error_raw("[Appending edits to history file]");
#endif
    seekf(fp, appendpos, sc.historyname);
    writeedits(fp, sc.lastedit);

    /*
     * Now append the latest edit template to the file, and follow
     * that with the saved position table, and then append a new
     * end of file line.  a new end of file line.  Save the
     * position of the beginning of the table for updating the
     * header.  Errors are checked for later.
     */
    pp = postable;
    pp->p_pos = ftell(fp);
    pp->p_names = editname;
    addhistory(fp, remarks, newedit);
    newtablepos = ftell(fp);
    for (edit = newedit; edit >= sc.firstedit; edit--, pp++)
    {
      fprintf
      (
          fp,
          (pp->p_names ? "%c %ld %ld %s\n" : "%c %ld %ld\n"),
          T_POSITION,
          edit,
          pp->p_pos,
          pp->p_names
      );
    }
    fprintf(fp, "%c %ld\n", T_EOF, ftell(fp));

    /*
     * Force the last bit of the output to the file, then check for
     * errors.  If an error occurred during any of the above writes
     * then we will try to recover by rewriting the old position
     * table back to where it was.  This should succeed since quota
     * exceeded or disk full errors cannot occur since the position
     * table was there previously!
     */
    fflush_and_check(fp, sc.historyname);
    if (renamefiles(EXT_SOURCE))
    {
      saveerror = errno;
      errno = 0;
      seekf(fp, appendpos, sc.historyname);
      fprintf(fp, "%c 0\n", T_FILE);
      fprintf(fp, "%c %ld 0 0\n", T_ENDEDIT, sc.lastedit);
      if (ftell(fp) != sc.tablepos)
      {
          fatal_with_filename
          (
                  sc.historyname,
                  0,
                  i18n("position table moved")
          );
      }
      pp = &postable[1];
      for (edit = sc.lastedit; edit >= sc.firstedit; edit--, pp++)
      {
          fprintf
          (
            fp,
            (pp->p_names ? "%c %ld %ld %s\n" : "%c %ld %ld\n"),
            T_POSITION,
            edit,
            pp->p_pos,
            pp->p_names
          );
      }
      fprintf(fp, "%c %ld\n", T_EOF, ftell(fp));
      if (fflush(fp))
      {
          scp = sub_context_new();
          sub_errno_set(scp);
          sub_var_set_charstar(scp, "File_Name", sc.historyname);
          fatal_intl(scp, i18n("write \"$filename\": $errno"));
      }
      fclose(fp);
      scp = sub_context_new();
      sub_errno_setx(scp, saveerror);
      sub_var_set_charstar(scp, "File_Name", sc.historyname);
      error_intl(scp, i18n("write \"$filename\": $errno"));
      sub_var_set_charstar(scp, "File_Name", sc.historyname);
      fatal_intl(scp, i18n("file \"$filename\" is not damaged"));
    }

    /*
     * As the last step for inserting the new edit, rewrite the old header
     * line to update the new last edit number and the new position of the
     * beginning of the position table.  This line must be of fixed length.
     * This write should succeed since the file is not being grown in size.
     */
    seekf(fp, 0L, sc.historyname);
    fprintf(fp, HEADERFORMAT, T_HEADER, sc.firstedit, newedit, 0L, newtablepos);
    fflush_and_check(fp, sc.historyname);
    fclose_and_check(fp, sc.historyname);
    if (fc.verbosity)
    {
      scp = sub_context_new();
      sub_var_set_long(scp, "Number", newedit);
      sub_var_set_charstar(scp, "Module", sc.modulename);
      sub_var_set_long(scp, "Inserts", fc.deletes); /* deliberate */
      sub_var_set_long(scp, "Deletes", fc.inserts); /* deliberate */
      sub_var_set_long(scp, "MAtches", fc.matches);
      error_intl
      (
          scp,
i18n("edit $number of module \"$module\" saved (inserts $inserts, \
deletes $deletes, matches $matches)")
      );
      sub_context_delete(scp);
    }
    fflush(stdout);
    breakson();
    trace(("}\n"));
}

Generated by  Doxygen 1.6.0   Back to index