Beispiel #1
0
/* Create a temporary file for diversion DIVNUM open for reading and
   writing in a secure temp directory.  The file will be automatically
   closed and deleted on a fatal signal.  The file can be closed and
   reopened with m4_tmpclose and m4_tmpopen, or moved with
   m4_tmprename; when finally done with the file, close it with
   m4_tmpremove.  Exits on failure, so the return value is always an
   open file.  */
static FILE *
m4_tmpfile (int divnum)
{
  const char *name;
  FILE *file;

  if (output_temp_dir == NULL)
    {
      output_temp_dir = create_temp_dir ("m4-", NULL, true);
      if (output_temp_dir == NULL)
        M4ERROR ((EXIT_FAILURE, errno,
                  "cannot create temporary file for diversion"));
      atexit (cleanup_tmpfile);
    }
  name = m4_tmpname (divnum);
  register_temp_file (output_temp_dir, name);
  file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
  if (file == NULL)
    {
      unregister_temp_file (output_temp_dir, name);
      M4ERROR ((EXIT_FAILURE, errno,
                "cannot create temporary file for diversion"));
    }
//  else if (set_cloexec_flag (fileno (file), true) != 0)
//    M4ERROR ((warning_status, errno,
//              "Warning: cannot protect diversion across forks"));
  return file;
}
Beispiel #2
0
static void
pop_input (void)
{
  input_block *tmp = isp->prev;

  switch (isp->type)
    {
    case INPUT_STRING:
    case INPUT_MACRO:
      break;

    case INPUT_FILE:
      if (debug_level & DEBUG_TRACE_INPUT)
        {
          if (tmp)
            DEBUG_MESSAGE2 ("input reverted to %s, line %d",
                            tmp->file, tmp->line);
          else
            DEBUG_MESSAGE ("input exhausted");
        }

      if (ferror (isp->u.u_f.fp))
        {
          M4ERROR ((warning_status, 0, "read error"));
          if (isp->u.u_f.close)
            fclose (isp->u.u_f.fp);
          retcode = EXIT_FAILURE;
        }
      else if (isp->u.u_f.close && fclose (isp->u.u_f.fp) == EOF)
        {
          M4ERROR ((warning_status, errno, "error reading file"));
          retcode = EXIT_FAILURE;
        }
      start_of_input_line = isp->u.u_f.advance;
      output_current_line = -1;
      break;

    default:
      M4ERROR ((warning_status, 0,
                "INTERNAL ERROR: input stack botch in pop_input ()"));
      abort ();
    }
  obstack_free (current_input, isp);
  next = NULL; /* might be set in push_string_init () */

  isp = tmp;
  input_change = true;
}
Beispiel #3
0
void
freeze_diversions (FILE *file)
{
  int saved_number;
  int last_inserted;
  gl_oset_iterator_t iter;
  const void *elt;

  saved_number = current_diversion;
  last_inserted = 0;
  make_diversion (0);
  output_file = file; /* kludge in the frozen file */

  iter = gl_oset_iterator (diversion_table);
  while (gl_oset_iterator_next (&iter, &elt))
    {
      m4_diversion *diversion = (m4_diversion *) elt;
      if (diversion->size || diversion->used)
        {
          if (diversion->size)
            xfprintf (file, "D%d,%d\n", diversion->divnum, diversion->used);
          else
            {
              struct stat file_stat;
              diversion->u.file = m4_tmpopen (diversion->divnum, true);
              if (fstat (fileno (diversion->u.file), &file_stat) < 0)
                M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion"));
              if (file_stat.st_size < 0
                  || (file_stat.st_size + 0UL
                      != (unsigned long int) file_stat.st_size))
                M4ERROR ((EXIT_FAILURE, 0, "diversion too large"));
              xfprintf (file, "D%d,%lu\n", diversion->divnum,
                        (unsigned long int) file_stat.st_size);
            }

          insert_diversion_helper (diversion);
          putc ('\n', file);

          last_inserted = diversion->divnum;
        }
    }
  gl_oset_iterator_free (&iter);

  /* Save the active diversion number, if not already.  */

  if (saved_number != last_inserted)
    xfprintf (file, "D%d,0\n\n", saved_number);
}
Beispiel #4
0
/* Clean up any temporary directory.  Designed for use as an atexit
   handler, where it is not safe to call exit() recursively; so this
   calls _exit if a problem is encountered.  */
