aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/nixexpr.cc3
-rw-r--r--src/libexpr/print-ambiguous.cc3
-rw-r--r--src/libexpr/print.cc66
-rw-r--r--src/libexpr/print.hh17
-rw-r--r--src/libutil/escape-string.cc40
-rw-r--r--src/libutil/escape-string.hh34
-rw-r--r--src/libutil/meson.build4
-rw-r--r--src/libutil/print-elided.cc23
-rw-r--r--src/libutil/print-elided.hh23
-rw-r--r--tests/unit/libutil-support/tests/cli-literate-parser.cc3
10 files changed, 142 insertions, 74 deletions
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index fd380935a..72a8764e6 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -4,6 +4,7 @@
#include "symbol-table.hh"
#include "util.hh"
#include "print.hh"
+#include "escape-string.hh"
#include <cstdlib>
@@ -36,7 +37,7 @@ void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{
- printLiteralString(str, s);
+ escapeString(str, s);
}
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
diff --git a/src/libexpr/print-ambiguous.cc b/src/libexpr/print-ambiguous.cc
index 3f3c2c45a..ec30f5073 100644
--- a/src/libexpr/print-ambiguous.cc
+++ b/src/libexpr/print-ambiguous.cc
@@ -2,6 +2,7 @@
#include "print.hh"
#include "eval.hh"
#include "signals.hh"
+#include "escape-string.hh"
namespace nix {
@@ -27,7 +28,7 @@ void printAmbiguous(
printLiteralBool(str, v.boolean);
break;
case nString:
- printLiteralString(str, v.string.s);
+ escapeString(str, v.string.s);
break;
case nPath:
str << v.path().to_string(); // !!! escaping?
diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc
index 43e366d08..e5e6b9b21 100644
--- a/src/libexpr/print.cc
+++ b/src/libexpr/print.cc
@@ -2,6 +2,7 @@
#include <span>
#include <unordered_set>
+#include "escape-string.hh"
#include "print.hh"
#include "ansicolor.hh"
#include "store-api.hh"
@@ -11,57 +12,6 @@
namespace nix {
-void printElided(
- std::ostream & output,
- unsigned int value,
- const std::string_view single,
- const std::string_view plural,
- bool ansiColors)
-{
- if (ansiColors)
- output << ANSI_FAINT;
- output << "«";
- pluralize(output, value, single, plural);
- output << " elided»";
- if (ansiColors)
- output << ANSI_NORMAL;
-}
-
-
-std::ostream &
-printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
-{
- size_t charsPrinted = 0;
- if (ansiColors)
- str << ANSI_MAGENTA;
- str << "\"";
- for (auto i = string.begin(); i != string.end(); ++i) {
- if (charsPrinted >= maxLength) {
- str << "\" ";
- printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
- return str;
- }
-
- if (*i == '\"' || *i == '\\') str << "\\" << *i;
- else if (*i == '\n') str << "\\n";
- else if (*i == '\r') str << "\\r";
- else if (*i == '\t') str << "\\t";
- else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
- else str << *i;
- charsPrinted++;
- }
- str << "\"";
- if (ansiColors)
- str << ANSI_NORMAL;
- return str;
-}
-
-std::ostream &
-printLiteralString(std::ostream & str, const std::string_view string)
-{
- return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
-}
-
std::ostream &
printLiteralBool(std::ostream & str, bool boolean)
{
@@ -93,7 +43,7 @@ printIdentifier(std::ostream & str, std::string_view s) {
else {
char c = s[0];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
- printLiteralString(str, s);
+ escapeString(str, s);
return str;
}
for (auto c : s)
@@ -101,7 +51,7 @@ printIdentifier(std::ostream & str, std::string_view s) {
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '_' || c == '\'' || c == '-')) {
- printLiteralString(str, s);
+ escapeString(str, s);
return str;
}
str << s;
@@ -129,7 +79,7 @@ printAttributeName(std::ostream & str, std::string_view name) {
if (isVarName(name))
str << name;
else
- printLiteralString(str, name);
+ escapeString(str, name);
return str;
}
@@ -248,7 +198,13 @@ private:
void printString(Value & v)
{
- printLiteralString(output, v.string.s, options.maxStringLength, options.ansiColors);
+ // NB: Non-printing characters won't be escaped.
+ escapeString(
+ output,
+ v.string.s,
+ options.maxStringLength,
+ options.ansiColors
+ );
}
void printPath(Value & v)
diff --git a/src/libexpr/print.hh b/src/libexpr/print.hh
index 94cb11ca7..42826d94d 100644
--- a/src/libexpr/print.hh
+++ b/src/libexpr/print.hh
@@ -11,28 +11,13 @@
#include "fmt.hh"
#include "print-options.hh"
+#include "print-elided.hh"
namespace nix {
class EvalState;
struct Value;
-/**
- * Print a string as a Nix string literal.
- *
- * Quotes and fairly minimal escaping are added.
- *
- * @param o The output stream to print to
- * @param s The logical string
- */
-std::ostream & printLiteralString(std::ostream & o, std::string_view s);
-inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
- return printLiteralString(o, std::string_view(s));
-}
-inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
- return printLiteralString(o, std::string_view(s));
-}
-
/** Print `true` or `false`. */
std::ostream & printLiteralBool(std::ostream & o, bool b);
diff --git a/src/libutil/escape-string.cc b/src/libutil/escape-string.cc
new file mode 100644
index 000000000..8160403cd
--- /dev/null
+++ b/src/libutil/escape-string.cc
@@ -0,0 +1,40 @@
+#include <iomanip>
+#include <ostream>
+#include <sstream>
+
+#include "ansicolor.hh"
+#include "escape-char.hh"
+#include "english.hh"
+#include "escape-string.hh"
+#include "print-elided.hh"
+
+namespace nix {
+
+std::ostream &
+escapeString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
+{
+ size_t charsPrinted = 0;
+ if (ansiColors)
+ str << ANSI_MAGENTA;
+ str << "\"";
+ for (auto i = string.begin(); i != string.end(); ++i) {
+ if (charsPrinted >= maxLength) {
+ str << "\" ";
+ printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
+ return str;
+ }
+ if (*i == '\"' || *i == '\\') str << "\\" << *i;
+ else if (*i == '\n') str << "\\n";
+ else if (*i == '\r') str << "\\r";
+ else if (*i == '\t') str << "\\t";
+ else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
+ else str << *i;
+ charsPrinted++;
+ }
+ str << "\"";
+ if (ansiColors)
+ str << ANSI_NORMAL;
+ return str;
+}
+
+}; // namespace nix
diff --git a/src/libutil/escape-string.hh b/src/libutil/escape-string.hh
new file mode 100644
index 000000000..28c6c8d64
--- /dev/null
+++ b/src/libutil/escape-string.hh
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <limits>
+#include <ostream>
+
+namespace nix {
+
+/**
+ * Escape a string for output.
+ *
+ * With default optional parameters, the output string will round-trip through
+ * the Nix evaluator (i.e. you can copy/paste this function's output into the
+ * REPL and have it evaluate as the string that got passed in).
+ *
+ * With non-default optional parameters, the output string will be
+ * human-readable.
+ */
+
+std::ostream & escapeString(
+ std::ostream & output,
+ const std::string_view string,
+ size_t maxLength = std::numeric_limits<size_t>::max(),
+ bool ansiColors = false
+);
+
+/**
+ * Escape a string for output, writing the escaped result to a new string.
+ */
+inline std::ostream & escapeString(std::ostream & output, const char * string)
+{
+ return escapeString(output, std::string_view(string));
+}
+
+} // namespace nix
diff --git a/src/libutil/meson.build b/src/libutil/meson.build
index ade925bcf..11bf97ee7 100644
--- a/src/libutil/meson.build
+++ b/src/libutil/meson.build
@@ -9,6 +9,7 @@ libutil_sources = files(
'english.cc',
'error.cc',
'escape-char.cc',
+ 'escape-string.cc',
'exit.cc',
'experimental-features.cc',
'filesystem.cc',
@@ -19,6 +20,7 @@ libutil_sources = files(
'logging.cc',
'namespaces.cc',
'position.cc',
+ 'print-elided.cc',
'references.cc',
'serialise.cc',
'shlex.cc',
@@ -52,6 +54,7 @@ libutil_headers = files(
'english.hh',
'error.hh',
'escape-char.hh',
+ 'escape-string.hh',
'exit.hh',
'experimental-features.hh',
'experimental-features-json.hh',
@@ -70,6 +73,7 @@ libutil_headers = files(
'namespaces.hh',
'pool.hh',
'position.hh',
+ 'print-elided.hh',
'ref.hh',
'references.hh',
'regex-combinators.hh',
diff --git a/src/libutil/print-elided.cc b/src/libutil/print-elided.cc
new file mode 100644
index 000000000..75357e5bc
--- /dev/null
+++ b/src/libutil/print-elided.cc
@@ -0,0 +1,23 @@
+#include "print-elided.hh"
+#include "ansicolor.hh"
+#include "english.hh"
+
+namespace nix {
+
+void printElided(
+ std::ostream & output,
+ unsigned int value,
+ const std::string_view single,
+ const std::string_view plural,
+ bool ansiColors)
+{
+ if (ansiColors)
+ output << ANSI_FAINT;
+ output << "«";
+ pluralize(output, value, single, plural);
+ output << " elided»";
+ if (ansiColors)
+ output << ANSI_NORMAL;
+}
+
+}
diff --git a/src/libutil/print-elided.hh b/src/libutil/print-elided.hh
new file mode 100644
index 000000000..a99b15ca1
--- /dev/null
+++ b/src/libutil/print-elided.hh
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <ostream>
+
+
+namespace nix {
+
+/**
+ * Print an `«... elided»` placeholder.
+ *
+ * Arguments are forwarded to `pluralize`.
+ *
+ * If `ansiColors` is set, the output will be wrapped in `ANSI_FAINT`.
+ */
+void printElided(
+ std::ostream & output,
+ unsigned int value,
+ const std::string_view single,
+ const std::string_view plural,
+ bool ansiColors
+);
+
+}
diff --git a/tests/unit/libutil-support/tests/cli-literate-parser.cc b/tests/unit/libutil-support/tests/cli-literate-parser.cc
index 08ebddebc..4edf434be 100644
--- a/tests/unit/libutil-support/tests/cli-literate-parser.cc
+++ b/tests/unit/libutil-support/tests/cli-literate-parser.cc
@@ -1,4 +1,5 @@
#include "cli-literate-parser.hh"
+#include "escape-string.hh"
#include "libexpr/print.hh"
#include "escape-char.hh"
#include "types.hh"
@@ -41,7 +42,7 @@ auto CLILiterateParser::Node::print() const -> std::string
s << "Output ";
break;
}
- printLiteralString(s, this->text);
+ escapeString(s, this->text);
return s.str();
}