aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/project-name.cxx115
-rw-r--r--libbutl/project-name.mxx223
-rw-r--r--tests/project-name/buildfile8
-rw-r--r--tests/project-name/driver.cxx84
-rw-r--r--tests/project-name/testscript78
-rw-r--r--tests/standard-version/buildfile2
-rw-r--r--tests/standard-version/driver.cxx10
7 files changed, 514 insertions, 6 deletions
diff --git a/libbutl/project-name.cxx b/libbutl/project-name.cxx
new file mode 100644
index 0000000..5f94ea0
--- /dev/null
+++ b/libbutl/project-name.cxx
@@ -0,0 +1,115 @@
+// file : libbutl/project-name.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef __cpp_modules
+#include <libbutl/project-name.mxx>
+#endif
+
+#ifndef __cpp_lib_modules
+#include <string>
+#include <vector>
+#include <utility> // move()
+#include <iterator> // back_inserter
+#include <algorithm> // find(), transform()
+#include <stdexcept> // invalid_argument
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules
+module butl.project_name;
+
+// Only imports additional to interface.
+#ifdef __clang__
+#ifdef __cpp_lib_modules
+import std.core;
+import std.io;
+#endif
+import butl.utility;
+#endif
+
+import butl.path; // path::traits
+import butl.utility; // alpha(), alnum()
+#else
+#include <libbutl/path.mxx>
+#include <libbutl/utility.mxx>
+#endif
+
+using namespace std;
+
+namespace butl
+{
+ // project_name
+ //
+ static const vector<string> illegal_prj_names ({
+ "build",
+ "con", "prn", "aux", "nul",
+ "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9",
+ "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"});
+
+ static const string legal_prj_chars ("_+-.");
+
+ project_name::
+ project_name (std::string&& nm)
+ {
+ if (nm.size () < 2)
+ throw invalid_argument ("length is less than two characters");
+
+ if (find (illegal_prj_names.begin (), illegal_prj_names.end (), nm) !=
+ illegal_prj_names.end ())
+ throw invalid_argument ("illegal name");
+
+ if (!alpha (nm.front ()))
+ throw invalid_argument ("illegal first character (must be alphabetic)");
+
+ // Here we rely on the fact that the name length >= 2.
+ //
+ for (auto i (nm.cbegin () + 1), e (nm.cend () - 1); i != e; ++i)
+ {
+ char c (*i);
+
+ if (!(alnum(c) || legal_prj_chars.find (c) != string::npos))
+ throw invalid_argument ("illegal character");
+ }
+
+ if (!alnum (nm.back ()) && nm.back () != '+')
+ throw invalid_argument (
+ "illegal last character (must be alphabetic, digit, or plus)");
+
+ value_ = move (nm);
+ }
+
+ string project_name::
+ base () const
+ {
+ using std::string;
+
+ size_t p (path::traits::find_extension (value_));
+ return string (value_, 0, p);
+ }
+
+ string project_name::
+ extension () const
+ {
+ using std::string;
+
+ size_t p (path::traits::find_extension (value_));
+ return p != string::npos ? string (value_, p + 1) : string ();
+ }
+
+ string project_name::
+ variable () const
+ {
+ using std::string;
+
+ auto sanitize = [] (char c)
+ {
+ return (c == '-' || c == '+' || c == '.') ? '_' : c;
+ };
+
+ string r;
+ transform (value_.begin (), value_.end (), back_inserter (r), sanitize);
+ return r;
+ }
+}
diff --git a/libbutl/project-name.mxx b/libbutl/project-name.mxx
new file mode 100644
index 0000000..bb92340
--- /dev/null
+++ b/libbutl/project-name.mxx
@@ -0,0 +1,223 @@
+// file : libbutl/project-name.mxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef __cpp_modules
+#pragma once
+#endif
+
+// C includes.
+
+#ifndef __cpp_lib_modules
+#include <string>
+#include <utility> // move()
+#include <ostream>
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules
+export module butl.project_name;
+#ifdef __cpp_lib_modules
+import std.core;
+import std.io;
+#endif
+import butl.utility; // casecmp()
+#else
+#include <libbutl/utility.mxx>
+#endif
+
+#include <libbutl/export.hxx>
+
+LIBBUTL_MODEXPORT namespace butl
+{
+ class LIBBUTL_SYMEXPORT project_name
+ {
+ public:
+ // Create project name from string verifying that it complied with the
+ // specification and throwing std::invalid_argument if that's not the
+ // case. Note that in this case the passed value is guaranteed to be
+ // unchanged.
+ //
+ explicit
+ project_name (const std::string& s): project_name (std::string (s)) {}
+
+ explicit
+ project_name (std::string&&);
+
+ // Create a special empty project name.
+ //
+ project_name () = default;
+
+ // Create an arbitrary string that can be used in contexts that expect
+ // a project name. For example, a project name pattern for use in ODB query
+ // expressions.
+ //
+ enum raw_string_type {raw_string};
+ project_name (std::string s, raw_string_type): value_ (std::move (s)) {}
+
+ bool
+ empty () const noexcept {return value_.empty ();}
+
+ const std::string&
+ string () const& noexcept {return value_;}
+
+ // Moves the underlying project name string out of the project name object.
+ // The object becomes empty. Usage: std::move (name).string ().
+ //
+ std::string
+ string () && {std::string r; r.swap (this->value_); return r;}
+
+ // Project name base and extension (without the dot). If there is no
+ // extension, then the base name is the same as the full name and the
+ // returned extension is empty.
+ //
+ std::string
+ base () const;
+
+ std::string
+ extension () const;
+
+ // Project name sanitized to a canonical variable name. Specifically,
+ // '.', '-', and '+' are replaced with '_'.
+ //
+ std::string
+ variable () const;
+
+ // Compare ignoring case. Note that a string is not checked to be a valid
+ // project name.
+ //
+ int compare (const project_name& n) const {return compare (n.value_);}
+ int compare (const std::string& n) const {return compare (n.c_str ());}
+ int compare (const char* n) const {return butl::casecmp (value_, n);}
+
+ private:
+ std::string value_;
+ };
+
+ inline bool
+ operator< (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) < 0;
+ }
+
+ inline bool
+ operator> (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) > 0;
+ }
+
+ inline bool
+ operator== (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) == 0;
+ }
+
+ inline bool
+ operator<= (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) <= 0;
+ }
+
+ inline bool
+ operator>= (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) >= 0;
+ }
+
+ inline bool
+ operator!= (const project_name& x, const project_name& y)
+ {
+ return x.compare (y) != 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator< (const project_name& x, const T& y)
+ {
+ return x.compare (y) < 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator> (const project_name& x, const T& y)
+ {
+ return x.compare (y) > 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator== (const project_name& x, const T& y)
+ {
+ return x.compare (y) == 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator<= (const project_name& x, const T& y)
+ {
+ return x.compare (y) <= 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator>= (const project_name& x, const T& y)
+ {
+ return x.compare (y) >= 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator!= (const project_name& x, const T& y)
+ {
+ return x.compare (y) != 0;
+ }
+
+ template <typename T>
+ inline auto
+ operator< (const T& x, const project_name& y)
+ {
+ return y > x;
+ }
+
+ template <typename T>
+ inline auto
+ operator> (const T& x, const project_name& y)
+ {
+ return y < x;
+ }
+
+ template <typename T>
+ inline auto
+ operator== (const T& x, const project_name& y)
+ {
+ return y == x;
+ }
+
+ template <typename T>
+ inline auto
+ operator<= (const T& x, const project_name& y)
+ {
+ return y >= x;
+ }
+
+ template <typename T>
+ inline auto
+ operator>= (const T& x, const project_name& y)
+ {
+ return y <= x;
+ }
+
+ template <typename T>
+ inline auto
+ operator!= (const T& x, const project_name& y)
+ {
+ return y != x;
+ }
+
+ inline std::ostream&
+ operator<< (std::ostream& os, const project_name& v)
+ {
+ return os << v.string ();
+ }
+}
diff --git a/tests/project-name/buildfile b/tests/project-name/buildfile
new file mode 100644
index 0000000..3e16fe8
--- /dev/null
+++ b/tests/project-name/buildfile
@@ -0,0 +1,8 @@
+# file : tests/project-name/buildfile
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libbutl%lib{butl}
+libs += $stdmod_lib
+
+exe{driver}: {hxx cxx}{*} $libs testscript
diff --git a/tests/project-name/driver.cxx b/tests/project-name/driver.cxx
new file mode 100644
index 0000000..51c8782
--- /dev/null
+++ b/tests/project-name/driver.cxx
@@ -0,0 +1,84 @@
+// file : tests/project-name/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <cassert>
+
+#ifndef __cpp_lib_modules
+#include <ios> // ios::*bit
+#include <string>
+#include <iostream>
+#include <stdexcept> // invalid_argument
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules
+#ifdef __cpp_lib_modules
+import std.core;
+import std.io;
+#endif
+import butl.utility; // operator<<(ostream,exception), eof(), *case()
+import butl.project_name;
+#else
+#include <libbutl/utility.mxx>
+#include <libbutl/project-name.mxx>
+#endif
+
+using namespace std;
+using namespace butl;
+
+// Create project_name from string and also perform some tests for the created
+// object.
+//
+static project_name
+name (const string& s)
+{
+ project_name r (s);
+
+ assert (r == project_name (lcase (s)));
+ assert (r == project_name (ucase (s)));
+
+ assert (r > project_name ("!", project_name::raw_string));
+ assert (r < project_name ("~", project_name::raw_string));
+
+ return r;
+}
+
+// Usage: argv[0] (string|base|extension|variable)
+//
+// Create project names from stdin lines, and for each of them print the
+// result of the specified member function to stdout, one per line.
+//
+int
+main (int argc, char* argv[])
+try
+{
+ assert (argc == 2);
+
+ string m (argv[1]);
+ assert (m == "string" || m == "base" || m == "extension" || m == "variable");
+
+ cin.exceptions (ios::badbit);
+ cout.exceptions (ios::failbit | ios::badbit);
+
+ string l;
+ while (!eof (getline (cin, l)))
+ {
+ project_name n (name (l));
+
+ const string& s (m == "string" ? n.string () :
+ m == "base" ? n.base () :
+ m == "extension" ? n.extension () :
+ n.variable ());
+
+ cout << s << endl;
+ }
+
+ return 0;
+}
+catch (const invalid_argument& e)
+{
+ cerr << e << endl;
+ return 1;
+}
diff --git a/tests/project-name/testscript b/tests/project-name/testscript
new file mode 100644
index 0000000..92bd5e8
--- /dev/null
+++ b/tests/project-name/testscript
@@ -0,0 +1,78 @@
+# file : tests/project-name/testscript
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+: string
+{
+ test.arguments += 'string'
+
+ : valid
+ :
+ $* <<EOF >>EOF
+ foo
+ foo1
+ Foo
+ foo_bar
+ foo+bar
+ foo-bar
+ foo.bar
+ build2
+ EOF
+
+ $* <'a' 2>'length is less than two characters' != 0: short
+ $* <'nul' 2>'illegal name' != 0: illegal-name
+ $* <'1a' 2>'illegal first character (must be alphabetic)' != 0: illegal-first-char
+ $* <'a!b' 2>'illegal character' != 0: illegal-char
+
+ : illegal-last-char
+ :
+ $* <'a.' 2>'illegal last character (must be alphabetic, digit, or plus)' != 0
+}
+
+: base
+:
+{
+ test.arguments += 'base';
+
+ $* <<EOI >>EOO
+ libbutl
+ libbutl.bash
+ a.b.c
+ EOI
+ libbutl
+ libbutl
+ a.b
+ EOO
+}
+
+: extension
+:
+{
+ test.arguments += 'extension';
+
+ $* <<EOI >>EOO
+ libbutl
+ libbutl.bash
+ a.b.c
+ EOI
+
+ bash
+ c
+ EOO
+}
+
+: variable
+:
+{
+ test.arguments += 'variable';
+
+ $* <<EOI >>EOO
+ foo-bar
+ libc++
+ libbutl.bash
+ EOI
+ foo_bar
+ libc__
+ libbutl_bash
+ EOO
+}
diff --git a/tests/standard-version/buildfile b/tests/standard-version/buildfile
index 8e8ded6..7ae054e 100644
--- a/tests/standard-version/buildfile
+++ b/tests/standard-version/buildfile
@@ -1,4 +1,4 @@
-# file : tests/tab-parser/buildfile
+# file : tests/standard-version/buildfile
# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
diff --git a/tests/standard-version/driver.cxx b/tests/standard-version/driver.cxx
index 3bc1d31..7ad5b00 100644
--- a/tests/standard-version/driver.cxx
+++ b/tests/standard-version/driver.cxx
@@ -18,7 +18,7 @@
import std.core;
import std.io;
#endif
-import butl.utility; // operator<<(ostream,exception)
+import butl.utility; // operator<<(ostream,exception), eof()
import butl.standard_version;
#else
#include <libbutl/utility.mxx>
@@ -124,11 +124,11 @@ version (const string& s,
// -a output 'y' for alpha-version, 'n' otherwise
// -b output 'y' for beta-version, 'n' otherwise
// -c output 0 if versions are equal, -1 if the first one is less, 1 otherwise
-// -r create version constraints from STDIN lines, and print them to STDOUT
+// -r create version constraints from stdin lines, and print them to stdout
// -s output 'y' if version satisfies constraint, 'n' otherwise
//
-// If no options are specified, then create versions from STDIN lines, and
-// print them to STDOUT.
+// If no options are specified, then create versions from stdin lines, and
+// print them to stdout.
//
int
main (int argc, char* argv[])
@@ -186,7 +186,7 @@ try
}
string s;
- while (getline (cin, s))
+ while (!eof (getline (cin, s)))
cout << (s.empty () ? standard_version () : version (s)) << endl;
return 0;