static void
cleanup_tmpfile (void)
{
  /* Close any open diversions.  */
  bool fail = false;

  if (diversion_table)
    {
      const void *elt;
      gl_oset_iterator_t iter = gl_oset_iterator (diversion_table);
      while (gl_oset_iterator_next (&iter, &elt))
        {
          m4_diversion *diversion = (m4_diversion *) elt;
          if (!diversion->size && diversion->u.file
              && close_stream_temp (diversion->u.file) != 0)
            {
              M4ERROR ((0, errno,
                        "cannot clean temporary file for diversion"));
              fail = true;
            }
        }
      gl_oset_iterator_free (&iter);
    }

  /* Clean up the temporary directory.  */
  if (cleanup_temp_dir (output_temp_dir) != 0)
    fail = true;
  if (fail)
    _exit (exit_failure);
}
Beispiel #5
0
static eval_error
cmp_term (eval_token et, eval_t *v1)
{
  eval_token op;
  eval_t v2;
  eval_error er;

  if ((er = shift_term (et, v1)) != NO_ERROR)
    return er;

  while ((op = eval_lex (&v2)) == EQ || op == NOTEQ
	 || op == GT || op == GTEQ
	 || op == LS || op == LSEQ)
    {

      et = eval_lex (&v2);
      if (et == ERROR)
	return UNKNOWN_INPUT;

      if ((er = shift_term (et, &v2)) != NO_ERROR)
	return er;

      switch (op)
	{
	case EQ:
	  *v1 = *v1 == v2;
	  break;

	case NOTEQ:
	  *v1 = *v1 != v2;
	  break;

	case GT:
	  *v1 = *v1 > v2;
	  break;

	case GTEQ:
	  *v1 = *v1 >= v2;
	  break;

	case LS:
	  *v1 = *v1 < v2;
	  break;

	case LSEQ:
	  *v1 = *v1 <= v2;
	  break;

	default:
	  M4ERROR ((warning_status, 0,
		    "INTERNAL ERROR: Bad comparison operator in cmp_term ()"));
	  abort ();
	}
    }
  if (op == ERROR)
    return UNKNOWN_INPUT;

  eval_undo ();
  return NO_ERROR;
}
Beispiel #6
0
static void
pop_input (void)
{
  input_block *tmp = isp->prev;

  switch (isp->type)
    {
    case INPUT_STRING:
    case INPUT_MACRO:
      break;

    case INPUT_FILE:
      if (debug_level & DEBUG_TRACE_INPUT)
	DEBUG_MESSAGE2 ("input reverted to %s, line %d",
			isp->u.u_f.name, isp->u.u_f.lineno);

      fclose (isp->u.u_f.file);
      current_file = isp->u.u_f.name;
      current_line = isp->u.u_f.lineno;
      output_current_line = isp->u.u_f.out_lineno;
      start_of_input_line = isp->u.u_f.advance_line;
      if (tmp != NULL)
	output_current_line = -1;
      break;

    default:
      M4ERROR ((warning_status, 0,
		"INTERNAL ERROR: Input stack botch in pop_input ()"));
      abort ();
    }
  obstack_free (current_input, isp);
  next = NULL;			/* might be set in push_string_init () */

  isp = tmp;
}
Beispiel #7
0
boolean
evaluate (const char *expr, eval_t *val)
{
  eval_token et;
  eval_error err;

  eval_init_lex (expr);
  et = eval_lex (val);
  err = logical_or_term (et, val);

  if (err == NO_ERROR && *eval_text != '\0')
    err = EXCESS_INPUT;
    
  switch (err)
    {
    case NO_ERROR:
      break;

    case MISSING_RIGHT:
      M4ERROR ((warning_status, 0,
		"Bad expression in eval (missing right parenthesis): %s",
		expr));
      break;

    case SYNTAX_ERROR:
      M4ERROR ((warning_status, 0,
		"Bad expression in eval: %s", expr));
      break;

    case UNKNOWN_INPUT:
      M4ERROR ((warning_status, 0,
		"Bad expression in eval (bad input): %s", expr));
      break;

    case EXCESS_INPUT:
      M4ERROR ((warning_status, 0,
		"Bad expression in eval (excess input): %s", expr));
      break;

    case DIVIDE_ZERO:
      M4ERROR ((warning_status, 0,
		"Divide by zero in eval: %s", expr));
      break;

    case MODULO_ZERO:
      M4ERROR ((warning_status, 0,
		"Modulo by zero in eval: %s", expr));
      break;

    default:
      M4ERROR ((warning_status, 0,
		"INTERNAL ERROR: Bad error code in evaluate ()"));
      abort ();
    }

  return (boolean) (err != NO_ERROR);
}
Beispiel #8
0
static void
init_macro_token (token_data *td)
{
  if (isp->type != INPUT_MACRO)
    {
      M4ERROR ((warning_status, 0,
                "INTERNAL ERROR: bad call to init_macro_token ()"));
      abort ();
    }

  TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
  TOKEN_DATA_FUNC (td) = isp->u.func;
}
Beispiel #9
0
static eval_error
mult_term (eval_token et, eval_t *v1)
{
  eval_token op;
  eval_t v2;
  eval_error er;

  if ((er = exp_term (et, v1)) != NO_ERROR)
    return er;

  while ((op = eval_lex (&v2)) == TIMES || op == DIVIDE || op == MODULO)
    {
      et = eval_lex (&v2);
      if (et == ERROR)
	return UNKNOWN_INPUT;

      if ((er = exp_term (et, &v2)) != NO_ERROR)
	return er;

      switch (op)
	{
	case TIMES:
	  *v1 = *v1 * v2;
	  break;

	case DIVIDE:
	  if (v2 == 0)
	    return DIVIDE_ZERO;
	  else
	    *v1 = *v1 / v2;
	  break;

	case MODULO:
	  if (v2 == 0)
	    return MODULO_ZERO;
	  else
	    *v1 = *v1 % v2;
	  break;

	default:
	  M4ERROR ((warning_status, 0,
		    "INTERNAL ERROR: Bad operator in mult_term ()"));
	  abort ();
	}
    }
  if (op == ERROR)
    return UNKNOWN_INPUT;

  eval_undo ();
  return NO_ERROR;
}
Beispiel #10
0
static int
next_char (void)
{
  register int ch;

  if (start_of_input_line)
    {
      start_of_input_line = FALSE;
      current_line++;
    }

  while (1)
    {
      if (isp == NULL)
	return CHAR_EOF;

      switch (isp->type)
	{
	case INPUT_STRING:
	  ch = *isp->u.u_s.string++;
	  if (ch != '\0')
	    return ch;
	  break;

	case INPUT_FILE:
	  ch = getc (isp->u.u_f.file);
	  if (ch != EOF)
	    {
	      if (ch == '\n')
		start_of_input_line = TRUE;
	      return ch;
	    }
	  break;

	case INPUT_MACRO:
	  pop_input ();		/* INPUT_MACRO input sources has only one
				   token */
	  return CHAR_MACRO;

	default:
	  M4ERROR ((warning_status, 0,
		    "INTERNAL ERROR: Input stack botch in next_char ()"));
	  abort ();
	}

      /* End of input source --- pop one level.  */
      pop_input ();
    }
}
Beispiel #11
0
struct obstack *
push_string_init (void)
{
  if (next != NULL)
    {
      M4ERROR ((warning_status, 0,
		"INTERNAL ERROR: Recursive push_string!"));
      abort ();
    }

  next = (input_block *) obstack_alloc (current_input,
				        sizeof (struct input_block));
  next->type = INPUT_STRING;
  return current_input;
}
Beispiel #12
0
Datei: debug.c Projekt: ezc/m4
static void
debug_set_file (FILE *fp)
{
  struct stat stdout_stat, debug_stat;

  if (debug != NULL && debug != stderr && debug != stdout
      && close_stream (debug) != 0)
    {
      M4ERROR ((warning_status, errno, "error writing to debug stream"));
      retcode = EXIT_FAILURE;
    }
  debug = fp;

  if (debug != NULL && debug != stdout)
    {
      if (fstat (STDOUT_FILENO, &stdout_stat) < 0)
	return;
      if (fstat (fileno (debug), &debug_stat) < 0)
	return;

      /* mingw has a bug where fstat on a regular file reports st_ino
	 of 0.  On normal system, st_ino should never be 0.  */
      if (stdout_stat.st_ino == debug_stat.st_ino
	  && stdout_stat.st_dev == debug_stat.st_dev
	  && stdout_stat.st_ino != 0)
	{
	  if (debug != stderr && close_stream (debug) != 0)
	    {
	      M4ERROR ((warning_status, errno,
			"error writing to debug stream"));
	      retcode = EXIT_FAILURE;
	    }
	  debug = stdout;
	}
    }
}
Beispiel #13
0
static void
expand_token (struct obstack *obs, token_type t, token_data *td, int line)
{
  symbol *sym;

  switch (t)
    { /* TOKSW */
    case TOKEN_EOF:
    case TOKEN_MACDEF:
      break;

    case TOKEN_OPEN:
    case TOKEN_COMMA:
    case TOKEN_CLOSE:
    case TOKEN_SIMPLE:
    case TOKEN_STRING:
      shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)),
                    line);
      break;

    case TOKEN_WORD:
      sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP);
      if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID
          || (SYMBOL_TYPE (sym) == TOKEN_FUNC
              && SYMBOL_BLIND_NO_ARGS (sym)
              && peek_token () != TOKEN_OPEN))
        {
#ifdef ENABLE_CHANGEWORD
          shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td),
                        strlen (TOKEN_DATA_ORIG_TEXT (td)), line);
