Logo Search packages:      
Sourcecode: fhist version File versions

sub.c

/*
 *    fhist - file history and comparison tools
 *    Copyright (C) 1998, 2000, 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 perform $ substitutions
 */

#include <ac/errno.h>
#include <ac/libintl.h>
#include <ac/limits.h>
#include <ac/string.h>
#include <ac/wctype.h>

#include <arglex.h>
#include <error.h>
#include <error_intl.h>
#include <language.h>
#include <mem.h>
#include <str.h>
#include <sub.h>
#include <sub/basename.h>
#include <sub/date.h>
#include <sub/dirname.h>
#include <sub/downcase.h>
#include <sub/errno.h>
#include <sub/expr.h>
#include <sub/ident.h>
#include <sub/length.h>
#include <sub/left.h>
#include <sub/plural.h>
#include <sub/private.h>
#include <sub/progname.h>
#include <sub/right.h>
#include <sub/upcase.h>
#include <sub/zero_pad.h>
#include <trace.h>
#include <wstr_list.h>


typedef wstring_ty *(*fp)(sub_context_ty *, wstring_list_ty *);

typedef struct table_ty table_ty;
struct table_ty
{
      const char  *name;
      fp          func;
      int         resubstitute;
      wstring_ty  *value;
      int         must_be_used      :1;
      int         append_if_unused :1;
      int         override    :1;
};

typedef struct diversion_ty diversion_ty;
struct diversion_ty
{
      long        pos;
      wstring_ty  *text;
      diversion_ty      *prev;
      int         resubstitute;
};


typedef struct collect_ty collect_ty;
struct collect_ty
{
      size_t      pos;
      size_t      size;
      wchar_t     *buf;
};

/* typedef struct sub_context_ty sub_context_ty; */
struct sub_context_ty
{
      diversion_ty      *diversion;
      table_ty    *sub_var_list;
      size_t            sub_var_size;
      size_t            sub_var_pos;
      char        *suberr;
      int         errno_sequester;
};


static void
collect_constructor(collect_ty *cp)
{
      cp->buf = 0;
      cp->size = 0;
      cp->pos = 0;
}


static void
collect_destructor(collect_ty *cp)
{
      if (cp->buf)
            mem_free(cp->buf);
      cp->buf = 0;
      cp->size = 0;
      cp->pos = 0;
}


/*
 * NAME
 *    collect
 *
 * SYNOPSIS
 *    void collect(collect_ty *, int c);
 *
 * DESCRIPTION
 *    The collect function is used to accumulate a string
 *    one character at a time.  No size limit.
 *
 * ARGUMENTS
 *    c - the character being collected
 */

static void
collect(collect_ty *cp, wchar_t c)
{
      if (cp->pos >= cp->size)
      {
            size_t      nbytes;

            cp->size += (1L << 10);
            nbytes = cp->size * sizeof(wchar_t);
            cp->buf = mem_change_size(cp->buf, nbytes);
      }
      cp->buf[cp->pos++] = c;
}


static void
collect_n(collect_ty *cp, wchar_t *s, size_t n)
{
      while (n > 0)
      {
            collect(cp, *s++);
            --n;
      }
}


/*
 * NAME
 *    collect_end
 *
 * SYNOPSIS
 *    wstring_ty *collect_end(collect_ty *);
 *
 * DESCRIPTION
 *    The collect_end function is used to fetch the string
 *    accumulated with the collect function.
 *    The bufferer for the collect function is cleared.
 *
 * RETURNS
 *    wstring_ty *; pointer to the string in dynamic memory.
 */

static wstring_ty *
collect_end(collect_ty *cp)
{
      wstring_ty  *result;

      result = wstr_n_from_wc(cp->buf, cp->pos);
      cp->pos = 0;
      return result;
}


static void
sub_context_constructor(sub_context_ty *scp)
{
      trace(("sub_context_constructor()\n{\n"));
      scp->diversion = 0;
      scp->sub_var_list = 0;
      scp->sub_var_size = 0;
      scp->sub_var_pos = 0;
      scp->suberr = 0;
      scp->errno_sequester = 0;
      trace(("}\n"));
}


