From 648b616c86e05c74b9ad25ad2bb5e5adf4172619 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 28 Jan 2016 07:24:22 +0200 Subject: Add support for file expansion in prologue/epilogue files This is similar to variable expansion ($name$) except here we detect if the name starts with ./ or ../ and treat it as a path relative to the prologue/ epilogue file. --- cli/context.cxx | 71 +++++++++++++++++++++++++++++++++++++++++++------------ cli/context.hxx | 16 ++++++++++--- cli/generator.cxx | 4 +++- 3 files changed, 72 insertions(+), 19 deletions(-) (limited to 'cli') diff --git a/cli/context.cxx b/cli/context.cxx index f1906c2..c87087b 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -6,6 +6,7 @@ #include #include #include // strncmp() +#include #include #include @@ -1595,11 +1596,11 @@ format (output_type ot, string const& s, bool para) } string context:: -substitute (const string& s, semantics::cli_unit& u) +substitute (const string& s, semantics::cli_unit& u, const path* d) { string r; - // Scan the string looking for variables ($var$). + // Scan the string looking for variables ($var$) or paths. // size_t b (0), e (b); for (size_t n (s.size ()); e != n; ++e) @@ -1613,38 +1614,78 @@ substitute (const string& s, semantics::cli_unit& u) continue; } - // Scan for as long as it is a C identifier. + bool file (false); + + // Scan for as long as it is a C identifier or a file. // size_t p (e + 1); // Position of the second '$'. for (; p != n; ++p) { char c (s[p]); + bool f (p == e + 1); // First. - if (!(c == '_' || - ('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || - (p != e + 1 && '0' <= c && c <= '9'))) - break; + if (d != 0 && f && c == '.' && p + 1 != n && + (s[p + 1] == '/' || // "./" + (s[p + 1] == '.' && p + 2 != n && s[p + 2] == '/'))) // "../" + file = true; + + if (file) + { + if (c == '$') + break; + } + else + { + if (!(c == '_' || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + (!f && '0' <= c && c <= '9'))) + break; + } } // Note: check that the second '$' is not escaped. // if (p != n && s[p] == '$' && (p + 1 == n || s[p + 1] != '$')) { - r.append (s, b, e - b); + r.append (s, b, e - b); // Save what came before the expansion. - // Lookup and substiute the variable. + // Var/file name. // ++e; string v (s, e, p - e); - if (semantics::doc* d = u.lookup ("", "var: " + v)) - r += d->front (); + if (file) + { + path p (*d / path (v)); + p.normalize (); // Apply '.' and '..'. + + ifstream ifs (p.string ().c_str (), ifstream::in | ifstream::binary); + if (!ifs.is_open ()) + { + cerr << p << ": error: unable to open in read mode" << endl; + throw generation_failed (); + } + + // We don't expect our file to contain '\0' so use that as the + // delimiter to read the entire file with getline(). + // + string s; + getline (ifs, s, '\0'); + r += s; + } else { - cerr << "error: undefined variable '" << v << "' in '" << s << "'" - << endl; - throw generation_failed (); + // Lookup and substiute the variable. + // + if (semantics::doc* d = u.lookup ("", "var: " + v)) + r += d->front (); + else + { + cerr << "error: undefined variable '" << v << "' in '" << s << "'" + << endl; + throw generation_failed (); + } } e = p; diff --git a/cli/context.hxx b/cli/context.hxx index f16f8c2..cdc0aa5 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -13,6 +13,7 @@ #include // std::size_t #include +#include #include "options.hxx" #include "semantics.hxx" @@ -44,6 +45,9 @@ public: typedef std::size_t size_t; typedef std::string string; + typedef cutl::fs::path path; + typedef cutl::fs::invalid_path invalid_path; + typedef ::options options_type; typedef ::usage usage_type; typedef ::class_doc class_doc_type; @@ -111,13 +115,19 @@ public: void format_line (output_type, string&, const char*, size_t); - // Substitute doc variable expansions ($var$). + // Substitute doc variable expansions ($var$). Var must be a C identifier. + // If the path is not NULL, then also recognize names that start with either + // ./ or ../ and treat them as files relative to path. Such file expansions + // are substituted with the files' contents. // static string - substitute (const string&, semantics::cli_unit&); + substitute (const string&, semantics::cli_unit&, const path* = 0); string - substitute (const string& s) {return substitute (s, unit);} + substitute (const string& s, const path* p = 0) + { + return substitute (s, unit, p); + } public: static string const& diff --git a/cli/generator.cxx b/cli/generator.cxx index 486c793..5964c25 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -94,12 +94,14 @@ namespace ifstream ifs; open (ifs, file); + path d (path (file).directory ()); + // getline() will set the failbit if it failed to extract anything, // not even the delimiter and eofbit if it reached eof before seeing // the delimiter. // for (string s; getline (ifs, s); ) - os << context::substitute (s, u) << endl; + os << context::substitute (s, u, &d) << endl; } } } -- cgit v1.1