#else
          shipout_text (obs, TOKEN_DATA_TEXT (td),
                        strlen (TOKEN_DATA_TEXT (td)), line);
#endif
        }
      else
        expand_macro (sym);
      break;

    default:
      M4ERROR ((warning_status, 0,
                "INTERNAL ERROR: bad token type in expand_token ()"));
      abort ();
    }
}
Beispiel #14
0
void
trace_pre (const char *name, int id, int argc, token_data **argv)
{
  int i;
  const builtin *bp;

  trace_header (id);
  trace_format ("%s", name);

  if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS))
    {
      trace_format ("(");

      for (i = 1; i < argc; i++)
        {
          if (i != 1)
            trace_format (", ");

          switch (TOKEN_DATA_TYPE (argv[i]))
            {
            case TOKEN_TEXT:
              trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i]));
              break;

            case TOKEN_FUNC:
              bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i]));
              if (bp == NULL)
                {
                  M4ERROR ((warning_status, 0, "\
INTERNAL ERROR: builtin not found in builtin table! (trace_pre ())"));
                  abort ();
                }
              trace_format ("<%s>", bp->name);
              break;

            case TOKEN_VOID:
            default:
              M4ERROR ((warning_status, 0,
                        "INTERNAL ERROR: bad token data type (trace_pre ())"));
              abort ();
            }

        }