sub_context_ty *
sub_context_new(void)
{
      sub_context_ty    *scp;
      int         hang_on_a_second;

      hang_on_a_second = errno;
      scp = mem_alloc(sizeof(sub_context_ty));
      errno = hang_on_a_second;
      sub_context_constructor(scp);
      return scp;
}


static void
sub_context_destructor(sub_context_ty *scp)
{
      size_t            j;

      for (j = 0; j < scp->sub_var_pos; ++j)
            wstr_free(scp->sub_var_list[j].value);
      if (scp->sub_var_list)
            mem_free(scp->sub_var_list);

      scp->diversion = 0;
      scp->sub_var_list = 0;
      scp->sub_var_size = 0;
      scp->sub_var_pos = 0;
      scp->suberr = 0;
      scp->errno_sequester = 0;
}


void
sub_context_delete(sub_context_ty *scp)
{
      sub_context_destructor(scp);
      mem_free(scp);
}


void
sub_context_error_set(sub_context_ty *scp, char *s)
{
      scp->suberr = s;
}



static table_ty   table[] =
{
      { "Basename",                 sub_basename,                 },
      { "DAte",               sub_date,               },
      { "Dirname",                  sub_dirname,                  },
      { "DownCase",                 sub_downcase,                 },
      { "ERrno",              sub_errno,              },
      { "EXpression",               sub_expression,               },
      /* File_Name                                          */
      /* Guess                                        */
      { "IDentifier",               sub_identifier,               },
      { "LEFt",               sub_left,               },
      { "LENgth",             sub_length,             },
      /* Name                                               */
      /* Number                                       */
      { "PLural",             sub_plural,             },
      { "PROGname",                 sub_progname,                 },
      { "RIght",              sub_right,              },
      { "UpCase",             sub_upcase,             },
      /* Value                                        */
      { "Zero_Pad",                 sub_zero_pad,                 },
};


/*
 * NAME
 *    sub_diversion
 *
 * SYNOPSIS
 *    void sub_diversion(wstring_ty *s, int resub);
 *
 * DESCRIPTION
 *    The sub_diversion function is used to divert input
 *    to a string, until that string is exhausted.
 *    When the string is exhausted, input will resume
 *    from the previous string.
 *
 * ARGUMENTS
 *    s - string to take as input
 */

static void
sub_diversion(sub_context_ty *scp, wstring_ty *s, int resubstitute)
{
      diversion_ty      *dp;

      trace(("sub_diversion(s = %8.8lX, resub = %d)\n{\n",
            (long)s, resubstitute));
      dp = (diversion_ty *)mem_alloc(sizeof(diversion_ty));
      dp->text = wstr_copy(s);
      dp->pos = 0;
      dp->resubstitute = resubstitute;
      dp->prev = scp->diversion;
      scp->diversion = dp;
      trace(("}\n"));
}


/*
 * NAME
 *    sub_diversion_close
 *
 * SYNOPSIS
 *    void sub_diversion_close(void);
 *
 * DESCRIPTION
 *    The sub_diversion_close function is used to
 *    release a diversion when it has been exhausted.
 */

static void
sub_diversion_close(sub_context_ty *scp)
{
      diversion_ty      *dp;

      trace(("sub_diversion_close()\n{\n"));
      assert(scp->diversion);
      dp = scp->diversion;
      scp->diversion = dp->prev;
      wstr_free(dp->text);
      mem_free((char *)dp);
      trace(("}\n"));
}


/*
 * NAME
 *    execute
 *
 * SYNOPSIS
 *    void execute(wstring_list_ty *args);
 *
 * DESCRIPTION
 *    The execute function is used to perform the substitution
 *    described by the argument list.
 *
 * ARGUMENTS
 *    args - the name and arguments of the substitution
 */

