From da74f00f7918d7dbd22641061752947b3909bd3c Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 25 Aug 2024 23:03:25 +0200 Subject: [PATCH] Add first implementation attempt --- .editorconfig | 16 ++ .gitattributes | 1 + .gitignore | 32 +++ Doxyfile.in | 404 ++++++++++++++++++++++++++++++++++++ LICENSE | 19 ++ arc/uri/.gitignore | 9 + arc/uri/buildfile | 65 ++++++ arc/uri/export.hxx | 32 +++ arc/uri/grammar.hxx | 133 ++++++++++++ arc/uri/uri.cxx | 284 +++++++++++++++++++++++++ arc/uri/uri.hxx | 122 +++++++++++ arc/uri/uri.txx | 242 +++++++++++++++++++++ arc/uri/version.hxx.in | 37 ++++ build/.gitignore | 4 + build/bootstrap.build | 7 + build/export.build | 6 + build/root.build | 16 ++ buildfile | 30 +++ docs/introduction.md | 39 ++++ manifest | 11 + repositories.manifest | 7 + tests/.gitignore | 8 + tests/build/.gitignore | 4 + tests/build/bootstrap.build | 5 + tests/build/root.build | 16 ++ tests/buildfile | 1 + 26 files changed, 1550 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Doxyfile.in create mode 100644 LICENSE create mode 100644 arc/uri/.gitignore create mode 100644 arc/uri/buildfile create mode 100644 arc/uri/export.hxx create mode 100644 arc/uri/grammar.hxx create mode 100644 arc/uri/uri.cxx create mode 100644 arc/uri/uri.hxx create mode 100644 arc/uri/uri.txx create mode 100644 arc/uri/version.hxx.in create mode 100644 build/.gitignore create mode 100644 build/bootstrap.build create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 buildfile create mode 100644 docs/introduction.md create mode 100644 manifest create mode 100644 repositories.manifest create mode 100644 tests/.gitignore create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0928329 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +[*.md] +indent_style = space +indent_size = 4 +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c19e5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.bdep/ +Doxyfile + +# Local default options files. +# +.build2/local/ + +# Compiler/linker output. +# +*.d +*.t +*.i +*.i.* +*.ii +*.ii.* +*.o +*.obj +*.gcm +*.pcm +*.ifc +*.so +*.dylib +*.dll +*.a +*.lib +*.exp +*.pdb +*.ilk +*.exe +*.exe.dlls/ +*.exe.manifest +*.pc diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..18d9cf9 --- /dev/null +++ b/Doxyfile.in @@ -0,0 +1,404 @@ +# Doxyfile 1.10.0 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "@project@" +PROJECT_NUMBER = "@version@" +PROJECT_BRIEF = "@SUMMARY@" +PROJECT_LOGO = +PROJECT_ICON = +OUTPUT_DIRECTORY = @out_root@/docs/api-reference +CREATE_SUBDIRS = YES +CREATE_SUBDIRS_LEVEL = 8 +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = YES +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@src_root@" +STRIP_FROM_INC_PATH = "@src_root@" +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +PYTHON_DOCSTRING = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +MARKDOWN_ID_STYLE = DOXYGEN +AUTOLINK_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +NUM_PROC_THREADS = 1 +TIMESTAMP = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = YES +RESOLVE_UNNAMED_PARAMS = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = SYSTEM +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_HEADERFILE = YES +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_IF_INCOMPLETE_DOC = YES +WARN_NO_PARAMDOC = NO +WARN_IF_UNDOC_ENUM_VAL = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LINE_FORMAT = "at line $line of file $file" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = @src_root@/README.md \ + @src_root@/arc +INPUT_ENCODING = UTF-8 +INPUT_FILE_ENCODING = +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.txx \ + *.ice +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = @src_root@/docs/introduction.md +FORTRAN_COMMENT_AFTER = 72 +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE = LIGHT +#HTML_COLORSTYLE_HUE = 327 +#HTML_COLORSTYLE_SAT = 89 +#HTML_COLORSTYLE_GAMMA = 65 +HTML_COLORSTYLE_HUE = 204 +HTML_COLORSTYLE_SAT = 60 +HTML_COLORSTYLE_GAMMA = 100 +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_CODE_FOLDING = YES +HTML_COPY_CLIPBOARD = YES +HTML_PROJECT_COOKIE = +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDURL = +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +SITEMAP_URL = +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +FULL_SIDEBAR = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +OBFUSCATE_EMAILS = NO +HTML_FORMULA_FORMAT = png +FORMULA_FONTSIZE = 10 +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_VERSION = MathJax_2 +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_BIB_STYLE = plain +LATEX_EMOJI_DIRECTORY = +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- +GENERATE_SQLITE3 = NO +SQLITE3_OUTPUT = sqlite3 +SQLITE3_RECREATE_DB = YES +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = "@src_root@" +INCLUDE_FILE_PATTERNS = +PREDEFINED = LIBARC_URI_SYMEXPORT +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to diagram generator tools +#--------------------------------------------------------------------------- +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +DOT_UML_DETAILS = NO +DOT_WRAP_THRESHOLD = 17 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DIR_GRAPH_MAX_DEPTH = 1 +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +DIA_PATH = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +MSCGEN_TOOL = +MSCFILE_DIRS = diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f42eda8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Arc Project Software License, version 1.0, June 2nd 2024 + +Permission to use, copy, modify, distribute, and sell this software +(the "Software") and its documentation for any purpose is hereby +granted without fee, provided that the copyright notices in the +Software and this license appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGMENT, +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONJUNCTION WITH THE +SOFTWARE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name(s) of copyright holders +of the Software shall not be used in advertising or otherwise to +promote the sale, use or other dealing in this Software without prior +written authorization from the copyright holders. diff --git a/arc/uri/.gitignore b/arc/uri/.gitignore new file mode 100644 index 0000000..b1ed0e0 --- /dev/null +++ b/arc/uri/.gitignore @@ -0,0 +1,9 @@ +# Generated version header. +# +version.hxx + +# Unit test executables and Testscript output directories +# (can be symlinks). +# +*.test +test-*.test diff --git a/arc/uri/buildfile b/arc/uri/buildfile new file mode 100644 index 0000000..a780443 --- /dev/null +++ b/arc/uri/buildfile @@ -0,0 +1,65 @@ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. + +./: lib{arc-uri}: libul{arc-uri} + +libul{arc-uri}: {hxx ixx txx cxx}{** -**.test... -version} \ + {hxx }{ version} + +libul{arc-uri}: $impl_libs $intf_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libul{arc-uri}: bin.whole = false +} + +hxx{version}: in{version} $src_root/manifest + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" +cxx.coptions =+ -fvisibility=hidden + +obja{*}: cxx.poptions += -DLIBARC_URI_STATIC_BUILD +objs{*}: cxx.poptions += -DLIBARC_URI_SHARED_BUILD + +# Export options. +# +lib{arc-uri}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $intf_libs +} + +liba{arc-uri}: cxx.export.poptions += -DLIBARC_URI_STATIC +libs{arc-uri}: cxx.export.poptions += -DLIBARC_URI_SHARED + +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{arc-uri}: bin.lib.version = "-$version.project_id" +else + lib{arc-uri}: bin.lib.version = "-$version.major.$version.minor" + +# Install into the arc/uri/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/arc/uri/ + install.subdirs = true +} diff --git a/arc/uri/export.hxx b/arc/uri/export.hxx new file mode 100644 index 0000000..a0a34da --- /dev/null +++ b/arc/uri/export.hxx @@ -0,0 +1,32 @@ +#ifndef arc__uri__export_hxx_ +#define arc__uri__export_hxx_ + +#if defined _WIN32 || defined __CYGWIN__ +# define LIBARC_URI_SO_IMPORT __declspec(dllimport) +# define LIBARC_URI_SO_EXPORT __declspec(dllexport) +# define LIBARC_URI_SO_LOCAL +#else +# if __GNUC__ >= 4 +# define LIBARC_URI_SO_IMPORT __attribute__ ((visibility ("default"))) +# define LIBARC_URI_SO_EXPORT __attribute__ ((visibility ("default"))) +# define LIBARC_URI_SO_LOCAL __attribute__ ((visibility ("hidden"))) +# else +# define LIBARC_URI_SO_IMPORT +# define LIBARC_URI_SO_EXPORT +# define LIBARC_URI_SO_LOCAL +# endif +#endif + +#if defined(LIBARC_URI_STATIC) // Using static. +# define LIBARC_URI_SYMEXPORT +#elif defined(LIBARC_URI_STATIC_BUILD) // Building static. +# define LIBARC_URI_SYMEXPORT +#elif defined(LIBARC_URI_SHARED) // Using shared. +# define LIBARC_URI_SYMEXPORT LIBARC_URI_SO_IMPORT +#elif defined(LIBARC_URI_SHARED_BUILD) // Building shared. +# define LIBARC_URI_SYMEXPORT LIBARC_URI_SO_EXPORT +#else +# error define LIBARC_URI_STATIC or LIBARC_URI_SHARED +#endif + +#endif diff --git a/arc/uri/grammar.hxx b/arc/uri/grammar.hxx new file mode 100644 index 0000000..4223369 --- /dev/null +++ b/arc/uri/grammar.hxx @@ -0,0 +1,133 @@ +#ifndef arc__uri__grammar_hxx_ +#define arc__uri__grammar_hxx_ + +namespace arc::uri::grammar +{ + + inline + bool + is_alpha(char c) + { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + inline + bool + is_digit(char c) + { + return '0' <= c && c <= '9'; + } + + inline + bool + is_unreserved(char c) + { + if (is_alpha(c) || is_digit(c)) { + return true; + } + + switch (c) { + case '-': + case '.': + case '_': + case '~': + return true; + } + + return false; + } + + inline + bool + is_subdelim(char c) + { + switch (c) { + case '!': + case '$': + case '&': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + case '\'': + return true; + } + + return false; + } + + inline + bool + is_scheme_start(char c) + { + return is_alpha(c); + } + + inline + bool + is_scheme(char c) + { + return is_alpha(c) || is_digit(c) || c == '+' || c == '-' || c == '.'; + } + + inline + bool + is_userinfo(char c) + { + return is_unreserved(c) || is_subdelim(c) || c == ':' || c == '%'; + } + + inline + bool + is_regname(char c) + { + return is_unreserved(c) || is_subdelim(c); + } + + inline + bool + is_host(char c) + { + return is_regname(c); + } + + inline + bool + is_port(char c) + { + return is_digit(c); + } + + inline + bool + is_pchar(char c) + { + return is_unreserved(c) || is_subdelim(c) || c == ':' || c == '@' || c == '%'; + } + + inline + bool + is_segment_nc(char c) + { + return is_pchar(c) && c != ':'; + } + + inline + bool + is_query(char c) + { + return is_pchar(c) || c == '/' || c == '?'; + } + + inline + bool + is_fragment(char c) + { + return is_pchar(c) || c == '/' || c == '?'; + } +} // namespace arc::uri::grammar + +#endif diff --git a/arc/uri/uri.cxx b/arc/uri/uri.cxx new file mode 100644 index 0000000..ed0c956 --- /dev/null +++ b/arc/uri/uri.cxx @@ -0,0 +1,284 @@ +#include + +#include +#include +#include + +namespace arc::uri +{ + + uri_t:: + uri_t() + {} + + uri_t:: + uri_t(std::string scheme, std::string host, std::string path) + : scheme_{std::move(scheme)}, + host_{std::move(host)}, + path_{std::move(path)} + {} + + uri_t:: + uri_t(std::string scheme, + std::string host, + std::string port, + std::string path) + : scheme_{std::move(scheme)}, + host_{std::move(host)}, + port_{std::move(port)}, + path_{std::move(path)} + {} + + uri_t:: + uri_t(std::string scheme, + std::string host, + std::string port, + std::string path, + std::string query) + : scheme_{std::move(scheme)}, + host_{std::move(host)}, + port_{std::move(port)}, + path_{std::move(path)}, + query_{std::move(query)} + {} + + uri_t:: + uri_t(std::string scheme, + std::string host, + std::string port, + std::string path, + std::string query, + std::string fragment) + : scheme_{std::move(scheme)}, + host_{std::move(host)}, + port_{std::move(port)}, + path_{std::move(path)}, + query_{std::move(query)}, + fragment_{std::move(fragment)} + {} + + uri_t:: + uri_t(std::string scheme, + std::string userinfo, + std::string host, + std::string port, + std::string path, + std::string query, + std::string fragment) + : scheme_{std::move(scheme)}, + userinfo_{std::move(userinfo)}, + host_{std::move(host)}, + port_{std::move(port)}, + path_{std::move(path)}, + query_{std::move(query)}, + fragment_{std::move(fragment)} + {} + + uri_t:: + uri_t(std::optional scheme, + std::optional userinfo, + std::optional host, + std::optional port, + std::string path, + std::optional query, + std::optional fragment) + : scheme_{std::move(scheme)}, + userinfo_{std::move(userinfo)}, + host_{std::move(host)}, + port_{std::move(port)}, + path_{std::move(path)}, + query_{std::move(query)}, + fragment_{std::move(fragment)} + {} + + std::optional const& + uri_t:: + scheme() const + { + return scheme_; + } + + std::string + uri_t:: + scheme_str() const + { + return scheme().value_or(std::string{}); + } + + std::optional const& + uri_t:: + userinfo() const + { + return userinfo_; + } + + std::string + uri_t:: + userinfo_str() const + { + return userinfo().value_or(std::string{}); + } + + std::optional const& + uri_t:: + host() const + { + return host_; + } + + std::string + uri_t:: + host_str() const + { + return host().value_or(std::string{}); + } + + std::optional const& + uri_t:: + port() const + { + return port_; + } + + std::string + uri_t:: + port_str() const + { + return port().value_or(std::string{}); + } + + std::string + uri_t:: + path_str() const + { + return path_; + } + + std::optional const& + uri_t:: + query() const + { + return query_; + } + + std::string + uri_t:: + query_str() const + { + return query().value_or(std::string{}); + } + + std::optional const& + uri_t:: + fragment() const + { + return fragment_; + } + + std::string + uri_t:: + fragment_str() const + { + return fragment().value_or(std::string{}); + } + + std::string + to_string(uri_t const& uri) + { + std::ostringstream str; + + // Scheme + // + if (auto scheme = uri.scheme(); scheme) { + str <<*scheme <<':'; + } + + // Authority + // + if (auto host = uri.host(); host) { + str <<"//"; + + // Userinfo + // + if (auto userinfo = uri.userinfo(); userinfo) { + str <<*userinfo <<'@'; + } + + // Host + // + str <<*host; + + // Port + if (auto port = uri.port(); port) { + str <<':' <<*port; + } + } + + // Path + // + str < segments; + + for (std::string segment; std::getline(path, segment, '/');) { + std::cout << "found segment: " << segment << '\n'; + + if (segment.empty()) { + continue; + } + if (segment == ".") { + continue; + } + if (segment == "..") { + if (!segments.empty()) { + segments.pop_back(); + } + continue; + } + segments.push_back(segment); + } + + std::string normalized; + + for (auto const& j : segments) { + normalized += '/'; + normalized += j; + } + + return uri_t{ + uri.scheme(), + uri.userinfo(), + uri.host(), + uri.port(), + normalized.empty() ? "/" : normalized, + uri.query(), + uri.fragment() + }; + } + + std::optional + try_parse(std::string const& str) + { + return try_parse(str.begin(), str.end()); + } + +} // namespace uri diff --git a/arc/uri/uri.hxx b/arc/uri/uri.hxx new file mode 100644 index 0000000..ba5fc8c --- /dev/null +++ b/arc/uri/uri.hxx @@ -0,0 +1,122 @@ +#ifndef arc__uri__uri_hxx_ +#define arc__uri__uri_hxx_ + +#include +#include + +#include +#include +#include + +namespace arc::uri +{ + + class LIBARC_URI_SYMEXPORT uri_t + { + public: + uri_t(); + + uri_t(std::string, std::string, std::string); + + uri_t(std::string, std::string, std::string, std::string); + + uri_t(std::string, + std::string, + std::string, + std::string, + std::string); + + uri_t(std::string, + std::string, + std::string, + std::string, + std::string, + std::string); + + uri_t(std::string, + std::string, + std::string, + std::string, + std::string, + std::string, + std::string); + + uri_t(std::optional, + std::optional, + std::optional, + std::optional, + std::string, + std::optional, + std::optional); + + std::optional const& + scheme() const; + + std::string + scheme_str() const; + + std::optional const& + userinfo() const; + + std::string + userinfo_str() const; + + std::optional const& + host() const; + + std::string + host_str() const; + + std::optional const& + port() const; + + std::string + port_str() const; + + std::string + path_str() const; + + std::optional const& + query() const; + + std::string + query_str() const; + + std::optional const& + fragment() const; + + std::string + fragment_str() const; + + private: + std::optional scheme_; + std::optional userinfo_; + std::optional host_; + std::optional port_; + std::string path_; + std::optional query_; + std::optional fragment_; + + }; + + LIBARC_URI_SYMEXPORT + std::string + to_string(uri_t const&); + + LIBARC_URI_SYMEXPORT + uri_t + normalize_path(uri_t const&); + + template + std::optional + try_parse(Iterator first, Iterator last); + + LIBARC_URI_SYMEXPORT + std::optional + try_parse(std::string const&); + +} // namespace arc::uri + +#include + +#endif diff --git a/arc/uri/uri.txx b/arc/uri/uri.txx new file mode 100644 index 0000000..83fab9f --- /dev/null +++ b/arc/uri/uri.txx @@ -0,0 +1,242 @@ +namespace arc::uri +{ + + template + std::optional + try_parse(Iterator first, Iterator last) + { + std::optional opt_scheme; + std::optional opt_userinfo; + std::optional opt_host; + std::optional opt_port; + std::string path; + std::optional opt_query; + std::optional opt_fragment; + + auto try_parse_scheme = [&](auto init) + { + auto c = init; + + std::string scheme; + while (c != last && grammar::is_scheme(*c)) { + scheme += *c++; + } + + if (c != last && *c == ':') { + ++c; // skips ':' + opt_scheme = std::move(scheme); + return c; + } + + return init; + }; + + auto try_parse_userinfo = [&](auto init) + { + auto c = init; + + std::string userinfo; + + while (c != last && grammar::is_userinfo(*c)) { + userinfo += *c++; + } + + if (c != last && *c == '@') { + ++c; // skips '@' + opt_userinfo = std::move(userinfo); + return c; + } + + return init; + }; + + auto try_parse_host = [&](auto init) + { + auto c = init; + + opt_host = std::string{}; + + while (c != last && grammar::is_host(*c)) { + *opt_host += *c++; + } + + return c; + }; + + auto try_parse_port = [&](auto init) + { + auto c = init; + + if (c != last && *c == ':') { + ++c; // skips ':' + + opt_port = std::string{}; + + while (c != last && grammar::is_digit(*c)) { + *opt_port += *c++; + } + + return c; + } + + return init; + }; + + auto try_parse_authority = [&](auto init) + { + auto c = init; + + if (c == last || *c != '/') { + return init; + } + + ++c; // skips first '/' + + if (c == last || *c != '/') { + return init; + } + + ++c; // skips second '/' + + c = try_parse_userinfo(c); + c = try_parse_host(c); + c = try_parse_port(c); + + return c; + }; + + auto try_parse_path = [&](auto init) + { + auto c = init; + + while (c != last && (grammar::is_pchar(*c) || *c == '/')) { + path += *c++; + } + + return c; + }; + + auto try_parse_query = [&](auto init) + { + auto c = init; + + if (c != last && *c == '?') { + ++c; // skips '?' + opt_query = std::string{}; + + while (c != last && grammar::is_query(*c)) { + *opt_query += *c++; + } + + return c; + } + + return init; + }; + + auto try_parse_fragment = [&](auto init) + { + auto c = init; + + if (c != last && *c == '#') { + ++c; // skips '?#' + + opt_fragment = std::string{}; + + while (c != last && grammar::is_fragment(*c)) { + *opt_fragment += *c++; + } + + return c; + } + + return init; + }; + + first = try_parse_scheme(first); + first = try_parse_authority(first); + first = try_parse_path(first); + first = try_parse_query(first); + first = try_parse_fragment(first); + + if (first != last) { + return std::nullopt; + } + + auto percent_decode = [](std::string const& input) + { + auto hex_to_char = [](char c) + { + if (c>= '0' && c <= '9') + return c - '0'; + + if (c>= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c>= 'A' && c <= 'F') + return c - 'A' + 10; + + throw std::invalid_argument{"invalid hex character"}; + }; + + auto make_byte = [&](char a, char b) + { + return hex_to_char(a) << 4 | hex_to_char(b); + }; + + std::string str; + + auto j = input.begin(); + + while (j != input.end()) { + if ('%' == *j) { + ++j; + + if (j == input.end()) { + break; + } + + char one = *j++; + + if (j == input.end()) { + break; + } + + char two = *j++; + + str += make_byte(one, two); + + continue; + } + + str += *j; + ++j; + } + + return str; + }; + + if (opt_userinfo) { + opt_userinfo = percent_decode(*opt_userinfo); + } + + path = percent_decode(path); + + if (opt_query) { + opt_query = percent_decode(*opt_query); + } + + if (opt_fragment) { + opt_fragment = percent_decode(*opt_fragment); + } + + return uri_t{opt_scheme, + opt_userinfo, + opt_host, + opt_port, + path, + opt_query, + opt_fragment}; + } + +} // namespace arc::uri diff --git a/arc/uri/version.hxx.in b/arc/uri/version.hxx.in new file mode 100644 index 0000000..3565d10 --- /dev/null +++ b/arc/uri/version.hxx.in @@ -0,0 +1,37 @@ +#ifndef arc__uri__export_hxx_ +#define arc__uri__export_hxx_ + +// The numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define LIBARC_URI_VERSION $libarc_uri.version.project_number$ULL +#define LIBARC_URI_VERSION_STR "$libarc_uri.version.project$" +#define LIBARC_URI_VERSION_ID "$libarc_uri.version.project_id$" +#define LIBARC_URI_VERSION_FULL "$libarc_uri.version$" + +#define LIBARC_URI_VERSION_MAJOR $libarc_uri.version.major$ +#define LIBARC_URI_VERSION_MINOR $libarc_uri.version.minor$ +#define LIBARC_URI_VERSION_PATCH $libarc_uri.version.patch$ + +#define LIBARC_URI_PRE_RELEASE $libarc_uri.version.pre_release$ + +#define LIBARC_URI_SNAPSHOT_SN $libarc_uri.version.snapshot_sn$ULL +#define LIBARC_URI_SNAPSHOT_ID "$libarc_uri.version.snapshot_id$" + +#endif diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/build/bootstrap.build b/build/bootstrap.build new file mode 100644 index 0000000..d7ccb24 --- /dev/null +++ b/build/bootstrap.build @@ -0,0 +1,7 @@ +project = libarc-uri + +using version +using config +using test +using install +using dist diff --git a/build/export.build b/build/export.build new file mode 100644 index 0000000..ae4193e --- /dev/null +++ b/build/export.build @@ -0,0 +1,6 @@ +$out_root/ +{ + include arc/uri/ +} + +export $out_root/arc/uri/$import.target diff --git a/build/root.build b/build/root.build new file mode 100644 index 0000000..21e0a2e --- /dev/null +++ b/build/root.build @@ -0,0 +1,16 @@ +# Uncomment to suppress warnings coming from external libraries. +# +#cxx.internal.scope = current + +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/buildfile b/buildfile new file mode 100644 index 0000000..eb72f8f --- /dev/null +++ b/buildfile @@ -0,0 +1,30 @@ +./: {arc/} doc{README.md} legal{LICENSE} manifest + +import? doxygen = doxygen%exe{doxygen} + +alias{all}: arc/ + +if ($doxygen != [null]) +{ + ./: file{Doxyfile}: in{Doxyfile} $src_root/manifest + { + in.symbol = '@' + in.mode = lax + backlink = copy + + SUMMARY = $project.summary + } + + alias{api-reference}: file{Doxyfile} $doxygen fsdir{docs/api-reference} + {{ + i = $path($<[0]) + diag doxygen $i + env --cwd $directory($i) -- $doxygen $leaf($i) + }} + + alias{all}: alias{api-reference} +} + +# Don't install tests. +# +tests/: install = false diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 0000000..3f50317 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,39 @@ +# Introduction + +[TOC] + +libarc-uri is a library for parsing and manipulating Unique Resource Identifiers. + +## Getting Started + +libarc-uri uses the [build2][build2] build system, which is also the +only officially supported build system. + +### 1. repositories.manifest + +To get started, first add the libarc-uri repository to your +`repositories.manifest` file: + +``` +: +role: prerequisite +location: https://code.helloryan.se/arc/libarc-uri.git##HEAD +``` + +### 2. manifest + +Then add `libarc-uri` as a dependency to your `manifest`-file: + +``` +depends: libarc-uri ^0.1.0- +``` + +### 3. buildfile + +Finally, import and link your binaries with `libarc-uri`: + +``` +import libs =+ libarc-uri%lib{arc-uri} +``` + +[build2]: https://build2.org/ diff --git a/manifest b/manifest new file mode 100644 index 0000000..d080e2c --- /dev/null +++ b/manifest @@ -0,0 +1,11 @@ +: 1 +name: libarc-uri +version: 0.1.0-a.0.z +language: c++ +summary: arc-uri C++ library +license: APSL-1.0 ; Arc Project Software License version 1.0 +description-file: README.md +url: https://www.helloryan.se/arc-project/ +email: arc-project@helloryan.se +depends: * build2 >= 0.17.0 +depends: * bpkg >= 0.17.0 diff --git a/repositories.manifest b/repositories.manifest new file mode 100644 index 0000000..0fa4cd1 --- /dev/null +++ b/repositories.manifest @@ -0,0 +1,7 @@ +: 1 +summary: libarc-uri project repository + +: +role: prerequisite +location: https://pkg.cppget.org/1/beta +trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43 diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..662178d --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,8 @@ +# Test executables. +# +driver + +# Testscript output directories (can be symlinks). +# +test +test-* diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build new file mode 100644 index 0000000..a07b5ea --- /dev/null +++ b/tests/build/bootstrap.build @@ -0,0 +1,5 @@ +project = # Unnamed tests subproject. + +using config +using test +using dist diff --git a/tests/build/root.build b/tests/build/root.build new file mode 100644 index 0000000..a67b2fe --- /dev/null +++ b/tests/build/root.build @@ -0,0 +1,16 @@ +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# Every exe{} in this subproject is by default a test. +# +exe{*}: test = true + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..aeeab15 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1 @@ +./: {*/ -build/}