Beispiel #15
0
static eval_error
shift_term (eval_token et, eval_t *v1)
{
  eval_token op;
  eval_t v2;
  eval_error er;

  if ((er = add_term (et, v1)) != NO_ERROR)
    return er;

  while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT)
    {

      et = eval_lex (&v2);
      if (et == ERROR)
	return UNKNOWN_INPUT;

      if ((er = add_term (et, &v2)) != NO_ERROR)
	return er;

      switch (op)
	{
	case LSHIFT:
	  *v1 = *v1 << v2;
	  break;

	case RSHIFT:
	  *v1 = *v1 >> v2;
	  break;

	default:
	  M4ERROR ((warning_status, 0,
		    "INTERNAL ERROR: Bad shift operator in shift_term ()"));
	  abort ();
	}
    }
  if (op == ERROR)
    return UNKNOWN_INPUT;

  eval_undo ();
  return NO_ERROR;
}
Beispiel #16
0
void
call_macro (symbol *sym, int argc, token_data **argv,
                 struct obstack *expansion)
{
  switch (SYMBOL_TYPE (sym))
    {
    case TOKEN_FUNC:
      (*SYMBOL_FUNC (sym)) (expansion, argc, argv);
      break;

    case TOKEN_TEXT:
      expand_user_macro (expansion, sym, argc, argv);
      break;

    case TOKEN_VOID:
    default:
      M4ERROR ((warning_status, 0,
                "INTERNAL ERROR: bad symbol type in call_macro ()"));
      abort ();
    }
}
Beispiel #17
0
void
insert_file (FILE *file)
{
  static char buffer[COPY_BUFFER_SIZE];
  size_t length;

  /* Optimize out inserting into a sink.  */
  if (!output_diversion)
    return;

  /* Insert output by big chunks.  */
  while (1)
    {
      length = fread (buffer, 1, sizeof buffer, file);
      if (ferror (file))
        M4ERROR ((EXIT_FAILURE, errno, "error reading inserted file"));
      if (length == 0)
        break;
      output_text (buffer, length);
    }
}
Beispiel #18
0
static int
peek_input (void)
{
  int ch;
  input_block *block = isp;

  while (1)
    {
      if (block == NULL)
        return CHAR_EOF;

      switch (block->type)
        {
        case INPUT_STRING:
          ch = to_uchar (block->u.u_s.string[0]);
          if (ch != '\0')
            return ch;
          break;

        case INPUT_FILE:
          ch = getc (block->u.u_f.fp);
          if (ch != EOF)
            {
              ungetc (ch, block->u.u_f.fp);
              return ch;
            }
          block->u.u_f.end = true;
          break;

        case INPUT_MACRO:
          return CHAR_MACRO;

        default:
          M4ERROR ((warning_status, 0,
                    "INTERNAL ERROR: input stack botch in peek_input ()"));
          abort ();
        }
      block = block->prev;
    }
}
Beispiel #19
0
Datei: debug.c Projekt: ezc/m4
bool
debug_set_output (const char *name)
{
  FILE *fp;

  if (name == NULL)
    debug_set_file (stderr);
  else if (*name == '\0')
    debug_set_file (NULL);
  else
    {
      fp = fopen (name, "a");
      if (fp == NULL)
	return false;

      if (set_cloexec_flag (fileno (fp), true) != 0)
	M4ERROR ((warning_status, errno,
		  "Warning: cannot protect debug file across forks"));
      debug_set_file (fp);
    }
  return true;
}
Beispiel #20
0
int
peek_input (void)
{
  register int ch;

  while (1)
    {
      if (isp == NULL)
	return CHAR_EOF;

      switch (isp->type)
	{
	case INPUT_STRING:
	  ch = isp->u.u_s.string[0];
	  if (ch != '\0')
	    return ch;
	  break;

	case INPUT_FILE:
	  ch = getc (isp->u.u_f.file);
	  if (ch != EOF)
	    {
	      ungetc (ch, isp->u.u_f.file);
	      return ch;
	    }
	  break;

	case INPUT_MACRO:
	  return CHAR_MACRO;

	default:
	  M4ERROR ((warning_status, 0,
		    "INTERNAL ERROR: Input stack botch in peek_input ()"));
	  abort ();
	}
      /* End of input source --- pop one level.  */
      pop_input ();
    }
}
Beispiel #21
0
void
set_word_regexp (const char *regexp)
{
  const char *msg;
  struct re_pattern_buffer new_word_regexp;

  if (!*regexp || STREQ (regexp, DEFAULT_WORD_REGEXP))
    {
      default_word_regexp = true;
      return;
    }

  /* Dry run to see whether the new expression is compilable.  */
  init_pattern_buffer (&new_word_regexp, NULL);
  msg = re_compile_pattern (regexp, strlen (regexp), &new_word_regexp);
  regfree (&new_word_regexp);

  if (msg != NULL)
    {
      M4ERROR ((warning_status, 0,
                "bad regular expression `%s': %s", regexp, msg));
      return;
    }

  /* If compilation worked, retry using the word_regexp struct.  We
     can't rely on struct assigns working, so redo the compilation.
     The fastmap can be reused between compilations, and will be freed
     by the final regfree.  */
  if (!word_regexp.fastmap)
    word_regexp.fastmap = xcharalloc (UCHAR_MAX + 1);
  msg = re_compile_pattern (regexp, strlen (regexp), &word_regexp);
  assert (!msg);
  re_set_registers (&word_regexp, &regs, regs.num_regs, regs.start, regs.end);
  if (re_compile_fastmap (&word_regexp))
    assert (false);

  default_word_regexp = false;
}
Beispiel #22
0
/* Reopen a temporary file for diversion DIVNUM for reading and
   writing in a secure temp directory.  If REREAD, the file is
   positioned at offset 0, otherwise the file is positioned at the
   end.  Exits on failure, so the return value is always an open
   file.  */