static void
execute(sub_context_ty *scp, wstring_list_ty *arg)
{
      string_ty   *cmd;
      wstring_ty  *s;
      table_ty    *hit[20];
      int         nhits;
      table_ty    *tp;
      long        j;

      trace(("execute(scp = %08lX, arg = %08lX)\n{\n",
            (long)scp, (long)arg));
      if (arg->nitems == 0)
      {
            sub_context_ty    *scp2;

            scp2 = sub_context_new();
            fatal_intl(scp2, i18n("empty $${} substitution"));
            /* NOTREACHED */
            sub_context_delete(scp2);
      }
      
      /*
       * scan the variables
       */
      nhits = 0;
      cmd = wstr_to_str(arg->item[0]);
      trace_string(cmd->str_text);
      for (j = 0; j < scp->sub_var_pos; ++j)
      {
            tp = &scp->sub_var_list[j];
            trace(("tp = %08lX\n", (long)tp));
            trace_string(tp->name);
            if (arglex_compare(tp->name, cmd->str_text))
            {
                  if (nhits < SIZEOF(hit))
                        hit[nhits++] = tp;
            }
      }

      /*
       * scan the functions
       */
      for (tp = table; tp < ENDOF(table); ++tp)
      {
            if (arglex_compare(tp->name, cmd->str_text))
            {
                  if (tp->override)
                        goto override;
                  if (nhits < SIZEOF(hit))
                        hit[nhits++] = tp;
            }
      }
      str_free(cmd);

      /*
       * figure what to do
       */
      switch (nhits)
      {
      case 0:
            scp->suberr = i18n("unknown substitution name");
            s = 0;
            break;

      case 1:
            tp = hit[0];
            override:
            if (tp->value)
            {
                  /*
                   * flag that the variable has been used
                   */
                  tp->must_be_used = 0;
                  s = wstr_copy(tp->value);
            }
            else
            {
                  wstr_free(arg->item[0]);
                  arg->item[0] = wstr_from_c(tp->name);
                  s = tp->func(scp, arg);
            }
            break;

      default:
            scp->suberr = i18n("ambiguous substitution name");
            s = 0;
            break;
      }

      /*
       * deal with the result
       */
      if (s)
      {
            sub_diversion(scp, s, tp->resubstitute);
            wstr_free(s);
      }
      else
      {
            wstring_ty  *s2;
            string_ty   *s3;
            sub_context_ty    *scp2;
            char        *the_error;

            assert(scp->suberr);
            s2 = wstring_list_to_wstring(arg, 0, arg->nitems, (char *)0);
            s3 = wstr_to_str(s2);
            wstr_free(s2);
            the_error = scp->suberr ? scp->suberr : "this is a bug";
            scp2 = sub_context_new();
            sub_var_set_string(scp2, "Name", s3);
            sub_var_set_charstar(scp2, "Message", gettext(the_error));
            fatal_intl(scp2, i18n("substitution $${$name} failed: $message"));
            /* NOTREACHED */
            sub_context_delete(scp2);
            str_free(s3);
      }
      trace(("}\n"));
}


/*
 * NAME
 *    sub_getc_meta
 *
 * SYNOPSIS
 *    void sub_getc_meta(void);
 *
 * DESCRIPTION
 *    The sub_getc_meta function is used to get a character from
 *    the current input string.  When the current string is exhaused,
 *    the previous string is resumed.
 *
 * RETURNS
 *    int - the chacater, or NUL to indicate end of input
 */

static wchar_t
sub_getc_meta(sub_context_ty *scp)
{
      wchar_t           result;
      diversion_ty      *dp;

      trace(("sub_getc_meta()\n{\n"));
      dp = scp->diversion;
      if (!dp)
            result = 0;
      else if (dp->pos >= dp->text->wstr_length)
            result = 0;
      else
            result = dp->text->wstr_text[dp->pos++];
#ifdef DEBUG
      if (iswprint(result) && result >= CHAR_MIN && result <= CHAR_MAX)
            trace(("return '%c';\n", (char)result));
      else
            trace(("return %4.4lX;\n", (long)result));
#endif
      trace(("}\n"));
      return result;
}


