From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- gcc/read-md.c | 1139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1139 insertions(+) create mode 100644 gcc/read-md.c (limited to 'gcc/read-md.c') diff --git a/gcc/read-md.c b/gcc/read-md.c new file mode 100644 index 000000000..9c622912b --- /dev/null +++ b/gcc/read-md.c @@ -0,0 +1,1139 @@ +/* MD reader for GCC. + Copyright (C) 1987, 1988, 1991, 1994, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2007, 2008, 2010 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +. */ + +#include "bconfig.h" +#include "system.h" +#include "coretypes.h" +#include "hashtab.h" +#include "errors.h" +#include "read-md.h" + +/* Associates PTR (which can be a string, etc.) with the file location + specified by FILENAME and LINENO. */ +struct ptr_loc { + const void *ptr; + const char *filename; + int lineno; +}; + +/* A singly-linked list of filenames. */ +struct file_name_list { + struct file_name_list *next; + const char *fname; +}; + +/* Obstack used for allocating MD strings. */ +struct obstack string_obstack; + +/* A table of ptr_locs, hashed on the PTR field. */ +static htab_t ptr_locs; + +/* An obstack for the above. Plain xmalloc is a bit heavyweight for a + small structure like ptr_loc. */ +static struct obstack ptr_loc_obstack; + +/* A hash table of triples (A, B, C), where each of A, B and C is a condition + and A is equivalent to "B && C". This is used to keep track of the source + of conditions that are made up of separate MD strings (such as the split + condition of a define_insn_and_split). */ +static htab_t joined_conditions; + +/* An obstack for allocating joined_conditions entries. */ +static struct obstack joined_conditions_obstack; + +/* The file we are reading. */ +FILE *read_md_file; + +/* The filename of READ_MD_FILE. */ +const char *read_md_filename; + +/* The current line number in READ_MD_FILE. */ +int read_md_lineno; + +/* The name of the toplevel file that indirectly included READ_MD_FILE. */ +const char *in_fname; + +/* The directory part of IN_FNAME. NULL if IN_FNAME is a bare filename. */ +static char *base_dir; + +/* The first directory to search. */ +static struct file_name_list *first_dir_md_include; + +/* A pointer to the null terminator of the md include chain. */ +static struct file_name_list **last_dir_md_include_ptr = &first_dir_md_include; + +/* This callback will be invoked whenever an md include directive is + processed. To be used for creation of the dependency file. */ +void (*include_callback) (const char *); + +/* The current maximum length of directory names in the search path + for include files. (Altered as we get more of them.) */ +static size_t max_include_len; + +/* A table of md_constant structures, hashed by name. Null if no + constant expansion should occur. */ +static htab_t md_constants; + +/* A table of enum_type structures, hashed by name. */ +static htab_t enum_types; + +static void handle_file (directive_handler_t); + +/* Given an object that starts with a char * name field, return a hash + code for its name. */ + +hashval_t +leading_string_hash (const void *def) +{ + return htab_hash_string (*(const char *const *) def); +} + +/* Given two objects that start with char * name fields, return true if + they have the same name. */ + +int +leading_string_eq_p (const void *def1, const void *def2) +{ + return strcmp (*(const char *const *) def1, + *(const char *const *) def2) == 0; +} + +/* Return a hash value for the pointer pointed to by DEF. */ + +static hashval_t +leading_ptr_hash (const void *def) +{ + return htab_hash_pointer (*(const void *const *) def); +} + +/* Return true if DEF1 and DEF2 are pointers to the same pointer. */ + +static int +leading_ptr_eq_p (const void *def1, const void *def2) +{ + return *(const void *const *) def1 == *(const void *const *) def2; +} + +/* Associate PTR with the file position given by FILENAME and LINENO. */ + +static void +set_md_ptr_loc (const void *ptr, const char *filename, int lineno) +{ + struct ptr_loc *loc; + + loc = (struct ptr_loc *) obstack_alloc (&ptr_loc_obstack, + sizeof (struct ptr_loc)); + loc->ptr = ptr; + loc->filename = filename; + loc->lineno = lineno; + *htab_find_slot (ptr_locs, loc, INSERT) = loc; +} + +/* Return the position associated with pointer PTR. Return null if no + position was set. */ + +static const struct ptr_loc * +get_md_ptr_loc (const void *ptr) +{ + return (const struct ptr_loc *) htab_find (ptr_locs, &ptr); +} + +/* Associate NEW_PTR with the same file position as OLD_PTR. */ + +void +copy_md_ptr_loc (const void *new_ptr, const void *old_ptr) +{ + const struct ptr_loc *loc = get_md_ptr_loc (old_ptr); + if (loc != 0) + set_md_ptr_loc (new_ptr, loc->filename, loc->lineno); +} + +/* If PTR is associated with a known file position, print a #line + directive for it. */ + +void +print_md_ptr_loc (const void *ptr) +{ + const struct ptr_loc *loc = get_md_ptr_loc (ptr); + if (loc != 0) + printf ("#line %d \"%s\"\n", loc->lineno, loc->filename); +} + +/* Return a condition that satisfies both COND1 and COND2. Either string + may be null or empty. */ + +const char * +join_c_conditions (const char *cond1, const char *cond2) +{ + char *result; + const void **entry; + + if (cond1 == 0 || cond1[0] == 0) + return cond2; + + if (cond2 == 0 || cond2[0] == 0) + return cond1; + + if (strcmp (cond1, cond2) == 0) + return cond1; + + result = concat ("(", cond1, ") && (", cond2, ")", NULL); + obstack_ptr_grow (&joined_conditions_obstack, result); + obstack_ptr_grow (&joined_conditions_obstack, cond1); + obstack_ptr_grow (&joined_conditions_obstack, cond2); + entry = XOBFINISH (&joined_conditions_obstack, const void **); + *htab_find_slot (joined_conditions, entry, INSERT) = entry; + return result; +} + +/* Print condition COND, wrapped in brackets. If COND was created by + join_c_conditions, recursively invoke this function for the original + conditions and join the result with "&&". Otherwise print a #line + directive for COND if its original file position is known. */ + +void +print_c_condition (const char *cond) +{ + const char **halves = (const char **) htab_find (joined_conditions, &cond); + if (halves != 0) + { + printf ("("); + print_c_condition (halves[1]); + printf (" && "); + print_c_condition (halves[2]); + printf (")"); + } + else + { + putc ('\n', stdout); + print_md_ptr_loc (cond); + printf ("(%s)", cond); + } +} + +/* A vfprintf-like function for reporting an error against line LINENO + of the current MD file. */ + +static void ATTRIBUTE_PRINTF(2,0) +message_with_line_1 (int lineno, const char *msg, va_list ap) +{ + fprintf (stderr, "%s:%d: ", read_md_filename, lineno); + vfprintf (stderr, msg, ap); + fputc ('\n', stderr); +} + +/* A printf-like function for reporting an error against line LINENO + in the current MD file. */ + +void +message_with_line (int lineno, const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + message_with_line_1 (lineno, msg, ap); + va_end (ap); +} + +/* Like message_with_line, but treat the condition as an error. */ + +void +error_with_line (int lineno, const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + message_with_line_1 (lineno, msg, ap); + va_end (ap); + have_error = 1; +} + +/* A printf-like function for reporting an error against the current + position in the MD file. */ + +void +fatal_with_file_and_line (const char *msg, ...) +{ + char context[64]; + size_t i; + int c; + va_list ap; + + va_start (ap, msg); + + fprintf (stderr, "%s:%d: ", read_md_filename, read_md_lineno); + vfprintf (stderr, msg, ap); + putc ('\n', stderr); + + /* Gather some following context. */ + for (i = 0; i < sizeof (context)-1; ++i) + { + c = read_char (); + if (c == EOF) + break; + if (c == '\r' || c == '\n') + { + unread_char (c); + break; + } + context[i] = c; + } + context[i] = '\0'; + + fprintf (stderr, "%s:%d: following context is `%s'\n", + read_md_filename, read_md_lineno, context); + + va_end (ap); + exit (1); +} + +/* Report that we found character ACTUAL when we expected to find + character EXPECTED. */ + +void +fatal_expected_char (int expected, int actual) +{ + if (actual == EOF) + fatal_with_file_and_line ("expected character `%c', found EOF", + expected); + else + fatal_with_file_and_line ("expected character `%c', found `%c'", + expected, actual); +} + +/* Read chars from the MD file until a non-whitespace char and return that. + Comments, both Lisp style and C style, are treated as whitespace. */ + +int +read_skip_spaces (void) +{ + int c; + + while (1) + { + c = read_char (); + switch (c) + { + case ' ': case '\t': case '\f': case '\r': case '\n': + break; + + case ';': + do + c = read_char (); + while (c != '\n' && c != EOF); + break; + + case '/': + { + int prevc; + c = read_char (); + if (c != '*') + { + unread_char (c); + fatal_with_file_and_line ("stray '/' in file"); + } + + prevc = 0; + while ((c = read_char ()) && c != EOF) + { + if (prevc == '*' && c == '/') + break; + prevc = c; + } + } + break; + + default: + return c; + } + } +} + +/* Read an rtx code name into NAME. It is terminated by any of the + punctuation chars of rtx printed syntax. */ + +void +read_name (struct md_name *name) +{ + int c; + size_t i; + + c = read_skip_spaces (); + + i = 0; + while (1) + { + if (c == ' ' || c == '\n' || c == '\t' || c == '\f' || c == '\r' + || c == EOF) + break; + if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/' + || c == '(' || c == '[') + { + unread_char (c); + break; + } + + if (i == sizeof (name->buffer) - 1) + fatal_with_file_and_line ("name too long"); + name->buffer[i++] = c; + + c = read_char (); + } + + if (i == 0) + fatal_with_file_and_line ("missing name or number"); + + name->buffer[i] = 0; + name->string = name->buffer; + + if (md_constants) + { + /* Do constant expansion. */ + struct md_constant *def; + + do + { + struct md_constant tmp_def; + + tmp_def.name = name->string; + def = (struct md_constant *) htab_find (md_constants, &tmp_def); + if (def) + name->string = def->value; + } + while (def); + } +} + +/* Subroutine of the string readers. Handles backslash escapes. + Caller has read the backslash, but not placed it into the obstack. */ + +static void +read_escape (void) +{ + int c = read_char (); + + switch (c) + { + /* Backslash-newline is replaced by nothing, as in C. */ + case '\n': + return; + + /* \" \' \\ are replaced by the second character. */ + case '\\': + case '"': + case '\'': + break; + + /* Standard C string escapes: + \a \b \f \n \r \t \v + \[0-7] \x + all are passed through to the output string unmolested. + In normal use these wind up in a string constant processed + by the C compiler, which will translate them appropriately. + We do not bother checking that \[0-7] are followed by up to + two octal digits, or that \x is followed by N hex digits. + \? \u \U are left out because they are not in traditional C. */ + case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case 'x': + obstack_1grow (&string_obstack, '\\'); + break; + + /* \; makes stuff for a C string constant containing + newline and tab. */ + case ';': + obstack_grow (&string_obstack, "\\n\\t", 4); + return; + + /* pass anything else through, but issue a warning. */ + default: + fprintf (stderr, "%s:%d: warning: unrecognized escape \\%c\n", + read_md_filename, read_md_lineno, c); + obstack_1grow (&string_obstack, '\\'); + break; + } + + obstack_1grow (&string_obstack, c); +} + +/* Read a double-quoted string onto the obstack. Caller has scanned + the leading quote. */ + +char * +read_quoted_string (void) +{ + int c; + + while (1) + { + c = read_char (); /* Read the string */ + if (c == '\\') + { + read_escape (); + continue; + } + else if (c == '"' || c == EOF) + break; + + obstack_1grow (&string_obstack, c); + } + + obstack_1grow (&string_obstack, 0); + return XOBFINISH (&string_obstack, char *); +} + +/* Read a braced string (a la Tcl) onto the string obstack. Caller + has scanned the leading brace. Note that unlike quoted strings, + the outermost braces _are_ included in the string constant. */ + +static char * +read_braced_string (void) +{ + int c; + int brace_depth = 1; /* caller-processed */ + unsigned long starting_read_md_lineno = read_md_lineno; + + obstack_1grow (&string_obstack, '{'); + while (brace_depth) + { + c = read_char (); /* Read the string */ + + if (c == '{') + brace_depth++; + else if (c == '}') + brace_depth--; + else if (c == '\\') + { + read_escape (); + continue; + } + else if (c == EOF) + fatal_with_file_and_line + ("missing closing } for opening brace on line %lu", + starting_read_md_lineno); + + obstack_1grow (&string_obstack, c); + } + + obstack_1grow (&string_obstack, 0); + return XOBFINISH (&string_obstack, char *); +} + +/* Read some kind of string constant. This is the high-level routine + used by read_rtx. It handles surrounding parentheses, leading star, + and dispatch to the appropriate string constant reader. */ + +char * +read_string (int star_if_braced) +{ + char *stringbuf; + int saw_paren = 0; + int c, old_lineno; + + c = read_skip_spaces (); + if (c == '(') + { + saw_paren = 1; + c = read_skip_spaces (); + } + + old_lineno = read_md_lineno; + if (c == '"') + stringbuf = read_quoted_string (); + else if (c == '{') + { + if (star_if_braced) + obstack_1grow (&string_obstack, '*'); + stringbuf = read_braced_string (); + } + else + fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c); + + if (saw_paren) + { + c = read_skip_spaces (); + if (c != ')') + fatal_expected_char (')', c); + } + + set_md_ptr_loc (stringbuf, read_md_filename, old_lineno); + return stringbuf; +} + +/* Skip the rest of a construct that started at line LINENO and that + is currently nested by DEPTH levels of parentheses. */ + +void +read_skip_construct (int depth, int lineno) +{ + struct md_name name; + int c; + + do + { + c = read_skip_spaces (); + if (c == EOF) + { + error_with_line (lineno, "unterminated construct"); + exit (1); + } + switch (c) + { + case '(': + depth++; + break; + + case ')': + depth--; + break; + + case ':': + case '[': + case ']': + case '/': + break; + + case '\"': + case '{': + unread_char (c); + read_string (false); + break; + + default: + unread_char (c); + read_name (&name); + break; + } + } + while (depth > 0); + unread_char (c); +} + +/* Given a string, return the number of comma-separated elements in it. + Return 0 for the null string. */ + +int +n_comma_elts (const char *s) +{ + int n; + + if (*s == '\0') + return 0; + + for (n = 1; *s; s++) + if (*s == ',') + n++; + + return n; +} + +/* Given a pointer to a (char *), return a pointer to the beginning of the + next comma-separated element in the string. Advance the pointer given + to the end of that element. Return NULL if at end of string. Caller + is responsible for copying the string if necessary. White space between + a comma and an element is ignored. */ + +const char * +scan_comma_elt (const char **pstr) +{ + const char *start; + const char *p = *pstr; + + if (*p == ',') + p++; + while (ISSPACE(*p)) + p++; + + if (*p == '\0') + return NULL; + + start = p; + + while (*p != ',' && *p != '\0') + p++; + + *pstr = p; + return start; +} + +/* Convert STRING to uppercase. */ + +void +upcase_string (char *string) +{ + int i; + + for (i = 0; string[i]; i++) + string[i] = TOUPPER (string[i]); +} + +/* Add a NAME = VALUE definition to md_constants-style hash table DEFS, + where both NAME and VALUE are malloc()ed strings. PARENT_ENUM is the + enum to which NAME belongs, or null if NAME is a stand-alone constant. */ + +static struct md_constant * +add_constant (htab_t defs, char *name, char *value, + struct enum_type *parent_enum) +{ + struct md_constant *def, tmp_def; + void **entry_ptr; + + tmp_def.name = name; + entry_ptr = htab_find_slot (defs, &tmp_def, INSERT); + if (*entry_ptr) + { + def = (struct md_constant *) *entry_ptr; + if (strcmp (def->value, value) != 0) + fatal_with_file_and_line ("redefinition of `%s', was `%s', now `%s'", + def->name, def->value, value); + else if (parent_enum || def->parent_enum) + fatal_with_file_and_line ("redefinition of `%s'", def->name); + free (name); + free (value); + } + else + { + def = XNEW (struct md_constant); + def->name = name; + def->value = value; + def->parent_enum = parent_enum; + *entry_ptr = def; + } + return def; +} + +/* Process a define_constants directive, starting with the optional space + after the "define_constants". */ + +static void +handle_constants (void) +{ + int c; + htab_t defs; + + c = read_skip_spaces (); + if (c != '[') + fatal_expected_char ('[', c); + + /* Disable constant expansion during definition processing. */ + defs = md_constants; + md_constants = 0; + while ( (c = read_skip_spaces ()) != ']') + { + struct md_name name, value; + + if (c != '(') + fatal_expected_char ('(', c); + + read_name (&name); + read_name (&value); + add_constant (defs, xstrdup (name.string), xstrdup (value.string), 0); + + c = read_skip_spaces (); + if (c != ')') + fatal_expected_char (')', c); + } + md_constants = defs; +} + +/* For every constant definition, call CALLBACK with two arguments: + a pointer a pointer to the constant definition and INFO. + Stop when CALLBACK returns zero. */ + +void +traverse_md_constants (htab_trav callback, void *info) +{ + htab_traverse (md_constants, callback, info); +} + +/* Return a malloc()ed decimal string that represents number NUMBER. */ + +static char * +decimal_string (int number) +{ + /* A safe overestimate. +1 for sign, +1 for null terminator. */ + char buffer[sizeof (int) * CHAR_BIT + 1 + 1]; + + sprintf (buffer, "%d", number); + return xstrdup (buffer); +} + +/* Process a define_enum or define_c_enum directive, starting with + the optional space after the "define_enum". LINENO is the line + number on which the directive started and MD_P is true if the + directive is a define_enum rather than a define_c_enum. */ + +static void +handle_enum (int lineno, bool md_p) +{ + char *enum_name, *value_name; + struct md_name name; + struct enum_type *def; + struct enum_value *ev; + void **slot; + int c; + + enum_name = read_string (false); + slot = htab_find_slot (enum_types, &enum_name, INSERT); + if (*slot) + { + def = (struct enum_type *) *slot; + if (def->md_p != md_p) + error_with_line (lineno, "redefining `%s' as a different type of enum", + enum_name); + } + else + { + def = XNEW (struct enum_type); + def->name = enum_name; + def->md_p = md_p; + def->values = 0; + def->tail_ptr = &def->values; + def->num_values = 0; + *slot = def; + } + + c = read_skip_spaces (); + if (c != '[') + fatal_expected_char ('[', c); + + while ((c = read_skip_spaces ()) != ']') + { + if (c == EOF) + { + error_with_line (lineno, "unterminated construct"); + exit (1); + } + unread_char (c); + read_name (&name); + + ev = XNEW (struct enum_value); + ev->next = 0; + if (md_p) + { + value_name = concat (def->name, "_", name.string, NULL); + upcase_string (value_name); + ev->name = xstrdup (name.string); + } + else + { + value_name = xstrdup (name.string); + ev->name = value_name; + } + ev->def = add_constant (md_constants, value_name, + decimal_string (def->num_values), def); + + *def->tail_ptr = ev; + def->tail_ptr = &ev->next; + def->num_values++; + } +} + +/* Try to find the definition of the given enum. Return null on failure. */ + +struct enum_type * +lookup_enum_type (const char *name) +{ + return (struct enum_type *) htab_find (enum_types, &name); +} + +/* For every enum definition, call CALLBACK with two arguments: + a pointer to the constant definition and INFO. Stop when CALLBACK + returns zero. */ + +void +traverse_enum_types (htab_trav callback, void *info) +{ + htab_traverse (enum_types, callback, info); +} + +/* Process an "include" directive, starting with the optional space + after the "include". Read in the file and use HANDLE_DIRECTIVE + to process each unknown directive. LINENO is the line number on + which the "include" occured. */ + +static void +handle_include (int lineno, directive_handler_t handle_directive) +{ + const char *filename; + const char *old_filename; + int old_lineno; + char *pathname; + FILE *input_file, *old_file; + + filename = read_string (false); + input_file = NULL; + + /* If the specified file name is absolute, skip the include stack. */ + if (!IS_ABSOLUTE_PATH (filename)) + { + struct file_name_list *stackp; + + /* Search the directory path, trying to open the file. */ + for (stackp = first_dir_md_include; stackp; stackp = stackp->next) + { + static const char sep[2] = { DIR_SEPARATOR, '\0' }; + + pathname = concat (stackp->fname, sep, filename, NULL); + input_file = fopen (pathname, "r"); + if (input_file != NULL) + break; + free (pathname); + } + } + + /* If we haven't managed to open the file yet, try combining the + filename with BASE_DIR. */ + if (input_file == NULL) + { + if (base_dir) + pathname = concat (base_dir, filename, NULL); + else + pathname = xstrdup (filename); + input_file = fopen (pathname, "r"); + } + + if (input_file == NULL) + { + free (pathname); + error_with_line (lineno, "include file `%s' not found", filename); + return; + } + + /* Save the old cursor. Note that the LINENO argument to this + function is the beginning of the include statement, while + read_md_lineno has already been advanced. */ + old_file = read_md_file; + old_filename = read_md_filename; + old_lineno = read_md_lineno; + + if (include_callback) + include_callback (pathname); + + read_md_file = input_file; + read_md_filename = pathname; + handle_file (handle_directive); + + /* Restore the old cursor. */ + read_md_file = old_file; + read_md_filename = old_filename; + read_md_lineno = old_lineno; + + /* Do not free the pathname. It is attached to the various rtx + queue elements. */ +} + +/* Process the current file, assuming that read_md_file and + read_md_filename are valid. Use HANDLE_DIRECTIVE to handle + unknown directives. */ + +static void +handle_file (directive_handler_t handle_directive) +{ + struct md_name directive; + int c, lineno; + + read_md_lineno = 1; + while ((c = read_skip_spaces ()) != EOF) + { + lineno = read_md_lineno; + if (c != '(') + fatal_expected_char ('(', c); + + read_name (&directive); + if (strcmp (directive.string, "define_constants") == 0) + handle_constants (); + else if (strcmp (directive.string, "define_enum") == 0) + handle_enum (lineno, true); + else if (strcmp (directive.string, "define_c_enum") == 0) + handle_enum (lineno, false); + else if (strcmp (directive.string, "include") == 0) + handle_include (lineno, handle_directive); + else if (handle_directive) + handle_directive (lineno, directive.string); + else + read_skip_construct (1, lineno); + + c = read_skip_spaces (); + if (c != ')') + fatal_expected_char (')', c); + } + fclose (read_md_file); +} + +/* Like handle_file, but for top-level files. Set up in_fname and + base_dir accordingly. */ + +static void +handle_toplevel_file (directive_handler_t handle_directive) +{ + char *lastsl; + + in_fname = read_md_filename; + lastsl = strrchr (in_fname, '/'); + if (lastsl != NULL) + base_dir = xstrndup (in_fname, lastsl - in_fname + 1); + else + base_dir = NULL; + + handle_file (handle_directive); +} + +/* Parse a -I option with argument ARG. */ + +static void +parse_include (const char *arg) +{ + struct file_name_list *dirtmp; + + dirtmp = XNEW (struct file_name_list); + dirtmp->next = 0; + dirtmp->fname = arg; + *last_dir_md_include_ptr = dirtmp; + last_dir_md_include_ptr = &dirtmp->next; + if (strlen (dirtmp->fname) > max_include_len) + max_include_len = strlen (dirtmp->fname); +} + +/* The main routine for reading .md files. Try to process all the .md + files specified on the command line and return true if no error occured. + + ARGC and ARGV are the arguments to main. + + PARSE_OPT, if nonnull, is passed all unknown command-line arguments. + It should return true if it recognizes the argument or false if a + generic error should be reported. + + If HANDLE_DIRECTIVE is nonnull, the parser calls it for each + unknown directive, otherwise it just skips such directives. + See the comment above the directive_handler_t definition for + details about the callback's interface. */ + +bool +read_md_files (int argc, char **argv, bool (*parse_opt) (const char *), + directive_handler_t handle_directive) +{ + int i; + bool no_more_options; + bool already_read_stdin; + int num_files; + + /* Initialize global data. */ + obstack_init (&string_obstack); + ptr_locs = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0); + obstack_init (&ptr_loc_obstack); + joined_conditions = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0); + obstack_init (&joined_conditions_obstack); + md_constants = htab_create (31, leading_string_hash, + leading_string_eq_p, (htab_del) 0); + enum_types = htab_create (31, leading_string_hash, + leading_string_eq_p, (htab_del) 0); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + /* First we loop over all the options. */ + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + { + /* An argument consisting of exactly one dash is a request to + read stdin. This will be handled in the second loop. */ + if (argv[i][1] == '\0') + continue; + + /* An argument consisting of just two dashes causes option + parsing to cease. */ + if (argv[i][1] == '-' && argv[i][2] == '\0') + break; + + if (argv[i][1] == 'I') + { + if (argv[i][2] != '\0') + parse_include (argv[i] + 2); + else if (++i < argc) + parse_include (argv[i]); + else + fatal ("directory name missing after -I option"); + continue; + } + + /* The program may have provided a callback so it can + accept its own options. */ + if (parse_opt && parse_opt (argv[i])) + continue; + + fatal ("invalid option `%s'", argv[i]); + } + + /* Now loop over all input files. */ + num_files = 0; + no_more_options = false; + already_read_stdin = false; + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + if (argv[i][1] == '\0') + { + /* Read stdin. */ + if (already_read_stdin) + fatal ("cannot read standard input twice"); + + read_md_file = stdin; + read_md_filename = ""; + handle_toplevel_file (handle_directive); + already_read_stdin = true; + continue; + } + else if (argv[i][1] == '-' && argv[i][2] == '\0') + { + /* No further arguments are to be treated as options. */ + no_more_options = true; + continue; + } + else if (!no_more_options) + continue; + } + + /* If we get here we are looking at a non-option argument, i.e. + a file to be processed. */ + read_md_filename = argv[i]; + read_md_file = fopen (read_md_filename, "r"); + if (read_md_file == 0) + { + perror (read_md_filename); + return false; + } + handle_toplevel_file (handle_directive); + num_files++; + } + + /* If we get to this point without having seen any files to process, + read the standard input now. */ + if (num_files == 0 && !already_read_stdin) + { + read_md_file = stdin; + read_md_filename = ""; + handle_toplevel_file (handle_directive); + } + + return !have_error; +} -- cgit v1.2.3