static FILE *
m4_tmpopen (int divnum, bool reread)
{
  const char *name;
  FILE *file;

  if (tmp_file1_owner == divnum)
    {
      if (reread && fseek (tmp_file1, 0, SEEK_SET) != 0)
        m4_error (EXIT_FAILURE, errno,
                  _("cannot seek within diversion"));
      tmp_file2_recent = false;
      return tmp_file1;
    }
  else if (tmp_file2_owner == divnum)
    {
      if (reread && fseek (tmp_file2, 0, SEEK_SET) != 0)
        m4_error (EXIT_FAILURE, errno,
                  _("cannot seek within diversion"));
      tmp_file2_recent = true;
      return tmp_file2;
    }
  name = m4_tmpname (divnum);
  /* We need update mode, to avoid truncation.  */
  file = fopen_temp (name, O_BINARY ? "rb+" : "r+");
  if (file == NULL)
    M4ERROR ((EXIT_FAILURE, errno,
              "cannot create temporary file for diversion"));
//  else if (set_cloexec_flag (fileno (file), true) != 0)
//    m4_error (0, errno, _("cannot protect diversion across forks"));
  /* Update mode starts at the beginning of the stream, but sometimes
     we want the end.  */
  else if (!reread && fseek (file, 0, SEEK_END) != 0)
    m4_error (EXIT_FAILURE, errno,
              _("cannot seek within diversion"));
  return file;
}
Beispiel #23
0
void
output_text (const char *text, int length)
{
  int count;

  if (!output_diversion || !length)
    return;

  if (!output_file && length > output_unused)
    make_room_for (length);

  if (output_file)
    {
      count = fwrite (text, length, 1, output_file);
      if (count != 1)
        M4ERROR ((EXIT_FAILURE, errno, "ERROR: copying inserted file"));
    }
  else
    {
      memcpy (output_cursor, text, (size_t) length);
      output_cursor += length;
      output_unused -= length;
    }
}
Beispiel #24
0
void
set_word_regexp (const char *regexp)
{
  int i;
  char test[2];
  const char *msg;

  if (!strcmp (regexp, DEFAULT_WORD_REGEXP))
    {
      default_word_regexp = TRUE;
      return;
    }

  default_word_regexp = FALSE;

  msg = re_compile_pattern (regexp, strlen (regexp), &word_regexp);

  if (msg != NULL)
    {
      M4ERROR ((warning_status, 0,
		"Bad regular expression `%s': %s", regexp, msg));
      return;
    }

  if (word_start == NULL)
    word_start = xmalloc (256);

  word_start[0] = '\0';
  test[1] = '\0';
  for (i = 1; i < 256; i++)
    {
      test[0] = i;
      if (re_search (&word_regexp, test, 1, 0, 0, &regs) >= 0)
	strcat (word_start, test);
    }
}
Beispiel #25
0
static void
expand_macro (symbol *sym)
{
  struct obstack arguments;     /* Alternate obstack if argc_stack is busy.  */
  unsigned argv_base;           /* Size of argv_stack on entry.  */
  bool use_argc_stack = true;   /* Whether argc_stack is safe.  */
  token_data **argv;
  int argc;
  struct obstack *expansion;
  const char *expanded;
  bool traced;
  int my_call_id;

  /* Report errors at the location where the open parenthesis (if any)
     was found, but after expansion, restore global state back to the
     location of the close parenthesis.  This is safe since we
     guarantee that macro expansion does not alter the state of
     current_file/current_line (dnl, include, and sinclude are special
     cased in the input engine to ensure this fact).  */
  const char *loc_open_file = current_file;
  int loc_open_line = current_line;
  const char *loc_close_file;
  int loc_close_line;

  SYMBOL_PENDING_EXPANSIONS (sym)++;
  expansion_level++;
  if (nesting_limit > 0 && expansion_level > nesting_limit)
    M4ERROR ((EXIT_FAILURE, 0,
              "recursion limit of %d exceeded, use -L<N> to change it",
              nesting_limit));

  macro_call_id++;
  my_call_id = macro_call_id;

  traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym);

  argv_base = obstack_object_size (&argv_stack);
  if (obstack_object_size (&argc_stack) > 0)
    {
      /* We cannot use argc_stack if this is a nested invocation, and an
         outer invocation has an unfinished argument being
         collected.  */
      obstack_init (&arguments);
      use_argc_stack = false;
    }

  if (traced && (debug_level & DEBUG_TRACE_CALL))
    trace_prepre (SYMBOL_NAME (sym), my_call_id);

  collect_arguments (sym, &argv_stack,
                     use_argc_stack ? &argc_stack : &arguments);

  argc = ((obstack_object_size (&argv_stack) - argv_base)
          / sizeof (token_data *));
  argv = (token_data **) ((char *) obstack_base (&argv_stack) + argv_base);

  loc_close_file = current_file;
  loc_close_line = current_line;
  current_file = loc_open_file;
  current_line = loc_open_line;

  if (traced)
    trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv);

  expansion = push_string_init ();
  call_macro (sym, argc, argv, expansion);
  expanded = push_string_finish ();

  if (traced)
    trace_post (SYMBOL_NAME (sym), my_call_id, argc, expanded);

  current_file = loc_close_file;
  current_line = loc_close_line;

  --expansion_level;
  --SYMBOL_PENDING_EXPANSIONS (sym);

  if (SYMBOL_DELETED (sym))
    free_symbol (sym);

  if (use_argc_stack)
    obstack_free (&argc_stack, argv[0]);
  else
    obstack_free (&arguments, NULL);
  obstack_blank (&argv_stack, -argc * sizeof (token_data *));
}
Beispiel #26
0
static void
insert_diversion_helper (m4_diversion *diversion)
{
  /* Effectively undivert only if an output stream is active.  */
  if (output_diversion)
    {
      if (diversion->size)
        {
          if (!output_diversion->u.file)
            {
              /* Transferring diversion metadata is faster than
                 copying contents.  */
              assert (!output_diversion->used && output_diversion != &div0
                      && !output_file);
              output_diversion->u.buffer = diversion->u.buffer;
              output_diversion->size = diversion->size;
              output_cursor = diversion->u.buffer + diversion->used;
              output_unused = diversion->size - diversion->used;
              diversion->u.buffer = NULL;
            }
          else
            {
              /* Avoid double-charging the total in-memory size when
                 transferring from one in-memory diversion to
                 another.  */
              total_buffer_size -= diversion->size;
              output_text (diversion->u.buffer, diversion->used);
            }
        }
      else if (!output_diversion->u.file)
        {
          /* Transferring diversion metadata is faster than copying
             contents.  */
          assert (!output_diversion->used && output_diversion != &div0
                  && !output_file);
          output_diversion->u.file = m4_tmprename (diversion->divnum,
                                                   output_diversion->divnum);
          output_diversion->used = 1;
          output_file = output_diversion->u.file;
          diversion->u.file = NULL;
          diversion->size = 1;
        }
      else
        {
          if (!diversion->u.file)
            diversion->u.file = m4_tmpopen (diversion->divnum, true);
          insert_file (diversion->u.file);
        }

      output_current_line = -1;
    }

  /* Return all space used by the diversion.  */
  if (diversion->size)
    {
      if (!output_diversion)
        total_buffer_size -= diversion->size;
      free (diversion->u.buffer);
      diversion->size = 0;
    }
  else
    {
      if (diversion->u.file)
        {
          FILE *file = diversion->u.file;
          diversion->u.file = NULL;
          if (m4_tmpclose (file, diversion->divnum) != 0)
            m4_error (0, errno,
                      _("cannot clean temporary file for diversion"));
        }
      if (m4_tmpremove (diversion->divnum) != 0)
        M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
    }
  diversion->used = 0;
  gl_oset_remove (diversion_table, diversion);
  diversion->u.next = free_list;
  free_list = diversion;
}
Beispiel #27
0
static void
make_room_for (int length)
{
  int wanted_size;
  m4_diversion *selected_diversion = NULL;

  /* Compute needed size for in-memory buffer.  Diversions in-memory
     buffers start at 0 bytes, then 512, then keep doubling until it is
     decided to flush them to disk.  */

  output_diversion->used = output_diversion->size - output_unused;

  for (wanted_size = output_diversion->size;
       wanted_size < output_diversion->used + length;
       wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2)
    ;

  /* Check if we are exceeding the maximum amount of buffer memory.  */

  if (total_buffer_size - output_diversion->size + wanted_size
      > MAXIMUM_TOTAL_SIZE)
    {
      int selected_used;
      char *selected_buffer;
      m4_diversion *diversion;
      int count;
      gl_oset_iterator_t iter;
      const void *elt;

      /* Find out the buffer having most data, in view of flushing it to
         disk.  Fake the current buffer as having already received the
         projected data, while making the selection.  So, if it is
         selected indeed, we will flush it smaller, before it grows.  */

      selected_diversion = output_diversion;
      selected_used = output_diversion->used + length;

      iter = gl_oset_iterator (diversion_table);
      while (gl_oset_iterator_next (&iter, &elt))
        {
          diversion = (m4_diversion *) elt;
          if (diversion->used > selected_used)
            {
              selected_diversion = diversion;
              selected_used = diversion->used;
            }
        }
      gl_oset_iterator_free (&iter);

      /* Create a temporary file, write the in-memory buffer of the
         diversion to this file, then release the buffer.  Zero the
         diversion before doing anything that can exit () (including
         m4_tmpfile), so that the atexit handler doesn't try to close
         a garbage pointer as a file.  */

      selected_buffer = selected_diversion->u.buffer;
      total_buffer_size -= selected_diversion->size;
      selected_diversion->size = 0;
      selected_diversion->u.file = NULL;
      selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);

      if (selected_diversion->used > 0)
        {
          count = fwrite (selected_buffer, (size_t) selected_diversion->used,
                          1, selected_diversion->u.file);
          if (count != 1)
            M4ERROR ((EXIT_FAILURE, errno,
                      "ERROR: cannot flush diversion to temporary file"));
        }

      /* Reclaim the buffer space for other diversions.  */

      free (selected_buffer);
      selected_diversion->used = 1;
    }

  /* Reload output_file, just in case the flushed diversion was current.  */

  if (output_diversion == selected_diversion)
    {
      /* The flushed diversion was current indeed.  */

      output_file = output_diversion->u.file;
      output_cursor = NULL;
      output_unused = 0;
    }
  else
    {
      /* Close any selected file since it is not the current diversion.  */
      if (selected_diversion)
        {
          FILE *file = selected_diversion->u.file;
          selected_diversion->u.file = NULL;
          if (m4_tmpclose (file, selected_diversion->divnum) != 0)
            m4_error (0, errno,
                      _("cannot close temporary file for diversion"));
        }

      /* The current buffer may be safely reallocated.  */
      {
        char *buffer = output_diversion->u.buffer;
        output_diversion->u.buffer = xcharalloc ((size_t) wanted_size);
        memcpy (output_diversion->u.buffer, buffer, output_diversion->used);
        free (buffer);
      }

      total_buffer_size += wanted_size - output_diversion->size;
      output_diversion->size = wanted_size;

      output_cursor = output_diversion->u.buffer + output_diversion->used;
      output_unused = wanted_size - output_diversion->used;
    }
}
Beispiel #28
0
static int
next_char_1 (void)
{
  int ch;

  while (1)
    {
      if (isp == NULL)
        {
          current_file = "";
          current_line = 0;
          return CHAR_EOF;
        }

      if (input_change)
        {
          current_file = isp->file;
          current_line = isp->line;
          input_change = false;
        }

      switch (isp->type)
        {
        case INPUT_STRING:
          ch = to_uchar (*isp->u.u_s.string++);
          if (ch != '\0')
            return ch;
          break;

        case INPUT_FILE:
          if (start_of_input_line)
            {
              start_of_input_line = false;
              current_line = ++isp->line;
            }

          /* If stdin is a terminal, calling getc after peek_input
             already called it would make the user have to hit ^D
             twice to quit.  */
          ch = isp->u.u_f.end ? EOF : getc (isp->u.u_f.fp);
          if (ch != EOF)
            {
              if (ch == '\n')
                start_of_input_line = true;
              return ch;
            }
          break;

        case INPUT_MACRO:
          pop_input (); /* INPUT_MACRO input sources has only one token */
          return CHAR_MACRO;

        default:
          M4ERROR ((warning_status, 0,
                    "INTERNAL ERROR: input stack botch in next_char ()"));
          abort ();
        }

      /* End of input source --- pop one level.  */
      pop_input ();
    }
}
Beispiel #29
0
token_type
next_token (token_data *td)
{
  int ch;
  int quote_level;
  token_type type;
#ifdef ENABLE_CHANGEWORD
  int startpos;
  char *orig_text = 0;
#endif

  obstack_free (&token_stack, token_bottom);
  obstack_1grow (&token_stack, '\0');
  token_bottom = obstack_finish (&token_stack);

  ch = peek_input ();
  if (ch == CHAR_EOF)
    {
      return TOKEN_EOF;
#ifdef DEBUG_INPUT
      fprintf (stderr, "next_token -> EOF\n");
#endif
    }
  if (ch == CHAR_MACRO)
    {
      init_macro_token (td);
      (void) next_char ();
      return TOKEN_MACDEF;
    }

  (void) next_char ();
  if (MATCH (ch, bcomm.string))
    {
      obstack_grow (&token_stack, bcomm.string, bcomm.length);
      while ((ch = next_char ()) != CHAR_EOF && !MATCH (ch, ecomm.string))
	obstack_1grow (&token_stack, ch);
      if (ch != CHAR_EOF)
	obstack_grow (&token_stack, ecomm.string, ecomm.length);
      type = TOKEN_STRING;
    }
#ifdef ENABLE_CHANGEWORD
  else if (default_word_regexp && (isalpha (ch) || ch == '_'))
#else
  else if (isalpha (ch) || ch == '_')
#endif
    {
      obstack_1grow (&token_stack, ch);
      while ((ch = peek_input ()) != CHAR_EOF && (isalnum (ch) || ch == '_'))
	{
	  obstack_1grow (&token_stack, ch);
	  (void) next_char ();
	}
      type = TOKEN_WORD;
    }

#ifdef ENABLE_CHANGEWORD

  else if (!default_word_regexp && strchr (word_start, ch))
    {
      obstack_1grow (&token_stack, ch);
      while (1)
        {
	  ch = peek_input ();
	  if (ch == CHAR_EOF)
	    break;
	  obstack_1grow (&token_stack, ch);
	  startpos = re_search (&word_regexp, obstack_base (&token_stack),
				obstack_object_size (&token_stack), 0, 0,
				&regs);
	  if (startpos != 0 ||
	      regs.end [0] != obstack_object_size (&token_stack))
	    {
	      *(((char *) obstack_base (&token_stack)
		 + obstack_object_size (&token_stack)) - 1) = '\0';
	      break;
	    }
	  next_char ();
	}

      obstack_1grow (&token_stack, '\0');
      orig_text = obstack_finish (&token_stack);

      if (regs.start[1] != -1)
	obstack_grow (&token_stack,orig_text + regs.start[1],
		      regs.end[1] - regs.start[1]);
      else
	obstack_grow (&token_stack, orig_text,regs.end[0]);

      type = TOKEN_WORD;
    }

#endif /* ENABLE_CHANGEWORD */

  else if (!MATCH (ch, lquote.string))
    {
      type = TOKEN_SIMPLE;
      obstack_1grow (&token_stack, ch);
    }
  else
    {
      quote_level = 1;
      while (1)
	{
	  ch = next_char ();
	  if (ch == CHAR_EOF)
	    M4ERROR ((EXIT_FAILURE, 0,
		      "ERROR: EOF in string"));

	  if (MATCH (ch, rquote.string))
	    {
	      if (--quote_level == 0)
		break;
	      obstack_grow (&token_stack, rquote.string, rquote.length);
	    }
	  else if (MATCH (ch, lquote.string))
	    {
	      quote_level++;
	      obstack_grow (&token_stack, lquote.string, lquote.length);
	    }
	  else
	    obstack_1grow (&token_stack, ch);
	}
      type = TOKEN_STRING;
    }

  obstack_1grow (&token_stack, '\0');

  TOKEN_DATA_TYPE (td) = TOKEN_TEXT;
  TOKEN_DATA_TEXT (td) = obstack_finish (&token_stack);
#ifdef ENABLE_CHANGEWORD
  if (orig_text == NULL)
    orig_text = TOKEN_DATA_TEXT (td);
  TOKEN_DATA_ORIG_TEXT (td) = orig_text;
#endif
#ifdef DEBUG_INPUT
  fprintf (stderr, "next_token -> %d (%s)\n", type, TOKEN_DATA_TEXT (td));
#endif
  return type;
}
Beispiel #30
0
static bool
expand_argument (struct obstack *obs, token_data *argp)
{
  token_type t;
  token_data td;
  char *text;
  int paren_level;
  const char *file = current_file;
  int line = current_line;

  TOKEN_DATA_TYPE (argp) = TOKEN_VOID;

  /* Skip leading white space.  */
  do
    {
      t = next_token (&td, NULL);
    }
  while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td))));

  paren_level = 0;

  while (1)
    {

      switch (t)
        { /* TOKSW */
        case TOKEN_COMMA:
        case TOKEN_CLOSE:
          if (paren_level == 0)
            {
              /* The argument MUST be finished, whether we want it or not.  */
              obstack_1grow (obs, '\0');
              text = (char *) obstack_finish (obs);

              if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID)
                {
                  TOKEN_DATA_TYPE (argp) = TOKEN_TEXT;
                  TOKEN_DATA_TEXT (argp) = text;
                }
              return t == TOKEN_COMMA;
            }
          /* fallthru */
        case TOKEN_OPEN:
        case TOKEN_SIMPLE:
          text = TOKEN_DATA_TEXT (&td);

          if (*text == '(')
            paren_level++;
          else if (*text == ')')
            paren_level--;
          expand_token (obs, t, &td, line);
          break;

        case TOKEN_EOF:
          /* current_file changed to "" if we see TOKEN_EOF, use the
             previous value we stored earlier.  */
          M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line,
                            "ERROR: end of file in argument list"));
          break;

        case TOKEN_WORD:
        case TOKEN_STRING:
          expand_token (obs, t, &td, line);
          break;

        case TOKEN_MACDEF:
          if (obstack_object_size (obs) == 0)
            {
              TOKEN_DATA_TYPE (argp) = TOKEN_FUNC;
              TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td);
            }
          break;

        default:
          M4ERROR ((warning_status, 0,
                    "INTERNAL ERROR: bad token type in expand_argument ()"));
          abort ();
        }

      t = next_token (&td, NULL);
    }
}