/*
 * NAME
 *    sub_getc_meta_undo
 *
 * SYNOPSIS
 *    void sub_getc_meta_undo(int c);
 *
 * DESCRIPTION
 *    The sub_getc_meta_undo function is used to give back
 *    a character output by sub_getc_meta.
 *
 * ARGUMENTS
 *    c - character being given back
 *
 * CAVEAT
 *    Only push back what was read.
 */

static void
sub_getc_meta_undo(sub_context_ty *scp, wchar_t c)
{
      diversion_ty      *dp;

      trace(("sub_getc_meta_undo(%ld)\n{\n", (long)c));
#ifdef DEBUG
      if (iswprint(c) && c >= CHAR_MIN && c <= CHAR_MAX)
            trace(("c = '%c'\n", (char)c));
#endif
      dp = scp->diversion;
      assert(dp);
      if (!c)
      {
            assert(dp->pos == dp->text->wstr_length);
      }
      else
      {
            assert(dp->pos >= 1);
            dp->pos--;
            assert(c == dp->text->wstr_text[dp->pos]);
      }
      trace(("}\n"));
}


/*
 * NAME
 *    dollar
 *
 * SYNOPSIS
 *    wchar_t dollar(void);
 *
 * DESCRIPTION
 *    The dollar function is used to perform dollar ($) substitutions.
 *    On entry, the $ is expected to have been consumed.
 *
 *    The substitution is usually achieved as a side-effect,
 *    by using the sub_diversion function.
 *
 * RETURNS
 *    wchar_t     a character to deliver as output,
 *          or NUL if none.
 */

static wchar_t sub_getc(sub_context_ty *); /* forward */

