/* 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; }
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; }
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); }
/* 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); }
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; }
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; }
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); }
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; }
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; }
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 (); } }
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; }
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; } } }
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 (); } }
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 (); } }
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; }
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 (); } }
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); } }
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; } }
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; }
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 (); } }
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, ®s, regs.num_regs, regs.start, regs.end); if (re_compile_fastmap (&word_regexp)) assert (false); default_word_regexp = false; }
/* 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; }
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; } }
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, ®s) >= 0) strcat (word_start, test); } }
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 *)); }
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; }
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; } }
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 (); } }
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, ®s); 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; }
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); } }