+/* Each input_file has its associated output file outf_p. The
+ association is computed by the function
+ get_output_file_with_visibility. The associated file is cached
+ inside input_file in its inpoutf field, so is really computed only
+ once. Associated output file paths (i.e. output_name-s) are
+ computed by a rule based regexp machinery, using the files_rules
+ array of struct file_rule_st. A for_name is also computed, giving
+ the source file name for which the output_file is generated; it is
+ often the last component of the input_file path. */
+
+
+/*
+ Regexpr machinery to compute the output_name and for_name-s of each
+ input_file. We have a sequence of file rules which gives the POSIX
+ extended regular expression to match an input file path, and two
+ transformed strings for the corresponding output_name and the
+ corresponding for_name. The transformed string contain dollars: $0
+ is replaced by the entire match, $1 is replaced by the substring
+ matching the first parenthesis in the regexp, etc. And $$ is replaced
+ by a single verbatim dollar. The rule order is important. The
+ general case is last, and the particular cases should come before.
+ An action routine can, when needed, update the out_name & for_name
+ and/or return the appropriate output file. It is invoked only when a
+ rule is triggered. When a rule is triggered, the output_name and
+ for_name are computed using their transform string in while $$, $0,
+ $1, ... are suitably replaced. If there is an action, it is called.
+ In some few cases, the action can directly return the outf_p, but
+ usually it just updates the output_name and for_name so should free
+ them before replacing them. The get_output_file_with_visibility
+ function creates an outf_p only once per each output_name, so it
+ scans the output_files list for previously seen output file names.
+ */
+
+/* Signature of actions in file rules. */
+typedef outf_p (frul_actionrout_t) (input_file*, char**, char**);
+
+
+struct file_rule_st {
+ const char* frul_srcexpr; /* Source string for regexp. */
+ int frul_rflags; /* Flags passed to regcomp, usually
+ * REG_EXTENDED. */
+ regex_t* frul_re; /* Compiled regular expression
+ obtained by regcomp. */
+ const char* frul_tr_out; /* Transformation string for making
+ * the output_name, with $1 ... $9 for
+ * subpatterns and $0 for the whole
+ * matched filename. */
+ const char* frul_tr_for; /* Tranformation string for making the
+ for_name. */
+ frul_actionrout_t* frul_action; /* The action, if non null, is
+ * called once the rule matches, on
+ * the transformed out_name &
+ * for_name. It could change them
+ * and/or give the output file. */
+};
+
+/* File rule action handling *.h files. */
+static outf_p header_dot_h_frul (input_file*, char**, char**);
+
+/* File rule action handling *.c files. */
+static outf_p source_dot_c_frul (input_file*, char**, char**);
+
+#define NULL_REGEX (regex_t*)0
+
+/* The prefix in our regexp-s matching the directory. */
+#define DIR_PREFIX_REGEX "^(([^/]*/)*)"
+
+#define NULL_FRULACT (frul_actionrout_t*)0
+
+/* The array of our rules governing file name generation. Rules order
+ matters, so change with extreme care! */
+
+struct file_rule_st files_rules[] = {
+ /* The general rule assumes that files in subdirectories belong to a
+ particular front-end, and files not in subdirectories are shared.
+ The following rules deal with exceptions - files that are in
+ subdirectories and yet are shared, and files that are top-level,
+ but are not shared. */
+
+ /* the c-family/ source directory is special. */
+ { DIR_PREFIX_REGEX "c-family/([[:alnum:]_-]*)\\.c$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-c-family-$3.h", "c-family/$3.c", NULL_FRULACT},
+
+ { DIR_PREFIX_REGEX "c-family/([[:alnum:]_-]*)\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-c-family-$3.h", "c-family/$3.h", NULL_FRULACT},
+
+ /* Both c-lang.h & c-tree.h gives gt-c-decl.h for c-decl.c ! */
+ { DIR_PREFIX_REGEX "c-lang\\.h$",
+ REG_EXTENDED, NULL_REGEX, "gt-c-decl.h", "c-decl.c", NULL_FRULACT},
+
+ { DIR_PREFIX_REGEX "c-tree\\.h$",
+ REG_EXTENDED, NULL_REGEX, "gt-c-decl.h", "c-decl.c", NULL_FRULACT},
+
+ /* cp/cp-tree.h gives gt-cp-tree.h for cp/tree.c ! */
+ { DIR_PREFIX_REGEX "cp/cp-tree\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-cp-tree.h", "cp/tree.c", NULL_FRULACT },
+
+ /* cp/decl.h & cp/decl.c gives gt-cp-decl.h for cp/decl.c ! */
+ { DIR_PREFIX_REGEX "cp/decl\\.[ch]$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-cp-decl.h", "cp/decl.c", NULL_FRULACT },
+
+ /* cp/name-lookup.h gives gt-cp-name-lookup.h for cp/name-lookup.c ! */
+ { DIR_PREFIX_REGEX "cp/name-lookup\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-cp-name-lookup.h", "cp/name-lookup.c", NULL_FRULACT },
+
+ /* cp/parser.h gives gt-cp-parser.h for cp/parser.c ! */
+ { DIR_PREFIX_REGEX "cp/parser\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-cp-parser.h", "cp/parser.c", NULL_FRULACT },
+
+ /* objc/objc-act.h gives gt-objc-objc-act.h for objc/objc-act.c ! */
+ { DIR_PREFIX_REGEX "objc/objc-act\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-objc-objc-act.h", "objc/objc-act.c", NULL_FRULACT },
+
+ /* objc/objc-map.h gives gt-objc-objc-map.h for objc/objc-map.c ! */
+ { DIR_PREFIX_REGEX "objc/objc-map\\.h$",
+ REG_EXTENDED, NULL_REGEX,
+ "gt-objc-objc-map.h", "objc/objc-map.c", NULL_FRULACT },
+
+ /* General cases. For header *.h and source *.c files, we need
+ * special actions to handle the language. */
+
+ /* Source *.c files are using get_file_gtfilename to compute their
+ output_name and get_file_basename to compute their for_name
+ thru the source_dot_c_frul action. */
+ { DIR_PREFIX_REGEX "([[:alnum:]_-]*)\\.c$",
+ REG_EXTENDED, NULL_REGEX, "gt-$3.h", "$3.c", source_dot_c_frul},
+ /* Common header files get "gtype-desc.c" as their output_name,
+ * while language specific header files are handled specially. So
+ * we need the header_dot_h_frul action. */
+ { DIR_PREFIX_REGEX "([[:alnum:]_-]*)\\.h$",
+ REG_EXTENDED, NULL_REGEX, "gt-$3.h", "$3.h", header_dot_h_frul},
+
+ { DIR_PREFIX_REGEX "([[:alnum:]_-]*)\\.in$",
+ REG_EXTENDED, NULL_REGEX, "gt-$3.h", "$3.in", NULL_FRULACT},
+
+ /* Mandatory null last entry signaling end of rules. */
+ {NULL, 0, NULL_REGEX, NULL, NULL, NULL_FRULACT}
+};
+
+/* Special file rules action for handling *.h header files. It gives
+ "gtype-desc.c" for common headers and corresponding output
+ files for language-specific header files. */
+static outf_p
+header_dot_h_frul (input_file* inpf, char**poutname,
+ char**pforname ATTRIBUTE_UNUSED)
+{
+ const char *basename = 0;
+ int lang_index = 0;
+ DBGPRINTF ("inpf %p inpname %s outname %s forname %s",
+ (void*) inpf, get_input_file_name (inpf),
+ *poutname, *pforname);
+ basename = get_file_basename (inpf);
+ lang_index = get_prefix_langdir_index (basename);
+ DBGPRINTF ("basename %s lang_index %d", basename, lang_index);
+
+ if (lang_index >= 0)
+ {
+ /* The header is language specific. Given output_name &
+ for_name remains unchanged. The base_files array gives the
+ outf_p. */
+ DBGPRINTF ("header_dot_h found language specific @ %p '%s'",
+ (void*) base_files[lang_index],
+ (base_files[lang_index])->name);
+ return base_files[lang_index];
+ }
+ else
+ {
+ /* The header is common to all front-end languages. So
+ output_name is "gtype-desc.c" file. The calling function
+ get_output_file_with_visibility will find its outf_p. */
+ free (*poutname);
+ *poutname = xstrdup ("gtype-desc.c");
+ DBGPRINTF ("special 'gtype-desc.c' for inpname %s",
+ get_input_file_name (inpf));
+ return NULL;
+ }
+}
+
+
+/* Special file rules action for handling *.c source files using
+ * get_file_gtfilename to compute their output_name and
+ * get_file_basename to compute their for_name. The output_name is
+ * gt-<LANG>-<BASE>.h for language specific source files, and
+ * gt-<BASE>.h for common source files. */
+static outf_p
+source_dot_c_frul (input_file* inpf, char**poutname, char**pforname)
+{
+ char *newbasename = CONST_CAST (char*, get_file_basename (inpf));
+ char *newoutname = CONST_CAST (char*, get_file_gtfilename (inpf));
+ DBGPRINTF ("inpf %p inpname %s original outname %s forname %s",
+ (void*) inpf, get_input_file_name (inpf),
+ *poutname, *pforname);
+ DBGPRINTF ("newoutname %s", newoutname);
+ DBGPRINTF ("newbasename %s", newbasename);
+ free (*poutname);
+ free (*pforname);
+ *poutname = newoutname;
+ *pforname = newbasename;
+ return NULL;
+}
+
+/* Utility function for get_output_file_with_visibility which returns
+ * a malloc-ed substituted string using TRS on matching of the FILNAM
+ * file name, using the PMATCH array. */
+static char*
+matching_file_name_substitute (const char *filnam, regmatch_t pmatch[10],
+ const char *trs)
+{
+ struct obstack str_obstack;
+ char *str = NULL;
+ char *rawstr = NULL;
+ const char *pt = NULL;
+ DBGPRINTF ("filnam %s", filnam);
+ obstack_init (&str_obstack);
+ for (pt = trs; *pt; pt++) {
+ char c = *pt;
+ if (c == '$')
+ {
+ if (pt[1] == '$')
+ {
+ /* A double dollar $$ is substituted by a single verbatim
+ dollar, but who really uses dollar signs in file
+ paths? */
+ obstack_1grow (&str_obstack, '$');
+ }
+ else if (ISDIGIT (pt[1]))
+ {
+ /* Handle $0 $1 ... $9 by appropriate substitution. */
+ int dolnum = pt[1] - '0';
+ int so = pmatch[dolnum].rm_so;
+ int eo = pmatch[dolnum].rm_eo;
+ DBGPRINTF ("so=%d eo=%d dolnum=%d", so, eo, dolnum);
+ if (so>=0 && eo>=so)
+ obstack_grow (&str_obstack, filnam + so, eo - so);
+ }
+ else
+ {
+ /* This can happen only when files_rules is buggy! */
+ gcc_unreachable();
+ }
+ /* Always skip the character after the dollar. */
+ pt++;
+ }
+ else
+ obstack_1grow (&str_obstack, c);
+ }
+ obstack_1grow (&str_obstack, '\0');
+ rawstr = XOBFINISH (&str_obstack, char *);
+ str = xstrdup (rawstr);
+ obstack_free (&str_obstack, NULL);
+ DBGPRINTF ("matched replacement %s", str);
+ rawstr = NULL;
+ return str;
+}
+
+