static wchar_t
dollar(sub_context_ty *scp)
{
      wstring_list_ty   arg;
      int         result;
      wchar_t           c;
      wstring_ty  *s;
      wchar_t           quoted;
      collect_ty  tmp;

      trace(("dollar()\n{\n"));
      collect_constructor(&tmp);
      wstring_list_zero(&arg);
      result = 0;
      c = sub_getc_meta(scp);
      switch (c)
      {
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9': 
            for (;;)
            {
                  collect(&tmp, c);
                  c = sub_getc_meta(scp);
                  switch (c)
                  {
                  case '0': case '1': case '2': case '3': case '4':
                  case '5': case '6': case '7': case '8': case '9':
                        continue;

                  default:
                        sub_getc_meta_undo(scp, c);
                        break;
                  }
                  break;
            }
            s = collect_end(&tmp);
            trace(("push arg\n"));
            wstring_list_append(&arg, s);
            wstr_free(s);
            execute(scp, &arg);
            wstring_list_free(&arg);
            break;

      case 'a': case 'b': case 'c': case 'd': case 'e':
      case 'f': case 'g': case 'h': case 'i': case 'j':
      case 'k': case 'l': case 'm': case 'n': case 'o':
      case 'p': case 'q': case 'r': case 's': case 't':
      case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': 
      case 'A': case 'B': case 'C': case 'D': case 'E':
      case 'F': case 'G': case 'H': case 'I': case 'J':
      case 'K': case 'L': case 'M': case 'N': case 'O':
      case 'P': case 'Q': case 'R': case 'S': case 'T':
      case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': 
            for (;;)
            {
                  collect(&tmp, c);
                  c = sub_getc_meta(scp);
                  switch (c)
                  {
                  case 'a': case 'b': case 'c': case 'd': case 'e':
                  case 'f': case 'g': case 'h': case 'i': case 'j':
                  case 'k': case 'l': case 'm': case 'n': case 'o':
                  case 'p': case 'q': case 'r': case 's': case 't':
                  case 'u': case 'v': case 'w': case 'x': case 'y':
                  case 'z': 
                  case 'A': case 'B': case 'C': case 'D': case 'E':
                  case 'F': case 'G': case 'H': case 'I': case 'J':
                  case 'K': case 'L': case 'M': case 'N': case 'O':
                  case 'P': case 'Q': case 'R': case 'S': case 'T':
                  case 'U': case 'V': case 'W': case 'X': case 'Y':
                  case 'Z': 
                  case '0': case '1': case '2': case '3': case '4':
                  case '5': case '6': case '7': case '8': case '9': 
                  case '_': case '-':
                        continue;

                  default:
                        sub_getc_meta_undo(scp, c);
                        break;
                  }
                  break;
            }
            s = collect_end(&tmp);
            trace(("push arg\n"));
            wstring_list_append(&arg, s);
            wstr_free(s);
            execute(scp, &arg);
            wstring_list_free(&arg);
            break;

      case '{':
            c = sub_getc(scp);
            for (;;)
            {
                  /*
                   * look for terminator
                   */
                  if (c == '}')
                        break;

                  /*
                   * watch out for unterminated substitutions
                   */
                  if (!c)
                  {
                        sub_context_ty    *scp2;

                        scp2 = sub_context_new();
                        fatal_intl(scp2, i18n("unterminated $${} substitution"));
                        /* NOTREACHED */
                        sub_context_delete(scp2);
                        break;
                  }

                  /*
                   * skip white space separating the arguments
                   */
                  if (iswspace(c))
                  {
                        c = sub_getc(scp);
                        continue;
                  }

                  /*
                   * collect the argument
                   *    any run of non-white-space characters
                   */
                  quoted = 0;
                  for (;;)
                  {
                        if (!c)
                        {
                              if (quoted)
                              {
                                    sub_context_ty    *scp2;

                                    scp2 = sub_context_new();
                                    fatal_intl
                                    (
                                          scp2,
                                    i18n("unterminated $${} quotes")
                                    );
                                    /* NOTREACHED */
                                    sub_context_delete(scp2);
                              }
                              break;
                        }
                        if (!quoted && (iswspace(c) || c == '}'))
                              break;
                        if (c == quoted)
                        {
                              assert(quoted);
                              quoted = 0;
                        }
                        else if (c == '\'')
                        {
                              assert(!quoted);
                              quoted = c;
                        }
                        else if (c == '\\')
                        {
                              c = sub_getc(scp);
                              if (!c)
                              {
                                    sub_context_ty    *scp2;

                                    scp2 = sub_context_new();
                                    fatal_intl
                                    (
                                          scp2,
                                 i18n("unterminated $${} \\ sequence")
                                    );
                                    /* NOTREACHED */
                                    sub_context_delete(scp2);
                              }
                              collect(&tmp, c);
                        }
                        else
                              collect(&tmp, c);
                        c = sub_getc(scp);
                  }
                  s = collect_end(&tmp);
                  trace(("push arg\n"));
                  wstring_list_append(&arg, s);
                  wstr_free(s);
            }
            execute(scp, &arg);
            wstring_list_free(&arg);
            break;

      case '$':
            result = '$';
            break;

      default:
            sub_getc_meta_undo(scp, c);
            result = '$';
            break;
      }
      collect_destructor(&tmp);
#ifdef DEBUG
      if (iswprint(result) && result >= CHAR_MIN && result <= CHAR_MAX)
            trace(("return '%c';\n", (char)result));
      else
            trace(("return %4.4lX;\n", (long)result));
#endif
      trace(("}\n"));
      return result;
}


/*
 * NAME
 *    sub_getc
 *
 * SYNOPSIS
 *    wchar_t sub_getc(void);
 *
 * DESCRIPTION
 *    The sub_getc function is used to get a character from
 *    the substitution stream.  This is used both for the final output
 *    and for fetching arguments to dollar ($) substitutions.
 *
 * RETURNS
 *    wchar_t     a character from the stream,
 *          or NUL to indicate end of input.
 */

static wchar_t
sub_getc(sub_context_ty *scp)
{
      wchar_t           c;

      trace(("sub_getc()\n{\n"));
      for (;;)
      {
            c = sub_getc_meta(scp);
            switch (c)
            {
            default:
                  break;

            case 0:
                  if (!scp->diversion)
                        break;
                  sub_diversion_close(scp);
                  continue;

            case '$':
                  if (!scp->diversion->resubstitute)
                        break;
                  c = dollar(scp);
                  if (!c)
                        continue;
                  break;
            }
            break;
      }
#ifdef DEBUG
      if (iswprint(c) && c >= CHAR_MIN && c <= CHAR_MAX)
            trace(("return '%c';\n", (char)c));
      else
            trace(("return %4.4lX;\n", (long)c));
#endif
      trace(("}\n"));
      return c;
}


static wstring_ty *
subst(sub_context_ty *scp, wstring_ty *s)
{
      collect_ty  buf;
      wchar_t           c;
      wstring_ty  *result;
      table_ty    *tp;
      table_ty    *the_end;
      int         error_count;

      trace(("subst(scp = %08lX, s = %8.8lX)\n{\n",
            (long)scp, (long)s));
      collect_constructor(&buf);
      sub_diversion(scp, s, 1);
      for (;;)
      {
            /*
             * get the next character
             */
            c = sub_getc(scp);
            if (!c)
                  break;

            /*
             * save the character
             */
            collect(&buf, c);
      }

      /*
       * find any unused variables marked "append if unused"
       */
      the_end = scp->sub_var_list + scp->sub_var_pos;
      for (tp = scp->sub_var_list; tp < the_end; ++tp)
      {
            if (!tp->append_if_unused)
                  continue;
            if (!tp->must_be_used)
                  continue;
            assert(tp->value);

            /*
             * flag that the variable has been used
             */
            tp->must_be_used = 0;
            if (!tp->value->wstr_length)
                  continue;

            /*
             * append to the buffer, separated by a space
             */
            collect(&buf, (wchar_t)' ');
            collect_n(&buf, tp->value->wstr_text, tp->value->wstr_length);
      }

      /*
       * find any unused variables
       * and complain about them
       */
      error_count = 0;
      for (tp = scp->sub_var_list; tp < the_end; ++tp)
      {
            sub_context_ty    *scp2;
            string_ty   *tmp;

            if (!tp->must_be_used)
                  continue;

            /*
             * Make sure the variables of this message are optional,
             * to avoid infinite loops if there is a mistake in the
             * translation string.
             */
            scp2 = sub_context_new();
            tmp = wstr_to_str(s);
            sub_var_set_string(scp2, "Message", tmp);
            sub_var_optional(scp2, "Message");
            sub_var_set_format(scp2, "Name", "$%s", tp->name);
            sub_var_optional(scp2, "Name");
            error_intl
            (
                  scp2,
                  i18n("in substitution \"$message\" variable \"$name\" unused")
            );
            sub_context_delete(scp2);
            str_free(tmp);
            ++error_count;
      }
      if (error_count > 0)
      {
            string_ty   *tmp;
            sub_context_ty    *scp2;

            /*
             * Make sure the variables of this message are optional,
             * to avoid infinite loops if there is a mistake in the
             * translation string.
             */
            scp2 = sub_context_new();
            tmp = wstr_to_str(s);
            sub_var_set_string(scp2, "Message", tmp);
            sub_var_optional(scp2, "Message");
            sub_var_set_format(scp2, "Number", "%d", error_count);
            sub_var_optional(scp2, "Number");
            fatal_intl
            (
                  scp2,
                  i18n("in substitution \"$message\" found unused variables")
            );
            /* NOTREACHED */
            str_free(tmp);
            sub_context_delete(scp2);
      }

      /*
       * clear the slate, ready for the next run
       */
      sub_var_clear(scp);
      result = collect_end(&buf);
      collect_destructor(&buf);
      trace(("return %8.8lX;\n", (long)result));
      trace(("}\n"));
      return result;
}


wstring_ty *
subst_intl_wide(sub_context_ty *scp, const char *msg)
{
      char        *tmp;
      wstring_ty  *s;
      wstring_ty  *result;

      trace(("subst_intl_wide(scp = %08lX, msg = \"%s\")\n{\n",
            (long)scp, msg));
      language_human();
      tmp = gettext(msg);
      language_C();
#ifdef DEBUG
#ifdef HAVE_GETTEXT
      if (tmp == msg)
            error_raw("warning: message \"%s\" has no translation", msg);
#endif /* HAVE_GETTEXT */
#endif /* DEBUG */
      s = wstr_from_c(tmp);
      result = subst(scp, s);
      wstr_free(s);
      trace(("return %8.8lX;\n", (long)result));
      trace(("}\n"));
      return result;
}


string_ty *
subst_intl(sub_context_ty *scp, const char *s)
{
      wstring_ty  *result_wide;
      string_ty   *result;

      trace(("subst_intl(scp = %08lX, s = \"%s\")\n{\n",
            (long)scp, s));
      result_wide = subst_intl_wide(scp, s);
      result = wstr_to_str(result_wide);
      wstr_free(result_wide);
      trace(("return \"%s\";\n", result->str_text));
      trace(("}\n"));
      return result;
}


/*
 * NAME
 *    substitute
 *
 * SYNOPSIS
 *    string_ty *substitute(string_ty *s);
 *
 * DESCRIPTION
 *    The substitute function is used to perform substitutions on
 *    strings.  Usually command strings, but not always.
 *
 *    The format of substitutions, and the commonly available
 *    substitutions, are described in aesub(5).
 *
 * ARGUMENTS
 *    cp    - the aegis change involved with the command
 *            This may never be NULL.
 *    s     - the string to be substituted.
 *
 * RETURNS
 *    string_ty *; pointer to string in dynamic memory
 */

string_ty *
substitute(sub_context_ty *scp, string_ty *s)
{
      wstring_ty  *ws;
      wstring_ty  *result_wide;
      string_ty   *result;

      assert(s);
      trace(("substitute(scp = %08lX, s = \"%s\")\n{\n",
            (long)scp, s->str_text));
      ws = str_to_wstr(s);
      result_wide = subst(scp, ws);
      wstr_free(ws);
      result = wstr_to_str(result_wide);
      wstr_free(result_wide);
      trace(("return \"%s\";\n", result->str_text));
      trace(("}\n"));
      return result;
}


/*
 * NAME
 *    sub_var_clear
 *
 * SYNOPSIS
 *    void sub_var_clear(void);
 *
 * DESCRIPTION
 *    The sub_var_clear function is used to clear all of
 *    the substitution variables.  Not usually needed manually,
 *    as this is done automatically at the end of every substitute().
 */

void
sub_var_clear(sub_context_ty *scp)
{
      size_t            j;

      trace(("sub_var_clear()\n{\n"));
      for (j = 0; j < scp->sub_var_pos; ++j)
            wstr_free(scp->sub_var_list[j].value);
      scp->sub_var_pos = 0;
      scp->errno_sequester = 0;
      trace(("}\n"));
}


/*
 * NAME
 *    sub_var_set
 *
 * SYNOPSIS
 *    void sub_var_set(char *name, char *fmt, ...);
 *
 * DESCRIPTION
 *    The sub_var_set function is used to set the value of a
 *    substitution variable.  These variables are command specific,
 *    as opposed to the functions which are always present.
 *    The user documentation does NOT make this distinction by
 *    using the names "variable" and "function", they are always referred
 *    to as "substitutions".
 *
 * ARGUMENTS
 *    name  - the name of the variable
 *    fmt,...     - a format string and arguments to construct the value.
 *            Handed to str_vformat to make a (string_ty *) out of it.
 *
 * CAVEAT
 *    remains in scope until the next invokation of sub_var_clear,
 *    or until the end of the next invokation of substitute.
 */

void
sub_var_set_string(sub_context_ty *scp, const char *name, string_ty *value)
{
      table_ty    *svp;

      trace(("sub_var_set_string(scp = %08lX, name = \"%s\", value = \"%s\")\n{\n",
            (long)scp, name, value->str_text));

      if (scp->sub_var_pos >= scp->sub_var_size)
      {
            size_t            nbytes;

            scp->sub_var_size += 10;
            nbytes = scp->sub_var_size * sizeof(scp->sub_var_list[0]);
            scp->sub_var_list = mem_change_size(scp->sub_var_list, nbytes);
      }
      svp = &scp->sub_var_list[scp->sub_var_pos++];
      svp->name = name;
      svp->func = 0;
      svp->value = str_to_wstr(value);
      svp->must_be_used = 1;
      svp->append_if_unused = 0;
      svp->override = 0;
      svp->resubstitute = !svp->must_be_used;
      trace(("}\n"));
}


void
sub_var_set_format(sub_context_ty *scp, const char *name, const char *fmt, ...)
{
      va_list           ap;
      string_ty   *s;

      trace(("sub_var_set_format(scp = %08lX, name = \"%s\")\n{\n",
            (long)scp, name));
      va_start(ap, fmt);
      s = str_vformat(fmt, ap);
      va_end(ap);
      sub_var_set_string(scp, name, s);
      str_free(s);
      trace(("}\n"));
}


void
sub_var_set_long(sub_context_ty *scp, const char *name, long value)
{
      sub_var_set_format(scp, name, "%ld", value);
}


void
sub_var_set_charstar(sub_context_ty *scp, const char *name, const char *value)
{
      string_ty   *s;

      trace(("sub_var_set_charstar(scp = %08lX, name = \"%s\")\n{\n",
            (long)scp, name));
      s = str_from_c(value);
      sub_var_set_string(scp, name, s);
      str_free(s);
      trace(("}\n"));
}


void
sub_var_resubstitute(sub_context_ty *scp, const char *name)
{
      table_ty    *the_end;
      table_ty    *svp;

      the_end = scp->sub_var_list + scp->sub_var_pos;
      for (svp = scp->sub_var_list; svp < the_end; ++svp)
            if (!strcmp(svp->name, name))
                  break;
      assert(svp < the_end);
      if (svp < the_end)
            svp->resubstitute = 1;
}


void
sub_var_override(sub_context_ty *scp, const char *name)
{
      table_ty    *the_end;
      table_ty    *svp;

      the_end = scp->sub_var_list + scp->sub_var_pos;
      for (svp = scp->sub_var_list; svp < the_end; ++svp)
            if (!strcmp(svp->name, name))
                  break;
      assert(svp < the_end);
      if (svp < the_end)
            svp->override = 1;
}


void
sub_var_optional(sub_context_ty *scp, const char *name)
{
      table_ty    *the_end;
      table_ty    *svp;

      the_end = scp->sub_var_list + scp->sub_var_pos;
      for (svp = scp->sub_var_list; svp < the_end; ++svp)
            if (!strcmp(svp->name, name))
                  break;
      assert(svp < the_end);
      if (svp < the_end)
            svp->must_be_used = 0;
}


void
sub_var_append_if_unused(sub_context_ty *scp, const char *name)
{
      table_ty    *the_end;
      table_ty    *svp;

      the_end = scp->sub_var_list + scp->sub_var_pos;
      for (svp = scp->sub_var_list; svp < the_end; ++svp)
            if (!strcmp(svp->name, name))
                  break;
      assert(svp < the_end);
      if (svp < the_end)
      {
            svp->must_be_used = 1;
            svp->append_if_unused = 1;
      }
}


void
sub_errno_setx(sub_context_ty *scp, int x)
{
      scp->errno_sequester = x;
}


void
sub_errno_set(sub_context_ty *scp)
{
      sub_errno_setx(scp, errno);
}


int
sub_context_errno_get(sub_context_ty *scp)
{
      if (scp->errno_sequester == 0)
            scp->errno_sequester = errno;
      return scp->errno_sequester;
}     

Generated by  Doxygen 1.6.0   Back to index