From 1cc404418891657ed04ea6794cc72185fb59a933 Mon Sep 17 00:00:00 2001 From: TheVice Date: Tue, 22 Oct 2019 16:24:46 +0300 Subject: [PATCH] [Ant4C] updated README.md, added sources of first release - v2019.10.21. --- CMakeLists.txt | 99 ++++ README.md | 208 +++++++ argument_parser.c | 933 +++++++++++++++++++++++++++++++ argument_parser.h | 53 ++ buffer.c | 416 ++++++++++++++ buffer.h | 57 ++ build.build | 96 ++++ common.c | 284 ++++++++++ common.h | 58 ++ conversion.c | 283 ++++++++++ conversion.h | 45 ++ date_time.c | 838 ++++++++++++++++++++++++++++ date_time.h | 65 +++ echo.c | 362 +++++++++++++ echo.h | 23 + environment.c | 1173 +++++++++++++++++++++++++++++++++++++++ environment.h | 83 +++ exec.c | 1295 ++++++++++++++++++++++++++++++++++++++++++++ exec.h | 32 ++ file_system.c | 281 ++++++++++ file_system.h | 52 ++ interpreter.c | 972 +++++++++++++++++++++++++++++++++ interpreter.h | 37 ++ main.c | 205 +++++++ math_unit.c | 377 +++++++++++++ math_unit.h | 59 ++ operating_system.c | 215 ++++++++ operating_system.h | 53 ++ path.c | 1117 ++++++++++++++++++++++++++++++++++++++ path.h | 50 ++ project.c | 666 +++++++++++++++++++++++ project.h | 49 ++ property.c | 600 ++++++++++++++++++++ property.h | 61 +++ range.c | 44 ++ range.h | 31 ++ string_unit.c | 825 ++++++++++++++++++++++++++++ string_unit.h | 56 ++ target.c | 279 ++++++++++ target.h | 29 + version.c | 250 +++++++++ version.h | 41 ++ xml.c | 286 ++++++++++ xml.h | 31 ++ 44 files changed, 13069 insertions(+) create mode 100644 CMakeLists.txt mode change 100644 => 100755 README.md create mode 100644 argument_parser.c create mode 100644 argument_parser.h create mode 100644 buffer.c create mode 100644 buffer.h create mode 100644 build.build create mode 100644 common.c create mode 100644 common.h create mode 100644 conversion.c create mode 100644 conversion.h create mode 100644 date_time.c create mode 100644 date_time.h create mode 100644 echo.c create mode 100644 echo.h create mode 100644 environment.c create mode 100644 environment.h create mode 100644 exec.c create mode 100644 exec.h create mode 100644 file_system.c create mode 100644 file_system.h create mode 100644 interpreter.c create mode 100644 interpreter.h create mode 100644 main.c create mode 100644 math_unit.c create mode 100644 math_unit.h create mode 100644 operating_system.c create mode 100644 operating_system.h create mode 100644 path.c create mode 100644 path.h create mode 100644 project.c create mode 100644 project.h create mode 100644 property.c create mode 100644 property.h create mode 100644 range.c create mode 100644 range.h create mode 100644 string_unit.c create mode 100644 string_unit.h create mode 100644 target.c create mode 100644 target.h create mode 100644 version.c create mode 100644 version.h create mode 100644 xml.c create mode 100644 xml.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0c8a81b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,99 @@ +cmake_minimum_required(VERSION 2.8.12) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR "Configuration process cannot start from project source directory.") +endif() + +project("Ant4C") + +# ant4c + +add_library(ant4c STATIC + "${CMAKE_SOURCE_DIR}/argument_parser.c" + "${CMAKE_SOURCE_DIR}/argument_parser.h" + "${CMAKE_SOURCE_DIR}/buffer.c" + "${CMAKE_SOURCE_DIR}/buffer.h" + "${CMAKE_SOURCE_DIR}/common.c" + "${CMAKE_SOURCE_DIR}/common.h" + "${CMAKE_SOURCE_DIR}/conversion.c" + "${CMAKE_SOURCE_DIR}/conversion.h" + "${CMAKE_SOURCE_DIR}/date_time.c" + "${CMAKE_SOURCE_DIR}/date_time.h" + "${CMAKE_SOURCE_DIR}/echo.c" + "${CMAKE_SOURCE_DIR}/echo.h" + "${CMAKE_SOURCE_DIR}/environment.c" + "${CMAKE_SOURCE_DIR}/environment.h" + "${CMAKE_SOURCE_DIR}/exec.c" + "${CMAKE_SOURCE_DIR}/exec.h" + "${CMAKE_SOURCE_DIR}/file_system.c" + "${CMAKE_SOURCE_DIR}/file_system.h" + "${CMAKE_SOURCE_DIR}/interpreter.c" + "${CMAKE_SOURCE_DIR}/interpreter.h" + "${CMAKE_SOURCE_DIR}/math_unit.c" + "${CMAKE_SOURCE_DIR}/math_unit.h" + "${CMAKE_SOURCE_DIR}/operating_system.c" + "${CMAKE_SOURCE_DIR}/operating_system.h" + "${CMAKE_SOURCE_DIR}/path.c" + "${CMAKE_SOURCE_DIR}/path.h" + "${CMAKE_SOURCE_DIR}/project.c" + "${CMAKE_SOURCE_DIR}/project.h" + "${CMAKE_SOURCE_DIR}/property.c" + "${CMAKE_SOURCE_DIR}/property.h" + "${CMAKE_SOURCE_DIR}/range.c" + "${CMAKE_SOURCE_DIR}/range.h" + "${CMAKE_SOURCE_DIR}/string_unit.c" + "${CMAKE_SOURCE_DIR}/string_unit.h" + "${CMAKE_SOURCE_DIR}/target.c" + "${CMAKE_SOURCE_DIR}/target.h" + "${CMAKE_SOURCE_DIR}/version.c" + "${CMAKE_SOURCE_DIR}/version.h" + "${CMAKE_SOURCE_DIR}/xml.c" + "${CMAKE_SOURCE_DIR}/xml.h") + +# ant4c_app + +add_executable(ant4c_app + "${CMAKE_SOURCE_DIR}/main.c") + +target_link_libraries(ant4c_app ant4c) + +if(DEFINED PROGRAM_VERSION AND DEFINED PROGRAM_VERSION_LENGTH) + target_compile_definitions(ant4c PRIVATE -DPROGRAM_VERSION=${PROGRAM_VERSION}) + target_compile_definitions(ant4c PRIVATE -DPROGRAM_VERSION_LENGTH=${PROGRAM_VERSION_LENGTH}) + + target_compile_definitions(ant4c_app PRIVATE -DPROGRAM_VERSION=${PROGRAM_VERSION}) + target_compile_definitions(ant4c_app PRIVATE -DPROGRAM_VERSION_LENGTH=${PROGRAM_VERSION_LENGTH}) +endif() + +if(NOT MSVC) + target_link_libraries(ant4c_app m) +endif() + +if(MSVC) + # (${MSVC_VERSION} LESS 1901) + # (${MSVC_TOOLSET_VERSION} LESS 141(142)) + if (("v140_clang_c2" STREQUAL CMAKE_GENERATOR_TOOLSET) OR ("v141_clang_c2" STREQUAL CMAKE_GENERATOR_TOOLSET)) + set(FLAGS "${FLAGS} -Wall /GS") + else() + set(FLAGS "${FLAGS} /W4 /GS") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEFAULT_CMAKE_C_FLAGS} ${FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEFAULT_CMAKE_CXX_FLAGS} ${FLAGS}") + if(CMAKE_CL_64) + set(LINK_FLAGS "${LINK_FLAGS} /DynamicBase /NXCompat") + else() + set(LINK_FLAGS "${LINK_FLAGS} /SafeSEH /DynamicBase /NXCompat") + endif() +else() + set(FLAGS "${FLAGS} -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEFAULT_CMAKE_C_FLAGS} ${FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEFAULT_CMAKE_CXX_FLAGS} ${FLAGS}") + if(${CMAKE_VERSION} LESS 3.0) + if(("GNU" STREQUAL "${CMAKE_CXX_COMPILER_ID}") AND ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "6.0")) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") + else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() +endif() diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 204c2aa..af5a943 --- a/README.md +++ b/README.md @@ -1,2 +1,210 @@ # Ant4C Tool that interprets scenarios. Similar to Apache Ant and NAnt. Written in C. + +#Overview +Ant for C is C-based script tool. Source of script should be written in XML-based code. +For understanding why tools with such vision was written read [Apache Ant Introduction](http://jakarta.apache.org/ant/manual/) or/and [NAnt help](http://nant.sourceforge.net/). + +Name of program just reference to language on which source was written. + +For first initial release echo and exec tasks support. Targets are not support. Functions from name spaces bool, cygpath, datetime, double, environment, int, int64, long, math, operating_system, path, platform, program, project, property, string, timespan and version are available. + +Developing was started in August of 2019 and first present to the public in October of 2019. + +#Downloads +Binaries for Windows, Ubuntu 16.04, OpenBSD 6.5 and FreeBSD 12.0 are available on [release page](https://github.com/TheVice/Ant4C/releases). +Also library available to use ant4c with other C/C++ projects. +For Windows binaries present after compiled with MinGW and Visual Studio 2019. + +#Differences from NAnt +Some name space have addition functions (like is64bit-operating-system from environment) and addition version (like substring with two arguments from string addition to version with three arguments) comparing to NAnt implementation. Some functions are missed. + +#Building +Build can be done by one of C compilers - MSVC, MinGW, GCC or CLang, after configuring with [CMake](http://www.cmake.org/download/). +For release configuration, without version information, Ant4C script can be used. +* ant4c_app -buildfile:build.build -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:VS2019="" -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:VS2019="" -D:cmake_tool="ClangCL" -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:VS2017="" -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:VS2017="" -D:cmake_arch="Win64" -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:VS2017="" -D:cmake_tool="v141_clang_c2" -D:cmake=cmake +* ant4c_app -buildfile:build.build -D:MinGW="" -D:cmake=cmake + +Of course full path to cmake (for example -D:cmake=/usr/local/bin/cmake or -D:cmake="C:\Program Files\cmake\bin\cmake.exe") and build file should be provided. + +#List of functions +``` +bool::parse +bool::to-string +``` +``` +double::parse +double::to-string +``` +``` +int::parse +int::to-string +``` +``` +long::parse +long::to-string +``` +``` +datetime::format-to-string +datetime::parse +datetime::to-string +``` +``` +datetime::get-day +datetime::get-day-of-year +datetime::get-days-in-month +datetime::get-hour +datetime::get-minute +datetime::get-month +datetime::get-second +datetime::get-year +datetime::is-leap-year +datetime::now +datetime::from-input +``` +``` +timespan::parse +timespan::to-string +``` +``` +timespan::from-days +timespan::from-hours +timespan::from-minutes +timespan::from-seconds +timespan::get-days +timespan::get-hours +timespan::get-minutes +timespan::get-seconds +timespan::get-total-days +timespan::get-total-hours +timespan::get-total-minutes +timespan::get-total-seconds +``` +``` +environment::get-folder-path +environment::get-machine-name +environment::get-operating-system +environment::get-user-name +environment::get-variable +environment::get-version +environment::newline +environment::variable-exists +``` +``` +math::abs +math::ceiling +math::floor +math::round +math::acos +math::asin +math::atan +math::atan2 +math::cos +math::cosh +math::exp +math::log +math::log10 +math::max +math::min +math::pow +math::sign +math::sin +math::sinh +math::sqrt +math::tan +math::tanh +math::cot +math::coth +math::truncate +math::PI +math::E +math::degrees +math::radians +math::addition +math::subtraction +math::multiplication +math::division +math::near +math::less +math::greater +``` +``` +program::version +program::current_directory +``` +``` +platform::get-name +platform::is-unix +platform::is-windows +``` +``` +project::get-base-directory +project::get-buildfile-path +project::get-buildfile-uri +project::get-default-target +project::get-name +``` +``` +property::exists +property::get-value +property::is-dynamic +property::is-readonly +``` +``` +operating-system::get-platform +operating-system::get-version +operating-system::to-string +``` +``` +path::change-extension +path::combine +path::get-directory-name +path::get-extension +path::get-file-name +path::get-file-name-without-extension +path::get-full-path +path::get-path-root +path::get-temp-file-name +path::get-temp-path +path::has-extension +path::is-path-rooted +``` +``` +string::contains +string::empty +string::ends-with +string::equal +string::get-length +string::index-of +string::last-index-of +string::pad-left +string::pad-right +string::quote +string::replace +string::starts-with +string::substring +string::to-lower +string::to-upper +string::trim +string::trim-end +string::trim-start +string::un-quote +``` +``` +cygpath::get-dos-path +cygpath::get-unix-path +cygpath::get-windows-path +``` +``` +version::parse +version::to-string +version::get-build +version::get-major +version::get-minor +version::get-revision +``` diff --git a/argument_parser.c b/argument_parser.c new file mode 100644 index 0000000..ebae2bd --- /dev/null +++ b/argument_parser.c @@ -0,0 +1,933 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "argument_parser.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "math_unit.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" + +#include + +#if defined(_WIN32) +#include +#endif + +struct command_argument +{ + struct buffer build_files_; + struct buffer build_files; + uint8_t pause; + uint8_t verbose; + uint8_t debug; + uint8_t quiet; + uint8_t indent; + struct buffer properties; + struct buffer log_file_; + struct buffer log_file; + uint8_t project_help; + uint8_t no_logo; + uint8_t help; + /*TODO: + struct buffer targets_; + struct buffer targets;*/ +}; + +static struct command_argument argument; +static uint8_t is_argument_init = 0; +static uint8_t is_from_file = 0; + +#define INIT_ARGUMENT \ + if (is_argument_init) \ + { \ + property_clear(&argument.properties); \ + \ + if (!buffer_resize(&argument.build_files_, 0) || \ + !buffer_resize(&argument.build_files, 0) || \ + !buffer_resize(&argument.log_file_, 0) || \ + !buffer_resize(&argument.log_file, 0)) \ + { \ + return 0; \ + } \ + } \ + else \ + { \ + SET_NULL_TO_BUFFER(argument.build_files_); \ + SET_NULL_TO_BUFFER(argument.build_files); \ + SET_NULL_TO_BUFFER(argument.properties); \ + SET_NULL_TO_BUFFER(argument.log_file_); \ + SET_NULL_TO_BUFFER(argument.log_file); \ + is_argument_init = 1; \ + } \ + \ + argument.pause = 0; \ + argument.verbose = UINT8_MAX; \ + argument.debug = 0; \ + argument.quiet = 0; \ + argument.indent = 0; \ + argument.project_help = 0; \ + argument.no_logo = 0; \ + argument.help = 0; + +#define GET_FILE_PATH_CHAR(CONDITION, ARGUMENT, START_SHIFT, LENGTH, OUTPUT) \ + else if (CONDITION) \ + { \ + struct range path; \ + path.start = (ARGUMENT) + (START_SHIFT); \ + path.finish = (ARGUMENT) + (LENGTH); \ + \ + if (range_is_null_or_empty(&path)) \ + { \ + return 0; \ + } \ + \ + path.start = find_any_symbol_like_or_not_like_that(path.start, path.finish, "\"", 1, 0, 1); \ + path.finish = 1 + find_any_symbol_like_or_not_like_that(path.finish - 1, path.start, "\"", 1, 0, -1); \ + \ + if (range_is_null_or_empty(&path) || \ + !buffer_append_data_from_range((OUTPUT), &path) || \ + !buffer_push_back((OUTPUT), '\0')) \ + { \ + return 0; \ + } \ + } + +#define GET_FILE_PATH_WCHAR(CONDITION, ARGUMENT, START_SHIFT, LENGTH, OUTPUT) \ + else if (CONDITION) \ + { \ + const wchar_t* start = (ARGUMENT) + (START_SHIFT); \ + const wchar_t* finish = (ARGUMENT) + (LENGTH); \ + \ + if (NULL == start || finish == NULL || finish <= start) \ + { \ + return 0; \ + } \ + \ + start = find_any_symbol_like_or_not_like_that_wchar_t(start, finish, L"\"", 1, 0, 1); \ + finish = 1 + find_any_symbol_like_or_not_like_that_wchar_t(finish - 1, start, L"\"", 1, 0, -1); \ + \ + if (finish <= start) \ + { \ + return 0; \ + } \ + \ + const ptrdiff_t size = buffer_size(OUTPUT); \ + \ + if (!buffer_append_char((OUTPUT), NULL, (LENGTH))) \ + { \ + return 0; \ + } \ + \ + char* m = (char*)buffer_data((OUTPUT), size); \ + uint16_t count = (uint16_t)(finish - start); \ + WIDE2MULTI(start, m, count) \ + \ + if (!count || \ + !buffer_resize((OUTPUT), size + (finish - start)) || \ + !buffer_push_back((OUTPUT), '\0')) \ + { \ + return 0; \ + } \ + } + +#define GET_BOOL_VALUE(CONDITION, TYPE, ARGUMENT, LENGTH, PLUS, MINUS, OUTPUT) \ + else if (CONDITION) \ + { \ + const TYPE ch = *((ARGUMENT) + (LENGTH) - 1); \ + \ + if ((PLUS) == ch) \ + { \ + (OUTPUT) = 1; \ + } \ + else if ((MINUS) == ch) \ + { \ + (OUTPUT) = 0; \ + } \ + else \ + { \ + (OUTPUT) = 1; \ + } \ + } + +#define GET_INDENT_CHAR(A, LENGTH, OUTPUT) \ + else if (7 < (LENGTH) && 0 == memcmp((A), "-indent:", 8)) \ + { \ + if ((LENGTH) < 9) \ + { \ + return 0; \ + } \ + \ + (OUTPUT) = (uint8_t)math_abs(long_parse((A) + 8)); \ + } + +#define GET_INDENT_WCHAR(A, LENGTH, OUTPUT) \ + else if (7 < (LENGTH) && 0 == wmemcmp((A), L"-indent:", 8)) \ + { \ + if ((LENGTH) < 9) \ + { \ + return 0; \ + } \ + \ + (OUTPUT) = (uint8_t)math_abs(long_parse_wchar_t((A) + 8)); \ + } + +#define GET_PROPERTY_CHAR(CONDITION, ARGUMENT, LENGTH, OUTPUT) \ + else if (CONDITION) \ + { \ + struct range key; \ + struct range value; \ + \ + if (!argument_get_key_and_value( \ + (ARGUMENT) + 3, \ + (ARGUMENT) + (LENGTH), \ + &key, \ + &value)) \ + { \ + return 0; \ + } \ + \ + if (!string_trim(&key)) \ + { \ + return 0; \ + } \ + \ + if (!property_set_by_name(NULL, NULL, (OUTPUT), key.start, (uint8_t)range_size(&key), \ + value.start, range_size(&value), \ + property_value_is_char_array, \ + 1, 1, 1, argument_parser_get_verbose())) \ + { \ + return 0; \ + } \ + } + +#define GET_PROPERTY_WCHAR(CONDITION, ARGUMENT, LENGTH, TMP_BUFFER, OUTPUT) \ + else if (CONDITION) \ + { \ + const wchar_t* start = (ARGUMENT) + 3; \ + const wchar_t* finish = (ARGUMENT) + (LENGTH); \ + const wchar_t* equal = find_any_symbol_like_or_not_like_that_wchar_t(start, finish, L"=", 1, 1, 1); \ + \ + if (start == equal || finish == equal) \ + { \ + return 0; \ + } \ + \ + const ptrdiff_t name_length = equal - start; \ + const ptrdiff_t value_length = finish - (equal + 1); \ + /*TODO: used external buffer.*/\ + const ptrdiff_t size = buffer_size(TMP_BUFFER); \ + \ + if (!buffer_append((TMP_BUFFER), NULL, 1 + (LENGTH))) \ + { \ + return 0; \ + } \ + \ + char* m = (char*)buffer_data((TMP_BUFFER), size); \ + uint16_t count = (uint16_t)(LENGTH); \ + WIDE2MULTI(start, m, count); \ + \ + /*if (!count) TODO: for some reason usedDefaultChar is TRUE.*/ \ + \ + struct range name; \ + name.start = m; \ + name.finish = m + name_length; \ + name.start = find_any_symbol_like_or_not_like_that(name.start, name.finish, "\"", 1, 0, 1); \ + name.finish = 1 + find_any_symbol_like_or_not_like_that(name.finish - 1, name.start, "\"", 1, 0, -1); \ + \ + if (!string_trim(&name)) \ + { \ + return 0; \ + } \ + \ + struct range value; \ + value.start = m + name_length + 1; \ + value.finish = value.start + value_length; \ + value.start = find_any_symbol_like_or_not_like_that(value.start, value.finish, "\"", 1, 0, 1); \ + value.finish = 1 + find_any_symbol_like_or_not_like_that(value.finish - 1, value.start, "\"", 1, 0, -1); \ + \ + if (!string_trim(&value)) \ + { \ + return 0; \ + } \ + \ + if (!property_set_by_name(NULL, NULL, (OUTPUT), name.start, (uint8_t)range_size(&name), \ + value.start, range_size(&value), \ + property_value_is_char_array, \ + 1, 1, 1, \ + argument_parser_get_verbose() || \ + !buffer_resize((TMP_BUFFER), size))) \ + { \ + return 0; \ + } \ + } + +#define CONTINUE(CONDITION) \ + else if (CONDITION) \ + { \ + continue; \ + } + +#define GET_COMMAND_LINE_FILE(CONDITION) \ + else if (CONDITION) \ + { \ + is_from_file = 1; \ + /*TODO: process parameters from command file.*/ \ + is_from_file = 0; \ + } + +#define BUILD_FILE_CHAR(A, LENGTH) \ + ((10 < (LENGTH) && 0 == memcmp((A), "-buildfile:", 11)) || \ + (2 < (LENGTH) && 0 == memcmp((A), "/f:", 3))) + +#define BUILD_FILE_WCHAR(A, LENGTH) \ + ((10 < (LENGTH) && 0 == wmemcmp((A), L"-buildfile:", 11)) || \ + (2 < (LENGTH) && 0 == wmemcmp((A), L"/f:", 3))) + +#define LOG_FILE_CHAR(A, LENGTH, OUTPUT) \ + (!buffer_size(OUTPUT) && \ + ((8 < (LENGTH) && 0 == memcmp((A), "-logfile:", 9)) || \ + (2 < length && 0 == memcmp((A), "-l:", 3)))) + +#define LOG_FILE_WCHAR(A, LENGTH, OUTPUT) \ + (!buffer_size(OUTPUT) && \ + ((8 < (LENGTH) && 0 == wmemcmp((A), L"-logfile:", 9)) || \ + (2 < length && 0 == wmemcmp((A), L"-l:", 3)))) + +#define BUILD_FILE_SHIFT_CHAR(A) ('/' == (A)[0] ? 3 : 11) +#define BUILD_FILE_SHIFT_WCHAR(A) (L'/' == (A)[0] ? 3 : 11) + +#define LOG_FILE_SHIFT_CHAR(A) (':' == (A)[2] ? 3 : 9) +#define LOG_FILE_SHIFT_WCHAR(A) (L':' == (A)[2] ? 3 : 9) + +#define PAUSE_CHAR(A, LENGTH) \ + ((6 == (LENGTH) && 0 == memcmp((A), "-pause", 6)) || \ + (7 == (LENGTH) && (0 == memcmp((A), "-pause+", 7) || \ + 0 == memcmp((A), "-pause-", 7)))) + +#define PAUSE_WCHAR(A, LENGTH) \ + ((6 == (LENGTH) && 0 == wmemcmp((A), L"-pause", 6)) || \ + (7 == (LENGTH) && (0 == wmemcmp((A), L"-pause+", 7) || \ + 0 == wmemcmp((A), L"-pause-", 7)))) + +#define VERBOSE_CHAR(A, LENGTH) \ + ((8 == (LENGTH) && 0 == memcmp((A), "-verbose", 8)) || \ + (2 == (LENGTH) && 0 == memcmp((A), "-v", 2)) || \ + (9 == (LENGTH) && (0 == memcmp((A), "-verbose+", 9) || \ + 0 == memcmp((A), "-verbose-", 9))) || \ + (3 == (LENGTH) && (0 == memcmp((A), "-v+", 3) || \ + 0 == memcmp((A), "-v-", 3)))) + +#define VERBOSE_WCHAR(A, LENGTH) \ + ((8 == (LENGTH) && 0 == wmemcmp((A), L"-verbose", 8)) || \ + (2 == (LENGTH) && 0 == wmemcmp((A), L"-v", 2)) || \ + (9 == (LENGTH) && (0 == wmemcmp((A), L"-verbose+", 9) || \ + 0 == wmemcmp((A), L"-verbose-", 9))) || \ + (3 == (LENGTH) && (0 == wmemcmp((A), L"-v+", 3) || \ + 0 == wmemcmp((A), L"-v-", 3)))) +#define DEBUG_CHAR(A, LENGTH) \ + (6 == (LENGTH) && 0 == memcmp((A), "-debug", 6)) + +#define DEBUG_WCHAR(A, LENGTH) \ + (6 == (LENGTH) && 0 == wmemcmp((A), L"-debug", 6)) + +#define QUIET_CHAR(A, LENGTH) \ + ((6 == (LENGTH) && 0 == memcmp((A), "-quiet", 6)) || \ + (2 == (LENGTH) && 0 == memcmp((A), "-q", 2))) + +#define QUIET_WCHAR(A, LENGTH) \ + ((6 == (LENGTH) && 0 == wmemcmp((A), L"-quiet", 6)) || \ + (2 == (LENGTH) && 0 == wmemcmp((A), L"-q", 2))) + +#define DEFINE_CHAR(A, LENGTH) \ + (2 < (LENGTH) && 0 == memcmp((A), "-D:", 3)) + +#define DEFINE_WCHAR(A, LENGTH) \ + (2 < (LENGTH) && 0 == wmemcmp((A), L"-D:", 3)) + +#define PROJECT_HELP_CHAR(A, LENGTH) \ + (12 == (LENGTH) && 0 == memcmp((A), "-projecthelp", 12)) + +#define PROJECT_HELP_WCHAR(A, LENGTH) \ + (12 == (LENGTH) && 0 == wmemcmp((A), L"-projecthelp", 12)) + +#define NO_LOGO_CHAR(A, LENGTH) \ + (7 == (LENGTH) && 0 == memcmp((A), "-nologo", 7)) + +#define NO_LOGO_WCHAR(A, LENGTH) \ + (7 == (LENGTH) && 0 == wmemcmp((A), L"-nologo", 7)) + +#define HELP_CHAR(A, LENGTH) \ + ((5 == (LENGTH) && 0 == memcmp((A), "-help", 5)) || \ + (2 == (LENGTH) && 0 == memcmp((A), "-h", 2))) + +#define HELP_WCHAR(A, LENGTH) \ + ((5 == (LENGTH) && 0 == wmemcmp((A), L"-help", 5)) || \ + (2 == (LENGTH) && 0 == wmemcmp((A), L"-h", 2))) + +#define COMMAND_LINE_FILE_CHAR(A) \ + (!is_from_file && '@' == (A)[0]) + +#define COMMAND_LINE_FILE_WCHAR(A) \ + (!is_from_file && L'@' == (A)[0]) + +#define GET_VERBOSE(CONDITION, ARGUMENT, MAX_I, STRLEN, TYPE, PLUS, MINUS, OUTPUT) \ + if (NULL == (ARGUMENT)) \ + { \ + return; \ + } \ + \ + for (int i = 0; i < (MAX_I); ++i) \ + { \ + const ptrdiff_t length = (ptrdiff_t)(STRLEN)((ARGUMENT)[i]); \ + \ + if (length < 2) \ + { \ + continue; \ + } \ + \ + GET_BOOL_VALUE((CONDITION), TYPE, (ARGUMENT)[i], length, (PLUS), (MINUS), (OUTPUT)) \ + } + +void argument_parser_get_verbose_char(int argc, char** argv) +{ + GET_VERBOSE(VERBOSE_CHAR(argv[i], length), argv, argc, strlen, char, '+', '-', argument.verbose) +} +#if defined(_WIN32) +void argument_parser_get_verbose_wchar_t(int argc, wchar_t** argv) +{ + GET_VERBOSE(VERBOSE_WCHAR(argv[i], length), argv, argc, wcslen, wchar_t, L'+', L'-', argument.verbose) +} +#endif +uint8_t argument_parser_char(int i, int argc, char** argv) +{ + if (!is_from_file) + { + INIT_ARGUMENT; + } + + if (argc <= i || NULL == argv) + { + return 0; + } + + argument_parser_get_verbose_char(argc, argv); + + for (; i < argc; ++i) + { + const ptrdiff_t length = (ptrdiff_t)strlen(argv[i]); + + if (length < 2 || + ('-' != argv[i][0] && + '@' != argv[i][0] && + '/' != argv[i][0])) + { + continue; + } + + GET_FILE_PATH_CHAR(BUILD_FILE_CHAR(argv[i], length), argv[i], + BUILD_FILE_SHIFT_CHAR(argv[i]), length, &argument.build_files) + GET_BOOL_VALUE(PAUSE_CHAR(argv[i], length), char, argv[i], length, '+', '-', argument.pause) + CONTINUE(VERBOSE_CHAR(argv[i], length)) + GET_BOOL_VALUE(DEBUG_CHAR(argv[i], length), char, argv[i], length, '+', '-', argument.debug) + GET_BOOL_VALUE(QUIET_CHAR(argv[i], length), char, argv[i], length, '+', '-', argument.quiet) + GET_INDENT_CHAR(argv[i], length, argument.indent) + GET_PROPERTY_CHAR(DEFINE_CHAR(argv[i], length), argv[i], length, &argument.properties) + GET_FILE_PATH_CHAR(LOG_FILE_CHAR(argv[i], length, &argument.log_file), argv[i], + LOG_FILE_SHIFT_CHAR(argv[i]), length, &argument.log_file) + GET_BOOL_VALUE(PROJECT_HELP_CHAR(argv[i], length), char, argv[i], length, '+', '-', + argument.project_help) + GET_BOOL_VALUE(NO_LOGO_CHAR(argv[i], length), char, argv[i], length, '+', '-', argument.no_logo) + GET_BOOL_VALUE(HELP_CHAR(argv[i], length), char, argv[i], length, '+', '-', argument.help) + /*TODO:if (INT8_MAX < length) + { + continue; + } + + if (!buffer_append_char(argument.targets, argv[i], length)) + { + return 0; + }*/ + } + + if (UINT8_MAX == argument.verbose) + { + argument.verbose = 0; + } + + return 1; +} +#if defined(_WIN32) +uint8_t argument_parser_wchar_t(int i, int argc, wchar_t** argv) +{ + if (!is_from_file) + { + INIT_ARGUMENT; + } + + if (argc <= i || NULL == argv) + { + return 0; + } + + argument_parser_get_verbose_wchar_t(argc, argv); + + for (; i < argc; ++i) + { + const ptrdiff_t length = (ptrdiff_t)wcslen(argv[i]); + + if (length < 2 || + (L'-' != argv[i][0] && + L'@' != argv[i][0] && + L'/' != argv[i][0])) + { + continue; + } + + GET_FILE_PATH_WCHAR(BUILD_FILE_WCHAR(argv[i], length), argv[i], + BUILD_FILE_SHIFT_WCHAR(argv[i]), length, &argument.build_files) + GET_BOOL_VALUE(PAUSE_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', argument.pause) + CONTINUE(VERBOSE_WCHAR(argv[i], length)) + GET_BOOL_VALUE(DEBUG_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', argument.debug) + GET_BOOL_VALUE(QUIET_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', argument.quiet) + GET_INDENT_WCHAR(argv[i], length, argument.indent) + GET_PROPERTY_WCHAR(DEFINE_WCHAR(argv[i], length), argv[i], length, &argument.log_file, &argument.properties) + GET_FILE_PATH_WCHAR(LOG_FILE_WCHAR(argv[i], length, &argument.log_file), argv[i], + LOG_FILE_SHIFT_WCHAR(argv[i]), length, &argument.log_file) + GET_BOOL_VALUE(PROJECT_HELP_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', + argument.project_help) + GET_BOOL_VALUE(NO_LOGO_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', argument.no_logo) + GET_BOOL_VALUE(HELP_WCHAR(argv[i], length), wchar_t, argv[i], length, L'+', L'-', argument.help) + } + + if (UINT8_MAX == argument.verbose) + { + argument.verbose = 0; + } + + return 1; +} +#endif +void argument_parser_release() +{ + if (is_argument_init) + { + buffer_release(&argument.build_files_); + buffer_release(&argument.build_files); + property_clear(&argument.properties); + buffer_release(&argument.log_file_); + buffer_release(&argument.log_file); + } +} + +uint8_t argument_get_key_and_value( + const char* input_start, const char* input_finish, + struct range* key, struct range* value) +{ + if (range_in_parts_is_null_or_empty(input_start, input_finish) || + NULL == key || NULL == value) + { + return 0; + } + + value->start = find_any_symbol_like_or_not_like_that(input_start, input_finish, "=", 1, 1, 1); + key->finish = value->start; + // + value->start = find_any_symbol_like_or_not_like_that(value->start + 1, input_finish, "\"", 1, 0, 1); + value->finish = 1 + find_any_symbol_like_or_not_like_that(input_finish - 1, value->start, "\"", 1, 0, -1); + // + key->start = find_any_symbol_like_or_not_like_that(input_start, key->finish, "\"", 1, 0, 1); + key->finish = 1 + find_any_symbol_like_or_not_like_that(key->finish - 1, key->start, "\"", 1, 0, -1); + // + return !range_is_null_or_empty(key) && value->start <= value->finish; +} + +uint8_t argument_append_arguments( + const char* input_start, const char* input_finish, struct buffer* output) +{ + if (range_in_parts_is_null_or_empty(input_start, input_finish) || + NULL == output) + { + return 0; + } + + for (; input_start < input_finish; ++input_start) + { + const char* finish = NULL; + + if (finish == input_start) + { + break; + } + + if ('\"' == *input_start) + { + input_start = find_any_symbol_like_or_not_like_that(input_start + 1, input_finish, "\"", 1, 0, 1); + finish = find_any_symbol_like_or_not_like_that(input_start + 1, input_finish, "\"", 1, 1, 1); + } + else if (' ' == *input_start) + { + continue; + } + else + { + finish = find_any_symbol_like_or_not_like_that(input_start + 1, input_finish, " ", 1, 1, 1); + } + + if (input_start < finish) + { + if (!buffer_append_char(output, input_start, finish - input_start) || + !buffer_push_back(output, '\0')) + { + return 0; + } + } + + input_start = finish; + } + + return 1; +} + +uint8_t argument_create_arguments(struct buffer* output, int* argc, char*** argv) +{ + if (NULL == output || + NULL == argc || + NULL == argv) + { + return 0; + } + + const int index = *argc; + const char* start = (const char*)buffer_data(output, index); + *argc = 0; + + if (NULL == start) + { + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(char*))) + { + return 0; + } + + (*argv) = (char**)buffer_data(output, buffer_size(output) - sizeof(char*)); + return 1; + } + + const char* finish = (const char*)(buffer_data(output, 0) + buffer_size(output)); + + while ((start = find_any_symbol_like_or_not_like_that(start, finish, "\0", 1, 1, 1)) < finish) + { + ++start; + (*argc)++; + } + + const ptrdiff_t size = buffer_size(output); + + if ((*argc) < 1) + { + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(char*))) + { + return 0; + } + + (*argv) = (char**)buffer_data(output, size); + return 1; + } + + if (!buffer_append(output, NULL, ((ptrdiff_t)1 + (*argc)) * sizeof(char*))) + { + return 0; + } + + start = (const char*)buffer_data(output, index); + finish = (const char*)buffer_data(output, size); + + if (!buffer_resize(output, size)) + { + return 0; + } + + while ((start = find_any_symbol_like_or_not_like_that(start, finish, "\0", 1, 0, 1)) < finish) + { + if (!buffer_append(output, (const uint8_t*)&start, sizeof(char*))) + { + return 0; + } + + start = find_any_symbol_like_or_not_like_that(start, finish, "\0", 1, 1, 1); + } + + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(char*))) + { + return 0; + } + + (*argv) = (char**)buffer_data(output, size); + return 1; +} + +uint8_t argument_from_char(const char* input_start, const char* input_finish, + struct buffer* output, int* argc, char*** argv) +{ + return argument_append_arguments(input_start, input_finish, output) && + argument_create_arguments(output, argc, argv); +} + +#if defined(_WIN32) + +uint8_t argument_append_arguments_wchar_t( + const wchar_t* input_start, const wchar_t* input_finish, struct buffer* output) +{ + if (NULL == input_start || + NULL == input_finish || + input_finish <= input_start || + NULL == output) + { + return 0; + } + + for (; input_start < input_finish; ++input_start) + { + const wchar_t* finish = NULL; + + if (finish == input_start) + { + break; + } + + if (L'\"' == *input_start) + { + input_start = find_any_symbol_like_or_not_like_that_wchar_t(input_start + 1, input_finish, L"\"", 1, 0, 1); + finish = find_any_symbol_like_or_not_like_that_wchar_t(input_start + 1, input_finish, L"\"", 1, 1, 1); + } + else if (L' ' == *input_start) + { + continue; + } + else + { + finish = find_any_symbol_like_or_not_like_that_wchar_t(input_start + 1, input_finish, L" ", 1, 1, 1); + } + + if (input_start < finish) + { + if (!buffer_append_wchar_t(output, input_start, finish - input_start) || + !buffer_push_back_uint16(output, L'\0')) + { + return 0; + } + } + + input_start = finish; + } + + return 1; +} + +uint8_t argument_create_arguments_wchar_t(struct buffer* output, int* argc, wchar_t*** argv) +{ + if (NULL == output || + NULL == argc || + NULL == argv) + { + return 0; + } + + const int index = *argc; + const wchar_t* start = (const wchar_t*)buffer_data(output, index); + *argc = 0; + + if (NULL == start) + { + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(wchar_t*))) + { + return 0; + } + + (*argv) = (wchar_t**)buffer_data(output, buffer_size(output) - sizeof(wchar_t*)); + return 1; + } + + const wchar_t* finish = (const wchar_t*)(buffer_data(output, 0) + buffer_size(output)); + + while ((start = find_any_symbol_like_or_not_like_that_wchar_t(start, finish, L"\0", 1, 1, 1)) < finish) + { + ++start; + (*argc)++; + } + + const ptrdiff_t size = buffer_size(output); + + if ((*argc) < 1) + { + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(wchar_t*))) + { + return 0; + } + + (*argv) = (wchar_t**)buffer_data(output, size); + return 1; + } + + if (!buffer_append(output, NULL, ((ptrdiff_t)2 + (*argc)) * sizeof(wchar_t*))) + { + return 0; + } + + start = (const wchar_t*)buffer_data(output, index); + finish = (const wchar_t*)buffer_data(output, size); + + if (!buffer_resize(output, size)) + { + return 0; + } + + while ((start = find_any_symbol_like_or_not_like_that_wchar_t(start, finish, L"\0", 1, 0, 1)) < finish) + { + if (!buffer_append(output, (const uint8_t*)&start, sizeof(wchar_t*))) + { + return 0; + } + + start = find_any_symbol_like_or_not_like_that_wchar_t(start, finish, L"\0", 1, 1, 1); + } + + start = NULL; + + if (!buffer_append(output, (const uint8_t*)&start, sizeof(wchar_t*))) + { + return 0; + } + + (*argv) = (wchar_t**)buffer_data(output, size); + return 1; +} + +uint8_t argument_from_wchar_t(const wchar_t* input_start, const wchar_t* input_finish, + struct buffer* output, int* argc, wchar_t*** argv) +{ + return argument_append_arguments_wchar_t(input_start, input_finish, output) && + argument_create_arguments_wchar_t(output, argc, argv); +} + +#endif + +const struct range* argument_parser_get_build_file(int index) +{ + if (!is_argument_init || !buffer_size(&argument.build_files)) + { + return NULL; + } + + if (!buffer_size(&argument.build_files_)) + { + const char* start = buffer_char_data(&argument.build_files, 0); + const char* finish = buffer_char_data(&argument.build_files, 0) + buffer_size(&argument.build_files); + + while (start < finish) + { + struct range build_file; + build_file.start = find_any_symbol_like_or_not_like_that(start, finish, "\0", 1, 0, 1); + build_file.finish = find_any_symbol_like_or_not_like_that(build_file.start, finish, "\0", 1, 1, 1); + + if (range_is_null_or_empty(&build_file)) + { + break; + } + + if (!buffer_append_range(&argument.build_files_, &build_file, 1)) + { + buffer_release(&argument.build_files_); + return NULL; + } + + start = build_file.finish; + } + } + + return buffer_range_data(&argument.build_files_, index); +} + +uint8_t argument_parser_get_pause() +{ + return is_argument_init ? argument.pause : 0; +} + +uint8_t argument_parser_get_verbose() +{ + return (is_argument_init && argument.verbose < 2) ? argument.verbose : 0; +} + +uint8_t argument_parser_get_debug() +{ + return is_argument_init ? argument.debug : 0; +} + +uint8_t argument_parser_get_quiet() +{ + return is_argument_init ? argument.quiet : 0; +} + +uint8_t argument_parser_get_indent() +{ + return is_argument_init ? argument.indent : 0; +} + +const struct buffer* argument_parser_get_properties() +{ + return is_argument_init ? &argument.properties : NULL; +} + +const struct range* argument_parser_get_log_file() +{ + if (!is_argument_init || !buffer_size(&argument.log_file)) + { + return NULL; + } + + if (!buffer_size(&argument.log_file_)) + { + struct range log_file; + log_file.start = buffer_char_data(&argument.log_file, 0); + log_file.finish = buffer_char_data(&argument.log_file, buffer_size(&argument.log_file) - 1); + + if (!buffer_append_range(&argument.log_file_, &log_file, 1)) + { + buffer_release(&argument.log_file_); + return NULL; + } + } + + return buffer_range_data(&argument.log_file_, 0); +} + +uint8_t argument_parser_get_project_help() +{ + return is_argument_init ? argument.project_help : 0; +} + +uint8_t argument_parser_get_no_logo() +{ + return is_argument_init ? argument.no_logo : 0; +} + +uint8_t argument_parser_get_help() +{ + return is_argument_init ? argument.help : 0; +} diff --git a/argument_parser.h b/argument_parser.h new file mode 100644 index 0000000..5cde427 --- /dev/null +++ b/argument_parser.h @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _ARGUMENT_PARSER_H_ +#define _ARGUMENT_PARSER_H_ + +#if defined(_WIN32) +#include +#endif +#include +#include + +struct buffer; +struct range; + +uint8_t argument_parser_char(int i, int argc, char** argv); +#if defined(_WIN32) +uint8_t argument_parser_wchar_t(int i, int argc, wchar_t** argv); +#endif +void argument_parser_release(); + +uint8_t argument_get_key_and_value(const char* input_start, const char* input_finish, + struct range* key, struct range* value); +uint8_t argument_append_arguments( + const char* input_start, const char* input_finish, struct buffer* output); +uint8_t argument_create_arguments(struct buffer* output, int* argc, char*** argv); +uint8_t argument_from_char(const char* input_start, const char* input_finish, + struct buffer* output, int* argc, char*** argv); +#if defined(_WIN32) +uint8_t argument_append_arguments_wchar_t( + const wchar_t* input_start, const wchar_t* input_finish, struct buffer* output); +uint8_t argument_create_arguments_wchar_t(struct buffer* output, int* argc, wchar_t*** argv); +uint8_t argument_from_wchar_t(const wchar_t* input_start, const wchar_t* input_finish, + struct buffer* output, int* argc, wchar_t*** argv); +#endif + +const struct range* argument_parser_get_build_file(int index); +uint8_t argument_parser_get_pause(); +uint8_t argument_parser_get_verbose(); +uint8_t argument_parser_get_debug(); +uint8_t argument_parser_get_quiet(); +uint8_t argument_parser_get_indent(); +const struct buffer* argument_parser_get_properties(); +const struct range* argument_parser_get_log_file(); +uint8_t argument_parser_get_project_help(); +uint8_t argument_parser_get_no_logo(); +uint8_t argument_parser_get_help(); + +#endif diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..6b6b711 --- /dev/null +++ b/buffer.c @@ -0,0 +1,416 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "buffer.h" + +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +static struct buffer pool; +static uint8_t is_pool_init = 0; + +#if defined(_WIN64) || defined(__amd64) || defined(__x86_64) +#define CAPACITY_TYPE uint32_t +static const uint32_t maximum_capacity = (uint32_t)(INT32_MAX) + (uint32_t)1; +#else +#define CAPACITY_TYPE int32_t +static const int32_t maximum_capacity = 1073741824; +#endif + +CAPACITY_TYPE get_capacity(ptrdiff_t size) +{ + CAPACITY_TYPE capacity = 2; + + while (capacity < size && capacity < maximum_capacity) + { + capacity = capacity << 1; + } + + return capacity < maximum_capacity ? capacity : maximum_capacity; +} + +struct buffer* buffer_to_real_buffer(const void* the_buffer) +{ + struct buffer* real_buffer = NULL; + ptrdiff_t i = 0; + + if (NULL == the_buffer) + { + return real_buffer; + } + + while (NULL != (real_buffer = buffer_buffer_data(&pool, i++))) + { + if (the_buffer == real_buffer) + { + break; + } + } + + return real_buffer; +} + +ptrdiff_t buffer_size(const struct buffer* the_buffer) +{ + return NULL == the_buffer ? 0 : the_buffer->size; +} + +uint8_t buffer_resize(struct buffer* the_buffer, ptrdiff_t size) +{ + if (NULL == the_buffer || size < 0 || the_buffer->size < 0) + { + return 0; + } + + if (the_buffer->capacity < size) + { + const CAPACITY_TYPE capacity = get_capacity(size); + + if (capacity < size) + { + return 0; + } + + if (NULL != the_buffer->data) + { + free(the_buffer->data); + the_buffer->data = NULL; + the_buffer->size = 0; + the_buffer->capacity = 0; + } + + the_buffer->data = (uint8_t*)malloc(capacity); + + if (NULL == the_buffer->data) + { + return 0; + } + + the_buffer->capacity = capacity; + } + + the_buffer->size = size; + return 1; +} + +void buffer_release(struct buffer* buffer) +{ + if (NULL == buffer) + { + return; + } + + if (NULL != buffer->data) + { + free(buffer->data); + buffer->data = NULL; + } + + buffer->size = 0; + buffer->capacity = 0; +} + +void buffer_release_with_inner_buffers(struct buffer* buffer) +{ + ptrdiff_t i = 0; + struct buffer* inner_buffer = NULL; + + while (NULL != (inner_buffer = buffer_buffer_data(buffer, i++))) + { + buffer_release(inner_buffer); + } + + buffer_release(buffer); +} + +uint8_t buffer_append(struct buffer* the_buffer, const uint8_t* data, ptrdiff_t size) +{ + if (NULL == the_buffer || size < 0 || the_buffer->size < 0) + { + return 0; + } + + if (!size) + { + return 1; + } + + if (the_buffer->capacity - the_buffer->size < size) + { + if ((maximum_capacity - the_buffer->capacity) < size) + { + return 0; + } + + const ptrdiff_t new_size = the_buffer->capacity + size; + const CAPACITY_TYPE capacity = get_capacity(new_size); + + if (capacity < new_size) + { + return 0; + } + + uint8_t* new_data = (uint8_t*)malloc(capacity); + + if (NULL == new_data) + { + return 0; + } + + if (0 < the_buffer->size && NULL != the_buffer->data) + { +#if __STDC_SEC_API__ + + if (0 != memcpy_s(new_data, capacity, the_buffer->data, the_buffer->size)) + { + free(new_data); + new_data = NULL; + return 0; + } + +#else + memcpy(new_data, the_buffer->data, the_buffer->size); +#endif + } + + if (NULL != the_buffer->data) + { + free(the_buffer->data); + } + + the_buffer->data = new_data; + the_buffer->capacity = capacity; + } + + if (NULL != data) + { +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&the_buffer->data[the_buffer->size], the_buffer->capacity - the_buffer->size, data, size)) + { + return 0; + } + +#else + memcpy(&the_buffer->data[the_buffer->size], data, size); +#endif + } + + the_buffer->size += size; + return 1; +} + +uint8_t buffer_append_char(struct buffer* the_buffer, const char* data, ptrdiff_t data_count) +{ + return buffer_append(the_buffer, (const uint8_t*)data, sizeof(char) * data_count); +} + +uint8_t buffer_append_wchar_t(struct buffer* the_buffer, const wchar_t* data, ptrdiff_t data_count) +{ + return buffer_append(the_buffer, (const uint8_t*)data, sizeof(wchar_t) * data_count); +} + +uint8_t buffer_append_buffer(struct buffer* the_buffer, const struct buffer* data, ptrdiff_t data_count) +{ + return buffer_append(the_buffer, (const uint8_t*)data, sizeof(struct buffer) * data_count); +} + +uint8_t buffer_append_data_from_buffer(struct buffer* the_buffer, const struct buffer* data) +{ + return NULL == data ? 0 : buffer_append(the_buffer, data->data, data->size); +} + +uint8_t* buffer_data(const struct buffer* the_buffer, ptrdiff_t index) +{ + if (NULL == the_buffer || + NULL == the_buffer->data || + index < 0 || + the_buffer->size <= index) + { + return NULL; + } + + return &the_buffer->data[index]; +} + +char* buffer_char_data(const struct buffer* the_buffer, ptrdiff_t data_position) +{ + return (char*)buffer_data(the_buffer, sizeof(char) * data_position); +} + +wchar_t* buffer_wchar_t_data(const struct buffer* the_buffer, ptrdiff_t data_position) +{ + return (wchar_t*)buffer_data(the_buffer, sizeof(wchar_t) * data_position); +} + +uint16_t* buffer_uint16_data(const struct buffer* the_buffer, ptrdiff_t data_position) +{ + return (uint16_t*)buffer_data(the_buffer, sizeof(uint16_t) * data_position); +} + +uint32_t* buffer_uint32_data(const struct buffer* the_buffer, ptrdiff_t data_position) +{ + return (uint32_t*)buffer_data(the_buffer, sizeof(uint32_t) * data_position); +} + +struct buffer* buffer_buffer_data(const struct buffer* the_buffer, ptrdiff_t data_position) +{ + return (struct buffer*)buffer_data(the_buffer, sizeof(struct buffer) * data_position); +} + +uint8_t buffer_push_back(struct buffer* the_buffer, uint8_t data) +{ + return buffer_append(the_buffer, &data, 1); +} + +uint8_t buffer_push_back_uint16(struct buffer* the_buffer, uint16_t data) +{ + return buffer_append(the_buffer, (const uint8_t*)&data, sizeof(uint16_t)); +} + +uint8_t buffer_push_back_uint32(struct buffer* the_buffer, uint32_t data) +{ + return buffer_append(the_buffer, (const uint8_t*)&data, sizeof(uint32_t)); +} + +uint8_t buffer_shrink_to_fit(struct buffer* the_buffer) +{ + if (NULL == the_buffer || the_buffer->size < 0 || the_buffer->capacity < 512) + { + return 0; + } + + const CAPACITY_TYPE capacity = get_capacity(the_buffer->size); + + if (capacity < the_buffer->capacity) + { + uint8_t* new_data = (uint8_t*)realloc(the_buffer->data, capacity); + + if (NULL == new_data) + { + return 0; + } + + free(the_buffer->data); + the_buffer->data = new_data; + the_buffer->capacity = capacity; + return 1; + } + + return 0; +} + +uint8_t buffer_init(void** the_buffer) +{ + static const ptrdiff_t pool_limit = (ptrdiff_t)(sizeof(struct buffer) * UINT8_MAX); + struct buffer* current_buffer = NULL; + struct buffer* candidate_buffer = NULL; + ptrdiff_t i = 0; + ptrdiff_t capacity = 0; + + if (NULL == the_buffer) + { + return 0; + } + + if (!is_pool_init) + { + SET_NULL_TO_BUFFER(pool); + + if (!buffer_append(&pool, NULL, pool_limit) || + !buffer_resize(&pool, 0)) + { + buffer_release(&pool); + return 0; + } + + is_pool_init = 1; + } + + while (NULL != (current_buffer = buffer_buffer_data(&pool, i++))) + { + if (buffer_size(current_buffer) < 0 && capacity < current_buffer->capacity) + { + capacity = current_buffer->capacity; + candidate_buffer = current_buffer; + } + } + + if (NULL != candidate_buffer) + { + candidate_buffer->size = 0; + *the_buffer = candidate_buffer; + } + else if (buffer_size(&pool) < pool_limit) + { + struct buffer new_buffer; + SET_NULL_TO_BUFFER(new_buffer); + + if (!buffer_append_buffer(&pool, &new_buffer, 1)) + { + return 0; + } + else + { + *the_buffer = buffer_buffer_data(&pool, i - 1); + } + } + else + { + return 0; + } + + return NULL != *the_buffer; +} + +uint8_t buffer_return_to_pool(void* the_buffer) +{ + struct buffer* real_buffer = buffer_to_real_buffer(the_buffer); + + if (NULL == real_buffer) + { + return 0; + } + + real_buffer->size = -1; + return 1; +} + +uint8_t buffer_release_pool() +{ + if (is_pool_init) + { + struct buffer* current_buffer = NULL; + ptrdiff_t i = 0; + uint8_t pool_is_free = 1; + + while (NULL != (current_buffer = buffer_buffer_data(&pool, i++))) + { + if (pool_is_free && !(buffer_size(current_buffer) < 0)) + { + pool_is_free = 0; + } + + buffer_release(current_buffer); + } + + buffer_release(&pool); + SET_NULL_TO_BUFFER(pool); + return pool_is_free; + } + else + { + SET_NULL_TO_BUFFER(pool); + is_pool_init = 1; + } + + return 1; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..ea82f17 --- /dev/null +++ b/buffer.h @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _BUFFER_H_ +#define _BUFFER_H_ + +#include +#include +#include + +struct buffer +{ + uint8_t* data; + ptrdiff_t size; + ptrdiff_t capacity; +}; + +#define SET_NULL_TO_BUFFER(A) \ + (A).data = NULL; \ + (A).size = 0; \ + (A).capacity = 0; + +uint8_t buffer_init(void** the_buffer); + +ptrdiff_t buffer_size(const struct buffer* the_buffer); + +uint8_t buffer_resize(struct buffer* the_buffer, ptrdiff_t size); +void buffer_release(struct buffer* the_buffer); +void buffer_release_with_inner_buffers(struct buffer* the_buffer); + +uint8_t buffer_append(struct buffer* the_buffer, const uint8_t* data, ptrdiff_t size); +uint8_t buffer_append_char(struct buffer* the_buffer, const char* data, ptrdiff_t data_count); +uint8_t buffer_append_wchar_t(struct buffer* the_buffer, const wchar_t* data, ptrdiff_t data_count); +uint8_t buffer_append_buffer(struct buffer* the_buffer, const struct buffer* data, ptrdiff_t data_count); +uint8_t buffer_append_data_from_buffer(struct buffer* the_buffer, const struct buffer* data); + +uint8_t* buffer_data(const struct buffer* the_buffer, ptrdiff_t index); +char* buffer_char_data(const struct buffer* the_buffer, ptrdiff_t data_position); +wchar_t* buffer_wchar_t_data(const struct buffer* the_buffer, ptrdiff_t data_position); +uint16_t* buffer_uint16_data(const struct buffer* the_buffer, ptrdiff_t data_position); +uint32_t* buffer_uint32_data(const struct buffer* the_buffer, ptrdiff_t data_position); +struct buffer* buffer_buffer_data(const struct buffer* the_buffer, ptrdiff_t data_position); + +uint8_t buffer_push_back(struct buffer* the_buffer, uint8_t data); +uint8_t buffer_push_back_uint16(struct buffer* the_buffer, uint16_t data); +uint8_t buffer_push_back_uint32(struct buffer* the_buffer, uint32_t data); + +uint8_t buffer_shrink_to_fit(struct buffer* the_buffer); + +uint8_t buffer_return_to_pool(void* the_buffer); +uint8_t buffer_release_pool(); + +#endif diff --git a/build.build b/build.build new file mode 100644 index 0000000..3b0b021 --- /dev/null +++ b/build.build @@ -0,0 +1,96 @@ + + + Build the project https://github.com/TheVice/Ant4C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This script not design to run at NAnt environment. + You can get tool, that accept this script, here https://github.com/TheVice/Ant4C + + + diff --git a/common.c b/common.c new file mode 100644 index 0000000..5d373c9 --- /dev/null +++ b/common.c @@ -0,0 +1,284 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "common.h" +#include "conversion.h" +#include "buffer.h" +#include "range.h" +#include "string_unit.h" + +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +#define FIND_ANY_SYMBOL_LIKE_OR_NOT_LIKE_THAT(start, finish, that, that_length, like, step) \ + if ((NULL == start) || (NULL == finish) || \ + (NULL == that) || (that_length < 1) || \ + (0 != like && 1 != like) || \ + (-1 != step && 1 != step) || \ + ((0 < step) ? (finish < start) : (finish > start))) \ + { \ + return finish; \ + } \ + \ + while (start != finish) \ + { \ + uint8_t is_like = 0; \ + \ + for (ptrdiff_t i = 0; i < that_length; ++i) \ + { \ + is_like = (that[i] == (*start)); \ + \ + if (is_like) \ + { \ + break; \ + } \ + } \ + \ + if (like == is_like) \ + { \ + return start; \ + } \ + \ + start += step; \ + } \ + \ + return start; + +const char* find_any_symbol_like_or_not_like_that( + const char* start, const char* finish, const char* that, + ptrdiff_t that_length, uint8_t like, int8_t step) +{ + FIND_ANY_SYMBOL_LIKE_OR_NOT_LIKE_THAT(start, finish, that, that_length, like, step); +} + +const wchar_t* find_any_symbol_like_or_not_like_that_wchar_t( + const wchar_t* start, const wchar_t* finish, const wchar_t* that, + ptrdiff_t that_length, uint8_t like, int8_t step) +{ + FIND_ANY_SYMBOL_LIKE_OR_NOT_LIKE_THAT(start, finish, that, that_length, like, step); +} + +void replace_double_char_by_single(char* string, ptrdiff_t* length, char to_be_replaced) +{ + if (NULL == string || NULL == length || 0 == (*length)) + { + return; + } + + ptrdiff_t match = (*length); + + for (ptrdiff_t i = 0, current = -1, count = match; i < count; ++i) + { + if (i + 1 < count && to_be_replaced == string[i + 1] && to_be_replaced == string[i]) + { + --match; + + if (-1 == current) + { + current = i; + } + + continue; + } + + if (-1 != current && current != i) + { + string[current++] = string[i]; + } + } + + if (match != (*length)) + { + (*length) = match; + } +} + +uint8_t common_string_to_enum(const char* string_start, const char* string_finish, + const char** reference_strings, uint8_t max_enum_value) +{ + if (NULL == string_start || NULL == string_finish || + NULL == reference_strings || + 0 == max_enum_value || + string_finish <= string_start) + { + return max_enum_value; + } + + for (uint8_t i = 0; i < max_enum_value; ++i) + { + const size_t length = strlen(reference_strings[i]); + + if (string_equal(string_start, string_finish, + reference_strings[i], reference_strings[i] + length)) + { + return i; + } + } + + return max_enum_value; +} + +uint8_t common_append_string_to_buffer(const char* input, struct buffer* output) +{ + if (NULL == input || NULL == output) + { + return 0; + } + + return buffer_append_char(output, input, (ptrdiff_t)strlen(input)); +} + +uint8_t common_unbox_char_data(const struct buffer* box_with_data, uint8_t i, uint8_t j, + struct range* data, uint8_t terminate) +{ + struct buffer* boxed_data = NULL; + + if (NULL == box_with_data || + NULL == data || + (0 != terminate && 1 != terminate)) + { + return 0; + } + + if (NULL == (boxed_data = buffer_buffer_data(box_with_data, i))) + { + return 0; + } + + if (buffer_size(boxed_data)) + { + if (terminate && !buffer_push_back(boxed_data, '\0')) + { + return 0; + } + } + + if (NULL == (data->start = buffer_char_data(boxed_data, j))) + { + return 0; + } + + data->finish = data->start + buffer_size(boxed_data); + return 1; +} + +uint8_t common_get_one_argument(const struct buffer* arguments, struct range* argument, uint8_t terminate) +{ + if (NULL == arguments || NULL == argument) + { + return 0; + } + + return common_unbox_char_data(arguments, 0, 0, argument, terminate); +} + +uint8_t common_get_two_arguments(const struct buffer* arguments, struct range* argument1, + struct range* argument2, uint8_t terminate) +{ + if (NULL == arguments || NULL == argument1 || NULL == argument2) + { + return 0; + } + + const uint8_t ret1 = common_unbox_char_data(arguments, 0, 0, argument1, terminate); + const uint8_t ret2 = common_unbox_char_data(arguments, 1, 0, argument2, terminate); + return ret1 && ret2; +} + +uint8_t common_get_three_arguments(const struct buffer* arguments, struct range* argument1, + struct range* argument2, struct range* argument3, uint8_t terminate) +{ + if (NULL == arguments || NULL == argument1 || NULL == argument2 || NULL == argument3) + { + return 0; + } + + const uint8_t ret1 = common_unbox_char_data(arguments, 0, 0, argument1, terminate); + const uint8_t ret2 = common_unbox_char_data(arguments, 1, 0, argument2, terminate); + const uint8_t ret3 = common_unbox_char_data(arguments, 2, 0, argument3, terminate); + return ret1 && ret2 && ret3; +} + +uint8_t common_unbox_bool_data(const struct buffer* box_with_data, uint8_t i, uint8_t j, uint8_t* data) +{ + struct range char_data; + + if (!common_unbox_char_data(box_with_data, i, j, &char_data, 0)) + { + return 0; + } + + return bool_parse(char_data.start, char_data.finish, data); +} + +int64_t common_unbox_int64_data(const struct buffer* box_with_data, uint8_t i, uint8_t j) +{ + struct range int64_data; + int64_data.start = int64_data.finish = NULL; + + if (common_unbox_char_data(box_with_data, i, j, &int64_data, 1)) + { + return int64_parse(int64_data.start); + } + + return 0; +} + +uint8_t read_file(const char* file_path, struct buffer* content) +{ + if (NULL == file_path || NULL == content) + { + return 0; + } + + FILE* file_stream = NULL; +#if __STDC_SEC_API__ + + if (0 != fopen_s(&file_stream, file_path, "rb") || NULL == file_stream) +#else + if (NULL == (file_stream = fopen(file_path, "rb"))) +#endif + { + return 0; + } + + uint8_t ret = 0; + + if (0 == fseek(file_stream, 0, SEEK_END)) + { + const long file_size = ftell(file_stream); + + if (0 < file_size) + { + const ptrdiff_t size = buffer_size(content); + + if (buffer_append(content, NULL, file_size)) + { + uint8_t* ptr = buffer_data(content, size); + + if (NULL != ptr && 0 == fseek(file_stream, 0, SEEK_SET)) + { +#if __STDC_SEC_API__ && defined(_MSC_VER) + ret = (file_size == (long)fread_s(ptr, file_size, sizeof(uint8_t), + file_size, file_stream)); +#else + ret = (file_size == (long)fread(ptr, sizeof(uint8_t), + file_size, file_stream)); +#endif + } + } + } + } + + fclose(file_stream); + file_stream = NULL; + return ret; +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..c43bbc6 --- /dev/null +++ b/common.h @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include +#include +#include + +struct buffer; +struct range; + +const char* find_any_symbol_like_or_not_like_that(const char* start, const char* finish, + const char* that, ptrdiff_t that_length, uint8_t like, int8_t step); +const wchar_t* find_any_symbol_like_or_not_like_that_wchar_t(const wchar_t* start, const wchar_t* finish, + const wchar_t* that, ptrdiff_t that_length, uint8_t like, int8_t step); +void replace_double_char_by_single(char* string, ptrdiff_t* length, char to_be_replaced); + +uint8_t common_string_to_enum(const char* string_start, const char* string_finish, + const char** reference_strings, uint8_t max_enum_value); + +uint8_t common_append_string_to_buffer(const char* input, struct buffer* output); + +uint8_t common_unbox_char_data(const struct buffer* box_with_data, uint8_t i, uint8_t j, + struct range* data, uint8_t terminate); +uint8_t common_get_one_argument(const struct buffer* arguments, struct range* argument, uint8_t terminate); +uint8_t common_get_two_arguments(const struct buffer* arguments, struct range* argument1, + struct range* argument2, uint8_t terminate); +uint8_t common_get_three_arguments(const struct buffer* arguments, struct range* argument1, + struct range* argument2, struct range* argument3, uint8_t terminate); +uint8_t common_unbox_bool_data(const struct buffer* box_with_data, uint8_t i, uint8_t j, uint8_t* data); +int64_t common_unbox_int64_data(const struct buffer* box_with_data, uint8_t i, uint8_t j); + +uint8_t read_file(const char* file_path, struct buffer* content); + +#if !defined(MAX) +#define MAX(A, B) ((A) < (B) ? (B) : (A)) +#endif + +#if !defined(MIN) +#define MIN(A, B) ((A) > (B) ? (B) : (A)) +#endif + +#if defined(_WIN32) + +#define WIDE2MULTI(W, M, COUNT) \ + (COUNT) = (0 < WideCharToMultiByte(CP_UTF8, 0, (W), (COUNT), (M), (COUNT), NULL, NULL)); +#endif + +#define MULTI2WIDE(M, W, COUNT) \ + (COUNT) = (0 < MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (M), (COUNT), (W), (COUNT))); + +#endif diff --git a/conversion.c b/conversion.c new file mode 100644 index 0000000..93199e0 --- /dev/null +++ b/conversion.c @@ -0,0 +1,283 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "conversion.h" +#include "buffer.h" +#include "common.h" +#include "range.h" + +#include +#include +#include +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +static const char* False = "False"; +static const char* True = "True"; + +#define FALSE_LENGTH 5 +#define TRUE_LENGTH 4 + +uint8_t bool_parse(const char* input_string_start, const char* input_string_finish, uint8_t* bool_value) +{ + if (range_in_parts_is_null_or_empty(input_string_start, input_string_finish) || + NULL == bool_value) + { + return 0; + } + + const ptrdiff_t length = input_string_finish - input_string_start; + + if (FALSE_LENGTH == length && + (0 == memcmp(False, input_string_start, length) || + 0 == memcmp("false", input_string_start, length))) + { + *bool_value = 0; + return 1; + } + else if (TRUE_LENGTH == length && + (0 == memcmp(True, input_string_start, length) || + 0 == memcmp("true", input_string_start, length))) + { + *bool_value = 1; + return 1; + } + + return 0; +} + +uint8_t bool_to_string(uint8_t bool_value, struct buffer* output_string) +{ + if (0 == bool_value) + { + return buffer_append_char(output_string, False, FALSE_LENGTH); + } + else if (1 == bool_value) + { + return buffer_append_char(output_string, True, TRUE_LENGTH); + } + + return 0; +} + +#define DIGIT_TO_STRING_COMMON(EXPECTED_SIZE, OUTPUT) \ + if (NULL == (OUTPUT)) \ + { \ + return 0; \ + } \ + \ + const ptrdiff_t size = buffer_size(OUTPUT); \ + \ + if (!buffer_append_char((OUTPUT), NULL, (EXPECTED_SIZE))) \ + { \ + return 0; \ + } \ + \ + char* ptr = (char*)buffer_data((OUTPUT), size); + +#define DIGIT_TO_STRING(VALUE, EXPECTED_SIZE, FORMAT, OUTPUT) \ + DIGIT_TO_STRING_COMMON((EXPECTED_SIZE), (OUTPUT)) \ + return buffer_resize((OUTPUT), size + sprintf(ptr, (FORMAT), (VALUE))); + +#define DIGIT_TO_STRING_STDC_SEC_API(VALUE, EXPECTED_SIZE, FORMAT, OUTPUT) \ + DIGIT_TO_STRING_COMMON((EXPECTED_SIZE), (OUTPUT)) \ + return buffer_resize((OUTPUT), size + sprintf_s(ptr, (EXPECTED_SIZE), (FORMAT), (VALUE))); + +double double_parse(const char* value) +{ + return atof(value); +} + +uint8_t double_to_string(double double_value, struct buffer* output_string) +{ +#if __STDC_SEC_API__ + DIGIT_TO_STRING_STDC_SEC_API(double_value, 386, "%.16lf", output_string); +#else + DIGIT_TO_STRING(double_value, 386, "%.16lf", output_string); +#endif +} + +int32_t int_parse(const char* value) +{ + return atoi(value); +} + +uint8_t int_to_string(int32_t int_value, struct buffer* output_string) +{ +#if __STDC_SEC_API__ + DIGIT_TO_STRING_STDC_SEC_API(int_value, 12, "%i", output_string); +#else + DIGIT_TO_STRING(int_value, 12, "%i", output_string); +#endif +} + +long long_parse(const char* value) +{ + return atol(value); +} + +long long_parse_wchar_t(const wchar_t* value) +{ + wchar_t* ch = NULL; + return wcstol(value, &ch, 10); +} + +uint8_t long_to_string(long long_value, struct buffer* output_string) +{ +#if __STDC_SEC_API__ + DIGIT_TO_STRING_STDC_SEC_API(long_value, 24, "%ld", output_string); +#else + DIGIT_TO_STRING(long_value, 24, "%ld", output_string); +#endif +} + +int64_t int64_parse(const char* value) +{ + return atoll(value); +} + +int64_t int64_parse_wchar_t(const wchar_t* value) +{ + wchar_t* ch = NULL; + return (int64_t)wcstoimax(value, &ch, 10); +} + +uint8_t int64_to_string(int64_t int_value, struct buffer* output_string) +{ +#if __STDC_SEC_API__ + DIGIT_TO_STRING_STDC_SEC_API(int_value, 24, "%"PRId64, output_string); +#else + DIGIT_TO_STRING(int_value, 24, "%"PRId64, output_string); +#endif +} + +static const char* conversion_str[] = +{ + "parse", "to-string" +}; + +enum conversion_function +{ + parse, to_string, + UNKNOWN_CONVERSION +}; + +uint8_t conversion_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, conversion_str, UNKNOWN_CONVERSION); +} + +uint8_t bool_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_CONVERSION <= function || NULL == arguments || 1 != arguments_count || NULL == output) + { + return 0; + } + + struct range argument; + + if (!common_get_one_argument(arguments, &argument, 0)) + { + return 0; + } + + switch (function) + { + case parse: + case to_string: + { + uint8_t bool_value = 0; + + if (!bool_parse(argument.start, argument.finish, &bool_value)) + { + break; + } + + return bool_to_string(bool_value, output); + } + + case UNKNOWN_CONVERSION: + default: + break; + } + + return 0; +} + +uint8_t conversion_exec_function(uint8_t name_space, uint8_t function, + const struct buffer* arguments, uint8_t arguments_count, struct buffer* output) +{ + if (UNKNOWN_CONVERSION <= function || NULL == arguments || 1 != arguments_count || NULL == output) + { + return 0; + } + + struct range argument; + + if (!common_get_one_argument(arguments, &argument, 1)) + { + return 0; + } + + switch (function) + { + case parse: + case to_string: + if (1 == name_space) + { + return double_to_string(double_parse(argument.start), output); + } + else if (2 == name_space) + { + return int_to_string(int_parse(argument.start), output); + } + +#if !defined(_WIN32) + else if (3 == name_space) + { + return long_to_string(long_parse(argument.start), output); + } + +#endif + return int64_to_string(int64_parse(argument.start), output); + + case UNKNOWN_CONVERSION: + default: + break; + } + + return 0; +} + +uint8_t double_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + return conversion_exec_function(1, function, arguments, arguments_count, output); +} + +uint8_t int_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + return conversion_exec_function(2, function, arguments, arguments_count, output); +} + +uint8_t long_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + return conversion_exec_function(3, function, arguments, arguments_count, output); +} + +uint8_t int64_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + return conversion_exec_function(4, function, arguments, arguments_count, output); +} diff --git a/conversion.h b/conversion.h new file mode 100644 index 0000000..5abe80f --- /dev/null +++ b/conversion.h @@ -0,0 +1,45 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _CONVERSION_H_ +#define _CONVERSION_H_ + +#include +#include + +struct buffer; + +uint8_t bool_parse(const char* input_string_start, const char* input_string_finish, uint8_t* bool_value); +uint8_t bool_to_string(uint8_t bool_value, struct buffer* output_string); + +double double_parse(const char* value); +uint8_t double_to_string(double double_value, struct buffer* output_string); + +int32_t int_parse(const char* value); +uint8_t int_to_string(int32_t int_value, struct buffer* output_string); + +long long_parse(const char* value); +long long_parse_wchar_t(const wchar_t* value); +uint8_t long_to_string(long long_value, struct buffer* output_string); + +int64_t int64_parse(const char* value); +int64_t int64_parse_wchar_t(const wchar_t* value); +uint8_t int64_to_string(int64_t int_value, struct buffer* output_string); + +uint8_t conversion_get_function(const char* name_start, const char* name_finish); +uint8_t bool_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t double_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t int_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t long_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t int64_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/date_time.c b/date_time.c new file mode 100644 index 0000000..9a0ba3e --- /dev/null +++ b/date_time.c @@ -0,0 +1,838 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "date_time.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "range.h" + +#include +#include +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +#define LOCAL_TIME(T) \ + struct tm* tm_ = localtime((const time_t* const)(T)); + +#define LOCAL_TIME_STDC_SEC_API(T) \ + struct tm tm__; \ + struct tm* tm_ = &tm__; \ + \ + if (0 != localtime_s(tm_, (const time_t* const)(T))) \ + { \ + return 0; \ + } + +static const int32_t seconds_per_day = 86400; +static const int16_t seconds_per_hour = 3600; +static const int8_t seconds_per_minute = 60; + +uint8_t datetime_decode_to_tm(int64_t time, struct tm* tm_) +{ + if (NULL == tm_) + { + return 0; + } + + memset(tm_, 0, sizeof(struct tm)); + tm_->tm_year = 1970; + tm_->tm_mday = 1; + tm_->tm_yday = 1; + /**/ + int64_t this_time = 0; + + while (1) + { + int64_t year = (int64_t)seconds_per_day * (datetime_is_leap_year(tm_->tm_year) ? 366 : 365); + + if (time < this_time + year) + { + break; + } + + this_time += year; + ++tm_->tm_year; + } + + while (1) + { + uint8_t days = datetime_get_days_in_month(tm_->tm_year, 1 + (uint8_t)tm_->tm_mon); + int64_t month = (int64_t)seconds_per_day * days; + + if (time < this_time + month) + { + break; + } + + this_time += month; + ++tm_->tm_mon; + tm_->tm_yday += days; + } + + while (1) + { + if (time < this_time + seconds_per_day) + { + break; + } + + this_time += seconds_per_day; + ++tm_->tm_mday; + ++tm_->tm_yday; + } + + while (1) + { + if (time < this_time + seconds_per_hour) + { + break; + } + + this_time += seconds_per_hour; + ++tm_->tm_hour; + } + + while (1) + { + if (time < this_time + seconds_per_minute) + { + break; + } + + this_time += seconds_per_minute; + ++tm_->tm_min; + } + + this_time = (time - this_time); + + if (60 < this_time) + { + return 0; + } + + tm_->tm_sec = (int)this_time; + tm_->tm_mon += 1; + return 1; +} + +uint8_t datetime_format_to_string(int64_t input, const char* format, struct buffer* output) +{ + struct tm tm_; + + if (NULL == format || NULL == output || !datetime_decode_to_tm(input, &tm_)) + { + return 0; + } + + const ptrdiff_t size = buffer_size(output); + + if (!buffer_append(output, NULL, INT8_MAX)) + { + return 0; + } + + tm_.tm_year -= 1900; + tm_.tm_mon -= 1; + const ptrdiff_t new_size = strftime((char*)buffer_data(output, size), INT8_MAX, format, &tm_); + + if (!new_size) + { + return 0; + } + + return buffer_resize(output, size + new_size); +} + +uint8_t datetime_parse(const char* input_start, const char* input_finish, + uint32_t* year, uint8_t* month, uint8_t* day, + uint8_t* hour, uint8_t* minute, uint8_t* second) +{ + if (range_in_parts_is_null_or_empty(input_start, input_finish) || + NULL == year || NULL == month || NULL == day || + NULL == hour || NULL == minute || NULL == second) + { + return 0; + } + + uint8_t step = 0; + uint8_t* ptr[6]; + ptr[0] = day; + ptr[1] = month; + ptr[2] = NULL; + ptr[3] = hour; + ptr[4] = minute; + ptr[5] = second; + + while (input_start != input_finish && step < 6) + { + input_start = find_any_symbol_like_or_not_like_that(input_start, input_finish, "0123456789", 10, 1, 1); + + if (input_start == input_finish) + { + break; + } + + const int32_t value = int_parse(input_start); + + if (2 == step) + { + (*year) = value; + } + else + { + (*ptr[step]) = (uint8_t)value; + } + + ++step; + input_start = find_any_symbol_like_or_not_like_that(input_start + 1, input_finish, "0123456789", 10, 0, 1); + } + + return (0 < *day && *day < 32 && 1 < *month && *month < 31 && *hour < 24 && *minute < 60 && *second < 60); +} + +uint8_t datetime_to_char_array(const struct tm* tm_, char* output) +{ + if (NULL == tm_ || NULL == output) + { + return 0; + } + + uint8_t length = 0; +#if __STDC_SEC_API__ + length += (uint8_t)sprintf_s(output + length, 75 - length, 9 < tm_->tm_mday ? "%i." : "0%i.", tm_->tm_mday); + length += (uint8_t)sprintf_s(output + length, 75 - length, 9 < tm_->tm_mon ? "%i." : "0%i.", tm_->tm_mon); + length += (uint8_t)sprintf_s(output + length, 75 - length, "%i ", tm_->tm_year); + /**/ + length += (uint8_t)sprintf_s(output + length, 75 - length, "%i:", tm_->tm_hour); + length += (uint8_t)sprintf_s(output + length, 75 - length, 9 < tm_->tm_min ? "%i:" : "0%i:", tm_->tm_min); + length += (uint8_t)sprintf_s(output + length, 75 - length, 9 < tm_->tm_sec ? "%i" : "0%i", tm_->tm_sec); +#else + length += (uint8_t)sprintf(output + length, 9 < tm_->tm_mday ? "%i." : "0%i.", tm_->tm_mday); + length += (uint8_t)sprintf(output + length, 9 < tm_->tm_mon ? "%i." : "0%i.", tm_->tm_mon); + length += (uint8_t)sprintf(output + length, "%i ", tm_->tm_year); + /**/ + length += (uint8_t)sprintf(output + length, "%i:", tm_->tm_hour); + length += (uint8_t)sprintf(output + length, 9 < tm_->tm_min ? "%i:" : "0%i:", tm_->tm_min); + length += (uint8_t)sprintf(output + length, 9 < tm_->tm_sec ? "%i" : "0%i", tm_->tm_sec); +#endif + return length; +} + +uint8_t datetime_to_string(uint32_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second, + struct buffer* output) +{ + if (year < 1970 || INT32_MAX < year || + month < 1 || 12 < month || + day < 1 || 31 < day || + 23 < hour || 59 < minute || + 60 < second || + NULL == output) + { + return 0; + } + + const ptrdiff_t size = buffer_size(output); + + if (!buffer_append(output, NULL, 75)) + { + return 0; + } + + struct tm tm_; + + tm_.tm_year = year; + + tm_.tm_mon = month; + + tm_.tm_mday = day; + + tm_.tm_hour = hour; + + tm_.tm_min = minute; + + tm_.tm_sec = second; + + char* ptr = (char*)buffer_data(output, size); + + return buffer_resize(output, size + datetime_to_char_array(&tm_, ptr)); +} + +uint8_t datetime_get_day(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return 0; + } + + return (uint8_t)tm_.tm_mday; +} + +uint8_t datetime_get_day_of_week(int64_t input) +{ + (void)input; + return UINT8_MAX; + /*TODO: + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return 0; + } + + return (uint8_t)tm_.tm_wday;*/ +} + +uint16_t datetime_get_day_of_year(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT16_MAX; + } + + return (uint16_t)tm_.tm_yday; +} + +uint8_t datetime_get_days_in_month(uint32_t year, uint8_t month) +{ + if (month < 1 || 12 < month) + { + return 0; + } + + if (2 == month) + { + return datetime_is_leap_year(year) ? 29 : 28; + } + + return (4 == month || 6 == month || 9 == month || 11 == month) ? 30 : 31; +} + +uint8_t datetime_get_hour(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT8_MAX; + } + + return (uint8_t)tm_.tm_hour; +} +/*uint16_t datetime_get_millisecond(int64_t input)*/ +uint8_t datetime_get_minute(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT8_MAX; + } + + return (uint8_t)tm_.tm_min; +} + +uint8_t datetime_get_month(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT8_MAX; + } + + return (uint8_t)tm_.tm_mon; +} + +uint8_t datetime_get_second(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT8_MAX; + } + + return (uint8_t)tm_.tm_sec; +} +/*int64_t datetime_get_ticks(int64_t input);*/ +uint32_t datetime_get_year(int64_t input) +{ + struct tm tm_; + + if (!datetime_decode_to_tm(input, &tm_)) + { + return UINT8_MAX; + } + + return (uint32_t)tm_.tm_year; +} + +uint8_t datetime_is_leap_year(uint32_t year) +{ + return ((!(year % 400)) || ((year % 100) && (!(year % 4)))); +} + +int64_t datetime_now_utc(time_t* now) +{ + if (NULL == now || ((time_t) -1) == time(now)) + { + return 0; + } + + /*TODO: gmtime(); + gmtime_s();*/ + return (*now); +} + +int64_t datetime_now() +{ + time_t now = 0; + + if (!datetime_now_utc(&now)) + { + return 0; + } + +#if __STDC_SEC_API__ + LOCAL_TIME_STDC_SEC_API(&now); +#else + LOCAL_TIME(&now); +#endif + return datetime_encode(1900 + tm_->tm_year, 1 + (uint8_t)tm_->tm_mon, (uint8_t)tm_->tm_mday, + (uint8_t)tm_->tm_hour, (uint8_t)tm_->tm_min, (uint8_t)tm_->tm_sec); +} + +int64_t datetime_encode(uint32_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second) +{ + if (year < 1970 || INT32_MAX < year || + month < 1 || 12 < month || + day < 1 || 31 < day || + 23 < hour || 59 < minute || + 60 < second) + { + return 0; + } + + int64_t this_time = 0; + + for (uint32_t y = 1970; y < year; ++y) + { + this_time += (int64_t)seconds_per_day * (datetime_is_leap_year(y) ? 366 : 365); + } + + for (uint8_t m = 1; m < month; ++m) + { + uint8_t days = datetime_get_days_in_month(year, m); + this_time += (int64_t)seconds_per_day * days; + } + + for (uint8_t d = 1; d < day; ++d) + { + this_time += seconds_per_day; + } + + for (uint8_t h = 0; h < hour; ++h) + { + this_time += seconds_per_hour; + } + + for (uint8_t m = 0; m < minute; ++m) + { + this_time += seconds_per_minute; + } + + this_time += second; + return this_time; +} + +uint8_t datetime_decode(int64_t time, uint32_t* year, uint8_t* month, uint8_t* day, + uint8_t* hour, uint8_t* minute, uint8_t* second, uint16_t* year_day) +{ + if (NULL == year && NULL == month && NULL == day && + NULL == hour && NULL == minute && NULL == second && + NULL == year_day) + { + return 0; + } + + struct tm tm_; + + if (!datetime_decode_to_tm(time, &tm_)) + { + return 0; + } + + if (year) + { + (*year) = tm_.tm_year; + } + + if (month) + { + (*month) = (uint8_t)tm_.tm_mon; + } + + if (day) + { + (*day) = (uint8_t)tm_.tm_mday; + } + + if (hour) + { + (*hour) = (uint8_t)tm_.tm_hour; + } + + if (minute) + { + (*minute) = (uint8_t)tm_.tm_min; + } + + if (second) + { + (*second) = (uint8_t)tm_.tm_sec; + } + + if (year_day) + { + (*year_day) = (uint16_t)tm_.tm_yday; + } + + return 1; +} + +int64_t timespan_from_days(double input) +{ + return (int64_t)((double)seconds_per_day * input); +} + +int64_t timespan_from_hours(double input) +{ + return (int64_t)((double)seconds_per_hour * input); +} +/*timespan_from_milliseconds*/ +int64_t timespan_from_minutes(double input) +{ + return (int64_t)((double)seconds_per_minute * input); +} + +int64_t timespan_from_seconds(double input) +{ + return (int64_t)input; +} +/*timespan_from_ticks*/ +int32_t timespan_get_days(int64_t input) +{ + int32_t days = 0; + + for (int64_t this_time = seconds_per_day; this_time <= input; ++days, this_time += seconds_per_day); + + return days; +} + +int32_t timespan_get_hours(int64_t input) +{ + int32_t hours = 0; + + for (int64_t this_time = seconds_per_hour; this_time <= input; ++hours, this_time += seconds_per_hour); + + return hours; +} +/*timespan_get_milliseconds*/ +int32_t timespan_get_minutes(int64_t input) +{ + int32_t minutes = 0; + + for (int64_t this_time = seconds_per_minute; this_time <= input; ++minutes, this_time += seconds_per_minute); + + return minutes; +} +/*timespan_get_seconds*/ +/*timespan_get_ticks*/ +double timespan_get_total_days(int64_t input) +{ + return (double)input / seconds_per_day; +} + +double timespan_get_total_hours(int64_t input) +{ + return (double)input / seconds_per_hour; +} +/*timespan_get_total_milliseconds*/ +double timespan_get_total_minutes(int64_t input) +{ + return (double)input / seconds_per_minute; +} + +static const char* datetime_function_str[] = +{ + "format-to-string", "parse", "to-string", "get-day", "get-day-of-week", + "get-day-of-year", "get-days-in-month", "get-hour", "get-millisecond", + "get-minute", "get-month", "get-second", "get-ticks", "get-year", + "is-leap-year", "now", "from-input" +}; + +enum datetime_function +{ + format_to_string, parse, to_string, get_day, get_day_of_week, + get_day_of_year, get_days_in_month, get_hour, get_millisecond, + get_minute, get_month, get_second, get_ticks, get_year, + is_leap_year, now, encode, + UNKNOWN_DATETIME_FUNCTION +}; + +uint8_t datetime_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, datetime_function_str, UNKNOWN_DATETIME_FUNCTION); +} + +uint8_t datetime_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_DATETIME_FUNCTION <= function || NULL == arguments || 2 < arguments_count || NULL == output) + { + return 0; + } + + struct range argument1; + + struct range argument2; + + argument1.start = argument2.start = argument1.finish = argument2.finish = NULL; + + if (1 == arguments_count) + { + if (!common_get_one_argument(arguments, &argument1, 1)) + { + return 0; + } + } + else if (2 == arguments_count) + { + if (!common_get_two_arguments(arguments, &argument1, &argument2, 1)) + { + return 0; + } + } + + switch (function) + { + case format_to_string: + return 2 == arguments_count && + datetime_format_to_string(int64_parse(argument1.start), argument2.start, output); + + case parse: + case to_string: + { + uint32_t year = 0; + uint8_t month = 0; + uint8_t day = 0; + uint8_t hour = 0; + uint8_t minute = 0; + uint8_t second = 0; + + if (1 != arguments_count || + !datetime_parse(argument1.start, argument1.finish, &year, &month, &day, &hour, &minute, &second)) + { + break; + } + + return datetime_to_string(year, month, day, hour, minute, second, output); + } + + case get_day: + return 1 == arguments_count && int_to_string(datetime_get_day(int64_parse(argument1.start)), output); + + case get_day_of_week: + /*TODO: + return 1 == arguments_count && int_to_string(datetime_get_day_of_week(int64_parse(argument1.start)), output);*/ + break; + + case get_day_of_year: + return 1 == arguments_count && int_to_string(datetime_get_day_of_year(int64_parse(argument1.start)), output); + + case get_days_in_month: + return 2 == arguments_count && + int_to_string(datetime_get_days_in_month(int_parse(argument1.start), (uint8_t)int_parse(argument2.start)), + output); + + case get_hour: + return 1 == arguments_count && int_to_string(datetime_get_hour(int64_parse(argument1.start)), output); + + case get_millisecond: + break; + + case get_minute: + return 1 == arguments_count && int_to_string(datetime_get_minute(int64_parse(argument1.start)), output); + + case get_month: + return 1 == arguments_count && int_to_string(datetime_get_month(int64_parse(argument1.start)), output); + + case get_second: + return 1 == arguments_count && int_to_string(datetime_get_second(int64_parse(argument1.start)), output); + + case get_ticks: + break; + + case get_year: + return 1 == arguments_count && int_to_string(datetime_get_year(int64_parse(argument1.start)), output); + + case is_leap_year: + return 1 == arguments_count && bool_to_string(datetime_is_leap_year(int_parse(argument1.start)), output); + + case now: + if (!arguments_count) + { + int64_t time_is_now = datetime_now(); + return 0 < time_is_now && int64_to_string(time_is_now, output); + } + + break; + + case encode: + { + uint32_t year = 0; + uint8_t month = 0; + uint8_t day = 0; + uint8_t hour = 0; + uint8_t minute = 0; + uint8_t second = 0; + + if (1 != arguments_count || + !datetime_parse(argument1.start, argument1.finish, &year, &month, &day, &hour, &minute, &second)) + { + break; + } + + return int64_to_string(datetime_encode(year, month, day, hour, minute, second), output); + } + + case UNKNOWN_DATETIME_FUNCTION: + default: + break; + } + + return 0; +} + +static const char* timespan_function_str[] = +{ + "parse", "to-string", "from-days", "from-hours", + "from-milliseconds", "from-minutes", "from-seconds", + "from-ticks", "get-days", "get-hours", + "get-milliseconds", "get-minutes", "get-seconds", + "get-ticks", "get-total-days", "get-total-hours", + "get-total-milliseconds", "get-total-minutes", + "get-total-seconds" +}; + +enum timespan_function +{ + ts_parse_, ts_to_string_, ts_from_days_, ts_from_hours_, + ts_from_milliseconds_, ts_from_minutes_, ts_from_seconds_, + ts_from_ticks_, ts_get_days_, ts_get_hours_, + ts_get_milliseconds_, ts_get_minutes_, ts_get_seconds_, + ts_get_ticks_, ts_get_total_days_, ts_get_total_hours_, + ts_get_total_milliseconds_, ts_get_total_minutes_, + ts_get_total_seconds_, UNKNOWN_TIMESPAN_FUNCTION +}; + +uint8_t timespan_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, timespan_function_str, UNKNOWN_TIMESPAN_FUNCTION); +} + +uint8_t timespan_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_TIMESPAN_FUNCTION <= function || NULL == arguments || 1 != arguments_count || NULL == output) + { + return 0; + } + + struct range argument; + + argument.start = argument.finish = NULL; + + if (!common_get_one_argument(arguments, &argument, 1)) + { + return 0; + } + + /*double double_argument = 0; + int64_t int64_argument = 0;*/ + + switch (function) + { + case ts_from_days_: + return int64_to_string(timespan_from_days(double_parse(argument.start)), output); + + case ts_from_hours_: + return int64_to_string(timespan_from_hours(double_parse(argument.start)), output); + + case ts_from_milliseconds_: + break; + + case ts_from_minutes_: + return int64_to_string(timespan_from_minutes(double_parse(argument.start)), output); + + case ts_from_seconds_: + return int64_to_string((int64_t)double_parse(argument.start), output); + + case ts_from_ticks_: + break; + + case ts_get_days_: + return int_to_string(timespan_get_days(int64_parse(argument.start)), output); + + case ts_get_hours_: + return int_to_string(timespan_get_hours(int64_parse(argument.start)), output); + + case ts_get_milliseconds_: + break; + + case ts_get_minutes_: + return int_to_string(timespan_get_minutes(int64_parse(argument.start)), output); + + case ts_parse_: + case ts_to_string_: + case ts_get_seconds_: + case ts_get_total_seconds_: + return int64_to_string(int64_parse(argument.start), output); + + case ts_get_ticks_: + break; + + case ts_get_total_days_: + return double_to_string(timespan_get_total_days(int64_parse(argument.start)), output); + + case ts_get_total_hours_: + return double_to_string(timespan_get_total_hours(int64_parse(argument.start)), output); + + case ts_get_total_milliseconds_: + break; + + case ts_get_total_minutes_: + return double_to_string(timespan_get_total_minutes(int64_parse(argument.start)), output); + + case UNKNOWN_TIMESPAN_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/date_time.h b/date_time.h new file mode 100644 index 0000000..ce4c7cd --- /dev/null +++ b/date_time.h @@ -0,0 +1,65 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _DATE_TIME_H_ +#define _DATE_TIME_H_ + +#include + +struct buffer; + +uint8_t datetime_format_to_string(int64_t input, const char* format, struct buffer* output); +uint8_t datetime_parse(const char* input_start, const char* input_finish, + uint32_t* year, uint8_t* month, uint8_t* day, + uint8_t* hour, uint8_t* minute, uint8_t* second); +uint8_t datetime_to_string(uint32_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second, + struct buffer* output); + +uint8_t datetime_get_day(int64_t input); +uint8_t datetime_get_day_of_week(int64_t input); +uint16_t datetime_get_day_of_year(int64_t input); + +uint8_t datetime_get_days_in_month(uint32_t year, uint8_t month); + +uint8_t datetime_get_hour(int64_t input); +/*uint16_t datetime_get_millisecond(int64_t input);*/ +uint8_t datetime_get_minute(int64_t input); +uint8_t datetime_get_month(int64_t input); +uint8_t datetime_get_second(int64_t input); +/*int64_t datetime_get_ticks(int64_t input);*/ +uint32_t datetime_get_year(int64_t input); + +uint8_t datetime_is_leap_year(uint32_t year); + +int64_t datetime_now(); + +int64_t datetime_encode(uint32_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); +uint8_t datetime_decode(int64_t time, uint32_t* year, uint8_t* month, uint8_t* day, + uint8_t* hour, uint8_t* minute, uint8_t* second, uint16_t* year_day); + +int64_t timespan_from_days(double input); +int64_t timespan_from_hours(double input); +int64_t timespan_from_minutes(double input); +int64_t timespan_from_seconds(double input); +int32_t timespan_get_days(int64_t input); +int32_t timespan_get_hours(int64_t input); +int32_t timespan_get_minutes(int64_t input); +double timespan_get_total_days(int64_t input); +double timespan_get_total_hours(int64_t input); +double timespan_get_total_minutes(int64_t input); + +uint8_t datetime_get_function(const char* name_start, const char* name_finish); +uint8_t datetime_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +uint8_t timespan_get_function(const char* name_start, const char* name_finish); +uint8_t timespan_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/echo.c b/echo.c new file mode 100644 index 0000000..ecd3544 --- /dev/null +++ b/echo.c @@ -0,0 +1,362 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "echo.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "interpreter.h" +#include "range.h" +#include "xml.h" + +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +#define INFO_LABEL "[Info]: " +#define DEBUG_LABEL "[Debug]: " +#define ERROR_LABEL "[Error]: " +#define VERBOSE_LABEL "[Verbose]: " +#define WARNING_LABEL "[Warning]: " + +#define INFO_LENGTH 8 +#define DEBUG_LENGTH 9 +#define ERROR_LENGTH 9 +#define VERBOSE_LENGTH 11 +#define WARNING_LENGTH 11 + +uint8_t echo(uint8_t append, uint8_t encoding, const char* file, + uint8_t level, const char* message, ptrdiff_t message_length, + uint8_t new_line, uint8_t verbose) +{ + uint8_t result = 0; + FILE* file_stream = NULL; + /**/ + (void)encoding; + (void)verbose;/*TODO: */ + + if (None == level) + { + return 1; + } + + if (NULL != file) + { +#if __STDC_SEC_API__ + result = (0 == fopen_s(&file_stream, file, append ? "ab" : "wb") && NULL != file_stream); +#else + file_stream = fopen(file, append ? "ab" : "wb"); + result = (NULL != file_stream); +#endif + } + else + { + size_t was_written = 0; + file_stream = (Error != level) ? stdout : stderr; + + switch (level) + { + case Error: + was_written = fwrite(ERROR_LABEL, sizeof(char), ERROR_LENGTH, file_stream); + result = (ERROR_LENGTH == was_written); + break; + + case Debug: + was_written = fwrite(DEBUG_LABEL, sizeof(char), DEBUG_LENGTH, file_stream); + result = (DEBUG_LENGTH == was_written); + break; + + case Info: + was_written = fwrite(INFO_LABEL, sizeof(char), INFO_LENGTH, file_stream); + result = (INFO_LENGTH == was_written); + break; + + case Verbose: + was_written = fwrite(VERBOSE_LABEL, sizeof(char), VERBOSE_LENGTH, file_stream); + result = (VERBOSE_LENGTH == was_written); + break; + + case Warning: + was_written = fwrite(WARNING_LABEL, sizeof(char), WARNING_LENGTH, file_stream); + result = (WARNING_LENGTH == was_written); + break; + + case NoLevel: + result = 1; + break; + + default: + return 0; + } + } + + if (result) + { + if (NULL != message && + 0 < message_length) + { + result = (message_length == (ptrdiff_t)fwrite(message, sizeof(char), message_length, file_stream)); + } + } + else + { + return 0; + } + + if (NULL != file) + { + result = result && (0 == fclose(file_stream)); + } + else if (result && new_line) + { + result = result && (1 == fwrite("\n", sizeof(char), 1, file_stream)); + } + + file_stream = NULL; + return result; +} + +#define APPEND_POSITION 0 +#define ENCODING_POSITION 1 +#define FILE_POSITION 2 +#define LEVEL_POSITION 3 +#define MESSAGE_POSITION 4 +#define FAIL_ON_ERROR_POSITION 5 +#define VERBOSE_POSITION 6 + +uint8_t echo_get_arguments_for_task( + const void* project, + const void* target, + const char* attributes_start, + const char* attributes_finish, + const char* element_finish, + struct buffer* arguments) +{ + if (range_in_parts_is_null_or_empty(attributes_start, attributes_finish) || + NULL == element_finish || element_finish < attributes_finish || + NULL == arguments) + { + return 0; + } + + static const char* attributes[] = { "append", "encoding", "file", "level", "message", "failonerror", "verbose" }; + static const uint8_t attributes_lengths[] = { 6, 8, 4, 5, 7, 11, 7 }; + /**/ + static const uint8_t default_append_value = 0; + static const char* default_encoding_value = "UTF8"; + static const uint8_t default_encoding_value_length = 4; + static const char* default_file_value = NULL; + static const char* defalut_level_value = "Info"; + static const uint8_t defalut_level_value_length = 4; + static const char* default_message_value = NULL; + static const uint8_t default_fail_on_error_value = 1; + static const uint8_t default_verbose_value = 0; + /**/ + struct buffer argument; + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_append_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_encoding_value, default_encoding_value_length) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_file_value, 0) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, defalut_level_value, defalut_level_value_length) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_message_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_fail_on_error_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_verbose_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + if (!interpreter_get_arguments_from_xml_tag_record(project, target, attributes_start, attributes_finish, + attributes, attributes_lengths, sizeof(attributes_lengths) / sizeof(*attributes_lengths), arguments)) + { + return 0; + } + + struct buffer* message = buffer_buffer_data(arguments, MESSAGE_POSITION); + + if (NULL == message) + { + return 0; + } + + if (default_message_value == buffer_char_data(message, 0)) + { + struct range value; + + if (!xml_get_element_value_from_parts(attributes_finish, element_finish, &value)) + { + return 0; + } + + if ((value.start < value.finish) && (!buffer_resize(message, 0) || + !buffer_append_data_from_range(message, &value))) + { + return 0; + } + } + + return 1; +} + +static const char* echo_encoding_str[] = { "UTF7", "BigEndianUnicode", "Unicode", "Default", "ASCII", "UTF8", "UTF32" }; +#define ECHO_UNKNOWN_ENCODING (UTF32 + 1) + +uint8_t echo_get_encoding(const char* encoding_start, const char* encoding_finish) +{ + return common_string_to_enum(encoding_start, encoding_finish, echo_encoding_str, ECHO_UNKNOWN_ENCODING); +} + +static const char* echo_level_str[] = { "Debug", "Error", "Info", "None", "Verbose", "Warning", "NoLevel" }; +#define ECHO_UNKNOWN_LEVEL (NoLevel + 1) + +uint8_t echo_get_level(const char* level_start, const char* level_finish) +{ + return common_string_to_enum(level_start, level_finish, echo_level_str, ECHO_UNKNOWN_LEVEL); +} + +uint8_t echo_evaluate_task(const void* project, const void* target, + const char* attributes_start, const char* attributes_finish, + const char* element_finish) +{ + struct buffer arguments; + SET_NULL_TO_BUFFER(arguments); + + if (!buffer_resize(&arguments, UINT8_MAX) || !buffer_resize(&arguments, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!echo_get_arguments_for_task(project, target, + attributes_start, attributes_finish, element_finish, &arguments)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + uint8_t append = 0; + + if (!common_unbox_bool_data(&arguments, APPEND_POSITION, 0, &append)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + struct range value; + + value.start = value.finish = NULL; + + if (!common_unbox_char_data(&arguments, ENCODING_POSITION, 0, &value, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + const uint8_t encoding = echo_get_encoding(value.start, value.finish); + + if (ECHO_UNKNOWN_ENCODING == encoding) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + const char* file = NULL; + + if (common_unbox_char_data(&arguments, FILE_POSITION, 0, &value, 1)) + { + file = value.start; + } + + if (!common_unbox_char_data(&arguments, LEVEL_POSITION, 0, &value, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + const uint8_t level = echo_get_level(value.start, value.finish); + + if (ECHO_UNKNOWN_LEVEL == level) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!common_unbox_char_data(&arguments, MESSAGE_POSITION, 0, &value, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + const char* message = value.start; + const ptrdiff_t message_length = range_size(&value); + /**/ + uint8_t fail_on_error = 1; + uint8_t new_line = 1; + uint8_t verbose = 0; + + if (!common_unbox_bool_data(&arguments, FAIL_ON_ERROR_POSITION, 0, &fail_on_error) || + !common_unbox_bool_data(&arguments, VERBOSE_POSITION, 0, &verbose)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + new_line = echo(append, encoding, file, level, message, message_length, new_line, verbose); + buffer_release_with_inner_buffers(&arguments); + /*TODO: comment about fail_on_error, if verbose mode, + and manipulate of return value. + return fail_on_error ? new_line : 1;*/ + return new_line; +} diff --git a/echo.h b/echo.h new file mode 100644 index 0000000..893c699 --- /dev/null +++ b/echo.h @@ -0,0 +1,23 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _ECHO_H_ +#define _ECHO_H_ + +#include +#include + +enum Encoding { UTF7, BigEndianUnicode, Unicode, Default, ASCII, UTF8, UTF32 }; +enum Level { Debug, Error, Info, None, Verbose, Warning, NoLevel }; + +uint8_t echo(uint8_t append, uint8_t encoding, const char* file, uint8_t level, + const char* message, ptrdiff_t message_length, uint8_t new_line, uint8_t verbose); +uint8_t echo_evaluate_task(const void* project, const void* target, + const char* attributes_start, const char* attributes_finish, + const char* element_finish); + +#endif diff --git a/environment.c b/environment.c new file mode 100644 index 0000000..e9f7b1e --- /dev/null +++ b/environment.c @@ -0,0 +1,1173 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "environment.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "operating_system.h" +#include "range.h" + +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +static struct OperatingSystem operating_system; +static uint8_t is_data_of_operating_system_filled = 0; + +#define ENVIRONMENT_UNKNOWN_SPECIAL_FOLDER (CDBurning + 1) + +#if defined(_WIN32) +#include + +#include +#include +#include + +#ifndef S_OK +#define S_OK ((HRESULT)0L) +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1800) +#include +#endif + +uint8_t environment_get_folder_path(enum SpecialFolder folder, struct buffer* path) +{ + if (ENVIRONMENT_UNKNOWN_SPECIAL_FOLDER <= folder || NULL == path) + { + return 0; + } + +#if defined(_WIN32_WINNT_VISTA) && defined(_WIN32_WINNT) && (_WIN32_WINNT_VISTA <= _WIN32_WINNT) + GUID folderID; + + switch (folder) + { + case Desktop: + case DesktopDirectory: + folderID = FOLDERID_Desktop; + break; + + case Programs: + folderID = FOLDERID_Programs; + break; + + case Personal: + case MyDocuments: + folderID = FOLDERID_Documents; + break; + + case Favorites: + folderID = FOLDERID_Favorites; + break; + + case Startup: + folderID = FOLDERID_Startup; + break; + + case Recent: + folderID = FOLDERID_Recent; + break; + + case SendTo: + folderID = FOLDERID_SendTo; + break; + + case StartMenu: + folderID = FOLDERID_StartMenu; + break; + + case MyMusic: + folderID = FOLDERID_Music; + break; + + case MyVideos: + folderID = FOLDERID_Videos; + break; + + case MyComputer: + folderID = FOLDERID_ComputerFolder; + break; + + case NetworkShortcuts: + folderID = FOLDERID_NetHood; + break; + + case Fonts: + folderID = FOLDERID_Fonts; + break; + + case Templates: + folderID = FOLDERID_Templates; + break; + + case CommonStartMenu: + folderID = FOLDERID_CommonStartMenu; + break; + + case CommonPrograms: + folderID = FOLDERID_CommonPrograms; + break; + + case CommonStartup: + folderID = FOLDERID_CommonStartup; + break; + + case CommonDesktopDirectory: + folderID = FOLDERID_PublicDesktop; + break; + + case ApplicationData: + folderID = FOLDERID_RoamingAppData; + break; + + case PrinterShortcuts: + folderID = FOLDERID_PrintHood; + break; + + case LocalApplicationData: + folderID = FOLDERID_LocalAppData; + break; + + case InternetCache: + folderID = FOLDERID_InternetCache; + break; + + case Cookies: + folderID = FOLDERID_Cookies; + break; + + case History: + folderID = FOLDERID_History; + break; + + case CommonApplicationData: + folderID = FOLDERID_ProgramData; + break; + + case Windows: + folderID = FOLDERID_Windows; + break; + + case System: + folderID = FOLDERID_System; + break; + + case ProgramFiles: + folderID = FOLDERID_ProgramFiles; + break; + + case MyPictures: + folderID = FOLDERID_Pictures; + break; + + case UserProfile: + folderID = FOLDERID_Profile; + break; + + case SystemX86: + folderID = FOLDERID_SystemX86; + break; + + case ProgramFilesX86: + folderID = FOLDERID_ProgramFilesX86; + break; + + case CommonProgramFiles: + folderID = FOLDERID_ProgramFilesCommon; + break; + + case CommonProgramFilesX86: + folderID = FOLDERID_ProgramFilesCommonX86; + break; + + case CommonTemplates: + folderID = FOLDERID_CommonTemplates; + break; + + case CommonDocuments: + folderID = FOLDERID_PublicDocuments; + break; + + case CommonAdminTools: + folderID = FOLDERID_CommonAdminTools; + break; + + case AdminTools: + folderID = FOLDERID_AdminTools; + break; + + case CommonMusic: + folderID = FOLDERID_PublicMusic; + break; + + case CommonPictures: + folderID = FOLDERID_PublicPictures; + break; + + case CommonVideos: + folderID = FOLDERID_PublicVideos; + break; + + case Resources: + folderID = FOLDERID_ResourceDir; + break; + + case LocalizedResources: + folderID = FOLDERID_LocalizedResourcesDir; + break; + + case CommonOemLinks: + folderID = FOLDERID_CommonOEMLinks; + break; + + case CDBurning: + folderID = FOLDERID_CDBurning; + break; + + default: + return 0; + } + + typedef HRESULT(WINAPI * LPFN_SHGETKNOWNFOLDERPATH)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*); + LPFN_SHGETKNOWNFOLDERPATH fnSHGetKnownFolderPath = NULL; + /**/ + const HMODULE shell32Module = LoadLibraryW(L"shell32.dll"); + + if (NULL == shell32Module) + { + return 0; + } + + fnSHGetKnownFolderPath = (LPFN_SHGETKNOWNFOLDERPATH)GetProcAddress(shell32Module, "SHGetKnownFolderPath"); + + if (NULL == fnSHGetKnownFolderPath) + { + return 0; + } + + wchar_t* pathW = NULL; + + if (S_OK == fnSHGetKnownFolderPath(&folderID, KF_FLAG_DEFAULT, NULL, &pathW)) + { + uint16_t count = (uint16_t)wcslen(pathW); + const ptrdiff_t path_size = buffer_size(path); + + if (!buffer_append_char(path, NULL, count)) + { + CoTaskMemFree(pathW); + pathW = NULL; + return 0; + } + + char* m = (char*)buffer_data(path, path_size); + WIDE2MULTI(pathW, m, count); + CoTaskMemFree(pathW); + pathW = NULL; + return 0 < count; + } + + return 1; +#else + int folderID = 0; + + switch (folder) + { + case Desktop: + case DesktopDirectory: + folderID = CSIDL_DESKTOP; + break; + + case Programs: + folderID = CSIDL_PROGRAMS; + break; + + case Personal: + case MyDocuments: + folderID = CSIDL_PERSONAL; + break; + + case Favorites: + folderID = CSIDL_FAVORITES; + break; + + case Startup: + folderID = CSIDL_STARTUP; + break; + + case Recent: + folderID = CSIDL_RECENT; + break; + + case SendTo: + folderID = CSIDL_SENDTO; + break; + + case StartMenu: + folderID = CSIDL_STARTMENU; + break; + + case MyMusic: + folderID = CSIDL_MYMUSIC; + break; + + case MyVideos: + folderID = CSIDL_MYVIDEO; + break; + + case MyComputer: + /*folderID = ; + break;*/ + return 1; + + case NetworkShortcuts: + folderID = CSIDL_NETHOOD; + break; + + case Fonts: + folderID = CSIDL_FONTS; + break; + + case Templates: + folderID = CSIDL_TEMPLATES; + break; + + case CommonStartMenu: + folderID = CSIDL_COMMON_STARTMENU; + break; + + case CommonPrograms: + folderID = CSIDL_COMMON_PROGRAMS; + break; + + case CommonStartup: + folderID = CSIDL_COMMON_STARTUP; + break; + + case CommonDesktopDirectory: + folderID = CSIDL_COMMON_DESKTOPDIRECTORY; + break; + + case ApplicationData: + folderID = CSIDL_APPDATA; + break; + + case PrinterShortcuts: + folderID = CSIDL_PRINTHOOD; + break; + + case LocalApplicationData: + folderID = CSIDL_LOCAL_APPDATA; + break; + + case InternetCache: + folderID = CSIDL_INTERNET_CACHE; + break; + + case Cookies: + folderID = CSIDL_COOKIES; + break; + + case History: + folderID = CSIDL_HISTORY; + break; + + case CommonApplicationData: + folderID = CSIDL_COMMON_APPDATA; + break; + + case Windows: + folderID = CSIDL_WINDOWS; + break; + + case System: + folderID = CSIDL_SYSTEM; + break; + + case ProgramFiles: + folderID = CSIDL_PROGRAM_FILES; + break; + + case MyPictures: + folderID = CSIDL_MYPICTURES; + break; + + case UserProfile: + folderID = CSIDL_PROFILE; + break; + + case SystemX86: + folderID = CSIDL_SYSTEMX86; + break; + + case ProgramFilesX86: + folderID = CSIDL_PROGRAM_FILESX86; + break; + + case CommonProgramFiles: + folderID = CSIDL_PROGRAM_FILES_COMMON; + break; + + case CommonProgramFilesX86: + folderID = CSIDL_PROGRAM_FILES_COMMONX86; + break; + + case CommonTemplates: + folderID = CSIDL_COMMON_TEMPLATES; + break; + + case CommonDocuments: + folderID = CSIDL_COMMON_DOCUMENTS; + break; + + case CommonAdminTools: + folderID = CSIDL_COMMON_ADMINTOOLS; + break; + + case AdminTools: + folderID = CSIDL_ADMINTOOLS; + break; + + case CommonMusic: + folderID = CSIDL_COMMON_MUSIC; + break; + + case CommonPictures: + folderID = CSIDL_COMMON_PICTURES; + break; + + case CommonVideos: + folderID = CSIDL_COMMON_VIDEO; + break; + + case Resources: + folderID = CSIDL_RESOURCES; + break; + + case LocalizedResources: + /* %SystemRoot%\resources\0409 + folderID = CSIDL_RESOURCES_LOCALIZED; + break; + NOTE: Vista approach, see above, return empty path. */ + return 1; + + case CommonOemLinks: + /* %ProgramData%\OEM Links + folderID = CSIDL_COMMON_OEM_LINKS; + break; + NOTE: Vista approach, see above, return empty path. */ + return 1; + + case CDBurning: + folderID = CSIDL_CDBURN_AREA; + break; + + default: + return 0; + } + + typedef HRESULT(WINAPI * LPFN_SHGETFOLDERPATHW)(HWND, int, HANDLE, DWORD, LPWSTR); + LPFN_SHGETFOLDERPATHW fnSHGetFolderPathW = NULL; + const HMODULE shell32Module = LoadLibraryW(L"shell32.dll"); + + if (NULL == shell32Module) + { + return 0; + } + +#if defined(__GNUC__) && 8 <= __GNUC__ + fnSHGetFolderPathW = (LPFN_SHGETFOLDERPATHW)(void*)GetProcAddress(shell32Module, "SHGetFolderPathW"); +#else + fnSHGetFolderPathW = (LPFN_SHGETFOLDERPATHW)GetProcAddress(shell32Module, "SHGetFolderPathW"); +#endif + ptrdiff_t path_size = buffer_size(path); + + if (!buffer_append_char(path, NULL, MAX_PATH + 1) || !buffer_append_char(path, NULL, MAX_PATH + 1)) + { + return 0; + } + + char* m = (char*)buffer_data(path, path_size); + wchar_t* w = (wchar_t*)buffer_data(path, path_size + MAX_PATH + 1); + + if (S_OK != fnSHGetFolderPathW(NULL, folderID, NULL, SHGFP_TYPE_DEFAULT, w)) + { + return 0; + } + + if (!buffer_resize(path, path_size + (ptrdiff_t)wcslen(w))) + { + return 0; + } + + uint16_t count = (uint16_t)(buffer_size(path) - path_size); + WIDE2MULTI(w, m, count); + return 0 < count; +#endif +} + +#else + +#define _POSIXSOURCE 1 + +#include +#include +#include + +#include +#include + +uint8_t environment_get_folder_path(enum SpecialFolder folder, struct buffer* path) +{ + switch (folder) + { + case Desktop: + case DesktopDirectory: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Desktop", path); + } + + break; + + case Personal: + case MyDocuments: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Documents", path); + } + + break; + + case MyMusic: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Music", path); + } + + break; + + case MyVideos: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Videos", path); + } + + break; + + case Templates: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Templates", path); + } + + break; + + case ApplicationData: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/.config", path); + } + + break; + + case LocalApplicationData: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/.local/share", path); + } + + break; + + case CommonApplicationData: + return common_append_string_to_buffer("/usr/share", path); + + case MyPictures: + if (environment_get_folder_path(UserProfile, path)) + { + return common_append_string_to_buffer("/Pictures", path); + } + + break; + + case UserProfile: + { + const int userID = getuid(); + const struct passwd* user_info = getpwuid(userID); + + if (NULL == user_info) + { + break; + } + + return common_append_string_to_buffer(user_info->pw_dir, path); + } + + default: + break; + } + + return 0; +} + +#endif +#if defined(_WIN32) + +uint8_t environment_get_machine_name(struct buffer* name) +{ + if (NULL == name) + { + return 0; + } + + const ptrdiff_t name_size = buffer_size(name); + DWORD size = MAX_COMPUTERNAME_LENGTH + 1; + + if (!buffer_append_char(name, NULL, size) || !buffer_append_wchar_t(name, NULL, size)) + { + return 0; + } + + wchar_t* nameW = (wchar_t*)buffer_data(name, name_size + size); + + if (!GetComputerNameW(nameW, &size)) + { + return 0; + } + + const DWORD computer_name_size = size; + char* m = (char*)buffer_data(name, name_size); + WIDE2MULTI(nameW, m, size); + return size && buffer_resize(name, name_size + computer_name_size); +} + +#else + +uint8_t environment_get_machine_name(struct buffer* name) +{ + if (NULL == name) + { + return 0; + } + + const ptrdiff_t size = buffer_size(name); + + if (!buffer_append_char(name, NULL, UINT8_MAX + 1)) + { + return 0; + } + + if (0 != gethostname((char*)buffer_data(name, size), UINT8_MAX)) + { + return 0; + } + + return (size < buffer_size(name)) && buffer_resize(name, size + strlen((char*)buffer_data(name, size))); +} + +#endif +#if defined(_WIN32) + +struct Version GetWindowsVersion() +{ + struct Version ver; + ver.major = ver.minor = ver.build = ver.revision = 0; +#if defined(_MSC_VER) && (_MSC_VER >= 1800) +#if _WIN32_WINNT > 0x0603 + + if (IsWindows10OrGreater()) +#else + if (IsWindowsVersionOrGreater(10, 0, 0)) +#endif + { + ver.major = 10; + } + else if (IsWindows8Point1OrGreater()) + { + ver.major = 6; + ver.minor = 3; + } + else if (IsWindows8OrGreater()) + { + ver.major = 6; + ver.minor = 2; + } + else if (IsWindows7OrGreater()) + { + ver.major = 6; + ver.minor = 1; + } + else if (IsWindowsVistaOrGreater()) + { + ver.major = 6; + } + + /* + IsWindowsXPOrGreater(); + IsWindowsServer(); + */ +#else + /*TODO: call VerSetConditionMask, VerifyVersionInfoW via pointers.*/ + static const struct Version versions[] = { {10, 0, 0, 0}, {6, 3, 0, 0}, {6, 2, 0, 0}, {6, 1, 0, 0}, {6, 0, 0, 0} }; + DWORDLONG const dwlConditionMask = VerSetConditionMask( + VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL); + + for (uint8_t i = 0, count = sizeof(versions) / sizeof(*versions); i < count; ++i) + { + OSVERSIONINFOEXW osvi = { sizeof(OSVERSIONINFOEXW), 0, 0, 0, 0, { 0 }, 0, 0, 0, 0, 0 }; + osvi.dwMajorVersion = versions[i].major; + osvi.dwMinorVersion = versions[i].minor; + + if (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask)) + { + return versions[i]; + } + } + +#endif + return ver; +} + +const struct OperatingSystem* environment_get_operating_system() +{ + if (!is_data_of_operating_system_filled) + { + operating_system.Platform = Win32; + operating_system.Version = GetWindowsVersion(); +#if __STDC_SEC_API__ + is_data_of_operating_system_filled = (0 == strcpy_s(operating_system.VersionString, INT8_MAX, Win32NT_str)); +#else + strcpy(operating_system.VersionString, Win32NT_str); + is_data_of_operating_system_filled = 1; +#endif + + if (is_data_of_operating_system_filled) + { + char* ptr = operating_system.VersionString + strlen(operating_system.VersionString); + *ptr = ' '; + ++ptr; + is_data_of_operating_system_filled = 0 < version_to_char_array(&operating_system.Version, ptr); + } + } + + return &operating_system; +} + +#else + +const struct OperatingSystem* environment_get_operating_system() +{ + if (!is_data_of_operating_system_filled) + { + struct utsname uname_data; + operating_system.Platform = Unix; + + if (-1 == uname(&uname_data)) + { + return NULL; + } + + if (!version_parse(uname_data.version, uname_data.version + strlen(uname_data.version), + &operating_system.Version)) + { + /*TODO: call echo with verbose or debug level.*/ + } + + const uint16_t max_count = sizeof(operating_system.VersionString) / sizeof(operating_system.VersionString[0]); + const uint16_t count = 4 + strlen(uname_data.sysname) + strlen(uname_data.release) + strlen( + uname_data.version) + strlen(uname_data.machine); + + if (max_count - 1 < count) + { + return NULL; + } + + sprintf(operating_system.VersionString, "%s %s %s %s", + uname_data.sysname, uname_data.release, + uname_data.version, uname_data.machine); + is_data_of_operating_system_filled = 1; + } + + return &operating_system; +} + +#endif +#if defined(_WIN32) + +uint8_t environment_get_user_name(struct buffer* name) +{ + if (NULL == name) + { + return 0; + } + + const ptrdiff_t name_size = buffer_size(name); + DWORD size = UNLEN + 1; + + if (!buffer_append_char(name, NULL, size) || !buffer_append_wchar_t(name, NULL, size)) + { + return 0; + } + + wchar_t* nameW = (wchar_t*)buffer_data(name, name_size + size); + + if (!GetUserNameW(nameW, &size)) + { + return 0; + } + + const DWORD user_name_size = size - 1; + char* m = (char*)buffer_data(name, name_size); + WIDE2MULTI(nameW, m, size); + return size && buffer_resize(name, name_size + user_name_size); +} + +#else + +uint8_t environment_get_user_name(struct buffer* name) +{ + if (NULL == name) + { + return 0; + } + + const int userID = getuid(); + const struct passwd* user_info = getpwuid(userID); + + if (NULL == user_info) + { + return 0; + } + + return common_append_string_to_buffer(user_info->pw_name, name); +} + +#endif +#if defined(_WIN32) + +uint8_t environment_get_variable(const char* variable_name, uint8_t variable_name_length, + struct buffer* variable) +{ + if (NULL == variable_name || 0 == variable_name_length) + { + return 0; + } + + struct buffer variable_name_w; + + SET_NULL_TO_BUFFER(variable_name_w); + + if (!buffer_append_wchar_t(&variable_name_w, NULL, (ptrdiff_t)variable_name_length + 2)) + { + buffer_release(&variable_name_w); + return 0; + } + + wchar_t* ptr = buffer_wchar_t_data(&variable_name_w, 0); + + for (uint8_t i = 0; i < variable_name_length; ++i)/*TODO: Use WIDE2MULTI*/ + { + ptr[i] = variable_name[i]; + } + + ptr[variable_name_length] = L'\0'; + const uint16_t pos = variable_name_length + 1; + DWORD size = GetEnvironmentVariableW(buffer_wchar_t_data(&variable_name_w, 0), + buffer_wchar_t_data(&variable_name_w, pos), + 1); + + if (0 == size) + { + buffer_release(&variable_name_w); + return 0; + } + + if (NULL == variable) + { + buffer_release(&variable_name_w); + return 1; + } + + if (1 < size) + { + if (!buffer_append_wchar_t(&variable_name_w, NULL, size)) + { + buffer_release(&variable_name_w); + return 0; + } + + size = GetEnvironmentVariableW(buffer_wchar_t_data(&variable_name_w, 0), + buffer_wchar_t_data(&variable_name_w, pos), + size); + + if (0 == size) + { + buffer_release(&variable_name_w); + return 0; + } + } + + ++size; + const ptrdiff_t variable_size = buffer_size(variable); + + if (!buffer_append_char(variable, NULL, size)) + { + buffer_release(&variable_name_w); + return 0; + } + + --size; + const wchar_t* w = buffer_wchar_t_data(&variable_name_w, pos); + char* m = (char*)buffer_data(variable, variable_size); + WIDE2MULTI(w, m, size); + buffer_release(&variable_name_w); + return size && buffer_resize(variable, buffer_size(variable) - 1); +} + +#else + +uint8_t environment_get_variable(const char* variable_name, uint8_t variable_name_length, + struct buffer* variable) +{ + if (NULL == variable_name || + 0 == variable_name_length) + { + return 0; + } + + struct buffer variable_name_; + + SET_NULL_TO_BUFFER(variable_name_); + + if (!buffer_append_char(&variable_name_, variable_name, variable_name_length) || + !buffer_push_back(&variable_name_, '\0')) + { + buffer_release(&variable_name_); + return 0; + } + + const char* value = getenv(buffer_char_data(&variable_name_, 0)); + buffer_release(&variable_name_); + + if (NULL == value) + { + return 0; + } + + if (NULL == variable) + { + return 1; + } + + return common_append_string_to_buffer(value, variable); +} + +#endif + + +uint8_t environment_newline(struct buffer* newline) +{ + if (NULL == newline) + { + return 0; + } + +#if defined(_WIN32) + return buffer_append_char(newline, "\r\n", 2); +#else + return buffer_append_char(newline, "\n", 1); +#endif +} + +uint8_t environment_variable_exists(const char* variable_name, uint8_t variable_name_length) +{ + return environment_get_variable(variable_name, variable_name_length, NULL); +} + +uint8_t environment_is64bit_process() +{ +#if defined(_WIN64) || defined(__amd64) || defined(__x86_64) + return 1; +#else + return 0; +#endif +} + +uint8_t environment_is64bit_operating_system() +{ + if (environment_is64bit_process()) + { + return 1; + } + +#if defined(_WIN32) + typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS2)(HANDLE, PUSHORT, PUSHORT); + LPFN_ISWOW64PROCESS2 fnIsWow64Process2 = NULL; + const HMODULE kernel32Module = GetModuleHandleW(L"kernel32.dll"); + + if (NULL == kernel32Module) + { + return UINT8_MAX; + } + +#if defined(__GNUC__) && 8 <= __GNUC__ + fnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)(void*)GetProcAddress(kernel32Module, "IsWow64Process2"); +#else + fnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)GetProcAddress(kernel32Module, "IsWow64Process2"); +#endif + + if (NULL == fnIsWow64Process2) + { + LPFN_ISWOW64PROCESS fnIsWow64Process = NULL; +#if defined(__GNUC__) && 8 <= __GNUC__ + fnIsWow64Process = (LPFN_ISWOW64PROCESS)(void*)GetProcAddress(kernel32Module, "IsWow64Process"); +#else + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(kernel32Module, "IsWow64Process"); +#endif + + if (NULL == fnIsWow64Process) + { + return 0; + } + + BOOL isWow64Process = FALSE; + + if (0 == fnIsWow64Process(GetCurrentProcess(), &isWow64Process)) + { + return UINT8_MAX; + } + + return 0 < isWow64Process; + } + else + { + USHORT processMachine = 0; + USHORT nativeMachine = 0; + + if (0 == fnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)) + { + return UINT8_MAX; + } + + return IMAGE_FILE_MACHINE_AMD64 == nativeMachine; + } + +#else + const struct OperatingSystem* os = environment_get_operating_system(); + return NULL != strstr(os->VersionString, "x86_64") || NULL != strstr(os->VersionString, "amd64"); +#endif +} + +static const char* environment_str[] = +{ + "get-folder-path", "get-machine-name", "get-operating-system", + "get-user-name", "get-variable", "newline", "variable-exists", + "is64bit-process", "is64bit-operating-system" +}; + +enum environment_function +{ + get_folder_path, get_machine_name, get_operating_system, + get_user_name, get_variable, newline, variable_exists, + is64bit_process, is64bit_operating_system, + UNKNOWN_ENVIRONMENT +}; + +uint8_t environment_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, environment_str, UNKNOWN_ENVIRONMENT); +} + +static const char* special_folder_str[] = +{ + "Desktop", "Programs", "Personal", "MyDocuments", "Favorites", + "Startup", "Recent", "SendTo", "StartMenu", "MyMusic", "MyVideos", + "DesktopDirectory", "MyComputer", "NetworkShortcuts", "Fonts", + "Templates", "CommonStartMenu", "CommonPrograms", "CommonStartup", + "CommonDesktopDirectory", "ApplicationData", "PrinterShortcuts", + "LocalApplicationData", "InternetCache", "Cookies", "History", + "CommonApplicationData", "Windows", "System", "ProgramFiles", + "MyPictures", "UserProfile", "SystemX86", "ProgramFilesX86", + "CommonProgramFiles", "CommonProgramFilesX86", "CommonTemplates", + "CommonDocuments", "CommonAdminTools", "AdminTools", "CommonMusic", + "CommonPictures", "CommonVideos", "Resources", "LocalizedResources", + "CommonOemLinks", "CDBurning" +}; + +uint8_t environment_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_ENVIRONMENT <= function || NULL == arguments || 1 < arguments_count || NULL == output) + { + return 0; + } + + struct range argument; + + argument.start = argument.finish = NULL; + + if (1 == arguments_count && !common_get_one_argument(arguments, &argument, 0)) + { + return 0; + } + + switch (function) + { + case get_folder_path: + { + if (1 != arguments_count) + { + break; + } + + const enum SpecialFolder folder = (enum SpecialFolder)common_string_to_enum( + argument.start, argument.finish, special_folder_str, ENVIRONMENT_UNKNOWN_SPECIAL_FOLDER); + return environment_get_folder_path(folder, output); + } + + case get_machine_name: + return !arguments_count && environment_get_machine_name(output); + + case get_operating_system: + { + const struct OperatingSystem* os = environment_get_operating_system(); + return !arguments_count && common_append_string_to_buffer(os->VersionString, output); + } + + case get_user_name: + return !arguments_count && environment_get_user_name(output); + + case get_variable: + return (1 == arguments_count) && + environment_get_variable(argument.start, (uint8_t)range_size(&argument), output); + + case newline: + return !arguments_count && environment_newline(output); + + case variable_exists: + return (1 == arguments_count) && + bool_to_string(environment_variable_exists(argument.start, (uint8_t)range_size(&argument)), + output); + + case is64bit_process: + return !arguments_count && bool_to_string(environment_is64bit_process(), output); + + case is64bit_operating_system: + return !arguments_count && bool_to_string(environment_is64bit_operating_system(), output); + + case UNKNOWN_ENVIRONMENT: + default: + break; + } + + return 0; +} diff --git a/environment.h b/environment.h new file mode 100644 index 0000000..f620f33 --- /dev/null +++ b/environment.h @@ -0,0 +1,83 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _ENVIRONMENT_H_ +#define _ENVIRONMENT_H_ + +#include +#include + +struct buffer; +struct OperatingSystem; + +enum SpecialFolder +{ + Desktop, + Programs, + Personal, + MyDocuments, + Favorites, + Startup, + Recent, + SendTo, + StartMenu, + MyMusic, + MyVideos, + DesktopDirectory, + MyComputer, + NetworkShortcuts, + Fonts, + Templates, + CommonStartMenu, + CommonPrograms, + CommonStartup, + CommonDesktopDirectory, + ApplicationData, + PrinterShortcuts, + LocalApplicationData, + InternetCache, + Cookies, + History, + CommonApplicationData, + Windows, + System, + ProgramFiles, + MyPictures, + UserProfile, + SystemX86, + ProgramFilesX86, + CommonProgramFiles, + CommonProgramFilesX86, + CommonTemplates, + CommonDocuments, + CommonAdminTools, + AdminTools, + CommonMusic, + CommonPictures, + CommonVideos, + Resources, + LocalizedResources, + CommonOemLinks, + CDBurning +}; + +uint8_t environment_get_folder_path(enum SpecialFolder folder, struct buffer* path); +uint8_t environment_get_machine_name(struct buffer* name); +const struct OperatingSystem* environment_get_operating_system(); +uint8_t environment_get_user_name(struct buffer* name); +uint8_t environment_get_variable(const char* variable_name, uint8_t variable_name_length, + struct buffer* variable); +uint8_t environment_newline(struct buffer* newline); +uint8_t environment_variable_exists(const char* variable_name, uint8_t variable_name_length); +uint8_t environment_is64bit_process(); +uint8_t environment_is64bit_operating_system(); + +uint8_t environment_get_function(const char* name_start, const char* name_finish); +uint8_t environment_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/exec.c b/exec.c new file mode 100644 index 0000000..7c4ec80 --- /dev/null +++ b/exec.c @@ -0,0 +1,1295 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "exec.h" +#include "argument_parser.h" +#include "buffer.h" +#include "conversion.h" +#include "common.h" +#include "echo.h" +#include "file_system.h" +#include "interpreter.h" +#include "math_unit.h" +#include "path.h" +#include "project.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" +#include "xml.h" + +#if defined(_WIN32) + +#include + +uint8_t exec_win32(const wchar_t* program, wchar_t* cmd, + wchar_t* env, const wchar_t* working_dir, + HANDLE hWritePipe, void* pid_property, + uint8_t spawn, uint32_t time_out, uint8_t verbose) +{ + (void)time_out;/*TODO:*/ + + if (NULL == program || L'\0' == *program) + { + return 0; + } + + STARTUPINFOW start_up_info = { 0 }; + start_up_info.cb = sizeof(STARTUPINFO); + start_up_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + start_up_info.wShowWindow = SW_HIDE; + start_up_info.hStdOutput = hWritePipe; + start_up_info.hStdError = hWritePipe; + /**/ + PROCESS_INFORMATION process_information; + process_information.hProcess = INVALID_HANDLE_VALUE; + process_information.hThread = INVALID_HANDLE_VALUE; + process_information.dwProcessId = process_information.dwThreadId = 0; + + if (!CreateProcessW(program, + cmd, + NULL, + NULL, + TRUE, + NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, + env, + working_dir, + &start_up_info, + &process_information)) + { + return 0; + } + + if (spawn && + NULL != pid_property && + !property_set_by_pointer(NULL, NULL, + pid_property, &process_information.dwProcessId, sizeof(HANDLE), + property_value_is_integer, 0, 0, verbose)) + { + CloseHandle(process_information.hProcess); + CloseHandle(process_information.hThread); + return 0; + } + + CloseHandle(process_information.hProcess); + CloseHandle(process_information.hThread); + return 1; +} + +uint8_t exec_win32_with_redirect( + const wchar_t* program, wchar_t* cmd, wchar_t* env, const wchar_t* working_dir, + const char* file, struct buffer* tmp, uint32_t time_out, uint8_t verbose) +{ + if (NULL == tmp) + { + return 0; + } + + HANDLE hReadPipe = INVALID_HANDLE_VALUE; + HANDLE hWritePipe = INVALID_HANDLE_VALUE; + /**/ + SECURITY_ATTRIBUTES security_attributes; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.lpSecurityDescriptor = NULL; + security_attributes.bInheritHandle = TRUE; + + if (!CreatePipe(&hReadPipe, &hWritePipe, &security_attributes, 0)) + { + return 0; + } + + if (!exec_win32(program, cmd, env, working_dir, hWritePipe, NULL, 0, time_out, verbose)) + { + CloseHandle(hWritePipe); + CloseHandle(hReadPipe); + return 0; + } + + CloseHandle(hWritePipe); + + if (!buffer_resize(tmp, 4096)) + { + CloseHandle(hReadPipe); + return 0; + } + + while (1) + { + DWORD numberOfBytesRead = (DWORD)(buffer_size(tmp) - 1); + BOOL ret = ReadFile(hReadPipe, buffer_char_data(tmp, 0), + numberOfBytesRead, &numberOfBytesRead, 0); + /* + static const char* new_line = "\n"; + static const char* new_line_with_info = "\n[Info]: ";*/ + + if (ret && 0 < numberOfBytesRead) + { + if (/*!string_replace_in_buffer(tmp, new_line, 1, new_line_with_info, 9) ||*/ + !echo(1, Default, file, NoLevel, buffer_char_data(tmp, 0), numberOfBytesRead, 0, verbose)) + { + CloseHandle(hReadPipe); + return 0; + } + } + else + { + break; + } + } + + CloseHandle(hReadPipe); + return 1; +} + +uint8_t exec( + uint8_t append, + const struct range* program, + const struct range* base_dir, + const struct range* command_line, + const struct range* output_file, + void* pid_property, + void* result_property, + const struct range* working_dir, + const struct range* environment_variables, + uint8_t spawn, + uint32_t time_out, + uint8_t verbose) +{ + (void)result_property; + + if (range_is_null_or_empty(program)) + { + return 0; + } + + const char* file = range_is_null_or_empty(output_file) ? NULL : output_file->start; + + if (!spawn && !append && NULL != file) + { + if (!echo(0, Default, file, NoLevel, NULL, 0, 1, verbose)) + { + return 0; + } + } + + struct buffer application; + + SET_NULL_TO_BUFFER(application); + + if (!range_is_null_or_empty(base_dir) && + !path_is_path_rooted(program->start, program->finish)) + { + if (!path_combine(base_dir->start, base_dir->finish, + program->start, program->finish, &application)) + { + buffer_release(&application); + return 0; + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + + if (!file_exists(buffer_char_data(&application, 0))) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_size(&application)) + { + if (!buffer_append_data_from_range(&application, program)) + { + buffer_release(&application); + return 0; + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + if (!range_is_null_or_empty(command_line)) + { + static const char space = ' '; + const char* ptr = buffer_char_data(&application, 0); + ptrdiff_t size_ = buffer_size(&application); + const uint8_t contains = string_contains(ptr, ptr + size_, &space, &space + 1); + + if (!buffer_append_char(&application, NULL, size_ + 3) || + !buffer_resize(&application, size_)) + { + buffer_release(&application); + return 0; + } + + ptr = buffer_char_data(&application, 0); + + if ((contains && !buffer_push_back(&application, '"')) || + !buffer_append_char(&application, ptr, size_ - 1) || + (contains && !buffer_push_back(&application, '"'))) + { + buffer_release(&application); + return 0; + } + + if (!buffer_push_back(&application, ' ') || + !buffer_append_data_from_range(&application, command_line)) + { + buffer_release(&application); + return 0; + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + if (!range_is_null_or_empty(working_dir)) + { + if (!buffer_append_data_from_range(&application, working_dir)) + { + buffer_release(&application); + return 0; + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + if (!range_is_null_or_empty(environment_variables)) + { + if (!buffer_append_data_from_range(&application, environment_variables) || + !buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + const ptrdiff_t size = buffer_size(&application); + + if (!buffer_append_wchar_t(&application, NULL, (ptrdiff_t)2 + size)) + { + buffer_release(&application); + return 0; + } + + char* m = buffer_char_data(&application, 0); + wchar_t* programW = (wchar_t*)buffer_data(&application, size); + + if (NULL == programW || + !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, m, (int32_t)size, programW, (int32_t)size)) + { + buffer_release(&application); + return 0; + } + + const wchar_t* finish = (wchar_t*)(buffer_data(&application, 0) + buffer_size(&application)); + wchar_t* command_lineW = NULL; + + if (!range_is_null_or_empty(command_line)) + { + command_lineW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t(programW, finish, L"\0", 1, 1, 1); + command_lineW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t(command_lineW + 1, finish, L"\0", 1, + 0, 1); + + if (finish == command_lineW) + { + buffer_release(&application); + return 0; + } + } + + wchar_t* working_dirW = NULL; + + if (!range_is_null_or_empty(working_dir)) + { + working_dirW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t((NULL == command_lineW) ? programW : + command_lineW, finish, L"\0", 1, 1, 1); + working_dirW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t(working_dirW + 1, finish, L"\0", 1, 0, + 1); + + if (finish == working_dirW) + { + buffer_release(&application); + return 0; + } + } + + wchar_t* environment_variablesW = NULL; + + if (!range_is_null_or_empty(environment_variables)) + { + wchar_t* ptr = (NULL == command_lineW) ? programW : command_lineW; + ptr = (NULL == working_dirW) ? ptr : working_dirW; + /**/ + environment_variablesW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t(ptr, finish, L"\0", 1, 1, 1); + environment_variablesW = (wchar_t*)find_any_symbol_like_or_not_like_that_wchar_t(environment_variablesW + 1, + finish, L"\0", 1, 0, 1); + + if (finish == environment_variablesW) + { + buffer_release(&application); + return 0; + } + } + + if (spawn) + { + spawn = exec_win32(programW, command_lineW, environment_variablesW, working_dirW, NULL, pid_property, spawn, + time_out, verbose); + } + else + { + spawn = exec_win32_with_redirect(programW, command_lineW, environment_variablesW, working_dirW, + file, &application, time_out, verbose); + } + + buffer_release(&application); + return spawn; +} + +#else + +#define _POSIXSOURCE 1 + +#include +#include +#include +#include +#include + +uint8_t exec_posix_no_redirect( + const char* program, char** cmd, char** env, const char* working_dir, + void* pid_property, uint8_t verbose) +{ + if (NULL == program || + NULL == cmd || + NULL == pid_property) + { + return 0; + } + + const pid_t pid = fork(); + + if (NULL != pid_property && + !property_set_by_pointer(NULL, NULL, + pid_property, (const void*)&pid, sizeof(pid_t), property_value_is_integer, 0, 0, verbose)) + { + return 0; + } + + if (-1 == pid) + { + return 0; + } + else if (0 == pid) + { + if (NULL != working_dir && -1 == chdir(working_dir)) + { + return 0; + } + + NULL == env ? execv(program, cmd) : execve(program, cmd, env); + return 0; + } + + return 1; +} + +uint8_t exec_posix_with_redirect( + const char* program, char** cmd, char** env, const char* working_dir, + const char* file, struct buffer* tmp, uint32_t time_out, uint8_t verbose) +{ + (void)time_out; + + if (NULL == program || + NULL == cmd || + NULL == tmp) + { + return 0; + } + + int file_des[2]; + + if (pipe(file_des) == -1) + { + return 0; + } + + const pid_t pid = fork(); + + if (-1 == pid) + { + close(file_des[1]); + close(file_des[0]); + return 0; + } + else if (0 == pid) + { + while ((dup2(file_des[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} + + close(file_des[1]); + close(file_des[0]); + + if (NULL != working_dir && -1 == chdir(working_dir)) + { + return 0; + } + + NULL == env ? execv(program, cmd) : execve(program, cmd, env); + return 0; + } + + close(file_des[1]); + + if (!buffer_resize(tmp, 4096)) + { + close(file_des[0]); + return 0; + } + + while (1) + { + const ssize_t count = read(file_des[0], buffer_char_data(tmp, 0), 4096); + + if (count == -1) + { + if (errno == EINTR) + { + continue; + } + else + { + close(file_des[0]); + return 0; + } + } + else if (count == 0) + { + break; + } + else + { + if (!echo(1, Default, file, NoLevel, buffer_char_data(tmp, 0), count, 0, verbose)) + { + close(file_des[0]); + return 0; + } + } + } + + close(file_des[0]); + wait(0); + return 1; +} + +uint8_t exec( + uint8_t append, + const struct range* program, + const struct range* base_dir, + const struct range* command_line, + const struct range* output_file, + void* pid_property, + void* result_property, + const struct range* working_dir, + const struct range* environment_variables, + uint8_t spawn, + uint32_t time_out, + uint8_t verbose) +{ + if (range_is_null_or_empty(program)) + { + return 0; + } + + const char* file = range_is_null_or_empty(output_file) ? NULL : output_file->start; + + if (!spawn && !append && NULL != file) + { + if (!echo(0, Default, file, NoLevel, NULL, 0, 1, verbose)) + { + return 0; + } + } + + ptrdiff_t expected_size = range_size(program) + 1; + expected_size += sizeof(char*); + expected_size += range_size(base_dir); + expected_size += range_size(command_line) + 1; + expected_size += range_size(working_dir) + 1; + expected_size += range_size(environment_variables) + 1; + expected_size += (range_size(command_line) / 2) * sizeof(char*); + expected_size += (range_size(environment_variables) / 2) * sizeof(char*); + expected_size += 2 * sizeof(char*) + 2; + // + static const char space = ' '; + struct buffer application; + SET_NULL_TO_BUFFER(application); + + if (!buffer_append(&application, NULL, expected_size) || + !buffer_resize(&application, 0)) + { + buffer_release(&application); + return 0; + } + + if (!range_is_null_or_empty(base_dir) && + !path_is_path_rooted(program->start, program->finish)) + { + const uint8_t contains = string_contains(base_dir->start, base_dir->finish, &space, &space + 1) || + string_contains(program->start, program->finish, &space, &space + 1); + + if (contains) + { + if (!buffer_append(&application, NULL, range_size(base_dir) + range_size(program) + 3) || + !buffer_resize(&application, 0) || + !buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!path_combine(base_dir->start, base_dir->finish, + program->start, program->finish, &application)) + { + buffer_release(&application); + return 0; + } + + if (contains) + { + if (!buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + + if (!file_exists(buffer_char_data(&application, 0))) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_size(&application)) + { + const uint8_t contains = string_contains(program->start, program->finish, &space, &space + 1); + + if (contains) + { + if (!buffer_append(&application, NULL, range_size(program) + 3) || + !buffer_resize(&application, 0) || + !buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_append_data_from_range(&application, program)) + { + buffer_release(&application); + return 0; + } + + if (contains) + { + if (!buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + if (!range_is_null_or_empty(command_line)) + { + if (!argument_append_arguments(command_line->start, command_line->finish, &application)) + { + buffer_release(&application); + return 0; + } + } + + int argc = 0; + char** cmd = NULL; + + if (!argument_create_arguments(&application, &argc, &cmd)) + { + buffer_release(&application); + return 0; + } + + const ptrdiff_t cmd_index = (uint8_t*)cmd - buffer_data(&application, 0); + ptrdiff_t working_dir_index = -1; + + if (!range_is_null_or_empty(working_dir)) + { + working_dir_index = buffer_size(&application); + const uint8_t contains = string_contains(working_dir->start, working_dir->finish, &space, &space + 1); + + if (contains) + { + if (!buffer_append(&application, NULL, range_size(working_dir) + 3) || + !buffer_resize(&application, working_dir_index) || + !buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_append_data_from_range(&application, working_dir)) + { + buffer_release(&application); + return 0; + } + + if (contains) + { + if (!buffer_push_back(&application, '"')) + { + buffer_release(&application); + return 0; + } + } + + if (!buffer_push_back(&application, '\0')) + { + buffer_release(&application); + return 0; + } + } + + char** env = NULL; + ptrdiff_t env_index = -1; + + if (!range_is_null_or_empty(environment_variables)) + { + env_index = buffer_size(&application); + + if (!buffer_append_data_from_range(&application, environment_variables)) + { + buffer_release(&application); + return 0; + } + + argc = (int)env_index; + + if (!argument_create_arguments(&application, &argc, &env)) + { + buffer_release(&application); + return 0; + } + + env_index = (uint8_t*)env - buffer_data(&application, 0); + } + + cmd = (char**)(buffer_data(&application, 0) + cmd_index); + env = 0 < env_index ? (char**)(buffer_data(&application, 0) + env_index) : NULL; + const char* work = 0 < working_dir_index ? (const char*)(buffer_data(&application, + 0) + working_dir_index) : NULL; + + if (spawn) + { + spawn = exec_posix_no_redirect((const char*)buffer_data(&application, 0), cmd, env, work, + pid_property, verbose); + } + else + { + spawn = exec_posix_with_redirect((const char*)buffer_data(&application, 0), cmd, env, work, + file, &application, time_out, verbose); + } + + /*spawn = 1; + printf("\n\n\n\n");*/ + /*(void)append; + (void)program; + (void)base_dir; + (void)command_line; + (void)output_file; + (void)pid_property;*/ + (void)result_property; + /*(void)working_dir; + (void)environment_variables; + (void)spawn; + (void)time_out; + (void)verbose;*/ + buffer_release(&application); + return spawn; +} + +#endif + +uint8_t exec_get_environments(const char* start, const char* finish, struct buffer* environments) +{ + if (range_in_parts_is_null_or_empty(start, finish) || + NULL == environments) + { + return 0; + } + + struct buffer elements; + + SET_NULL_TO_BUFFER(elements); + + if (!buffer_resize(&elements, 0)) + { + buffer_release(&elements); + return 0; + } + + uint16_t count = xml_get_sub_nodes_elements(start, finish, &elements); + + if (!count) + { + buffer_release(&elements); + return 1; + } + + count = 0; + struct range name; + struct range* env_ptr = NULL; + + while (NULL != (env_ptr = buffer_range_data(&elements, count++))) + { + static const char* env_name = "environment"; + + if (!xml_get_tag_name(env_ptr->start, env_ptr->finish, &name)) + { + buffer_release(&elements); + return 0; + } + + if (string_equal(name.start, name.finish, env_name, env_name + 11)) + { + break; + } + } + + if (NULL == env_ptr) + { + buffer_release(&elements); + return 1; + } + + name = *env_ptr; + + if (!buffer_resize(&elements, 0)) + { + buffer_release(&elements); + return 0; + } + + count = xml_get_sub_nodes_elements(name.start, name.finish, &elements); + + if (!count) + { + buffer_release(&elements); + return 1; + } + + count = 0; + + while (NULL != (env_ptr = buffer_range_data(&elements, count++))) + { + static const char* var_name = "variable"; + + if (!xml_get_tag_name(env_ptr->start, env_ptr->finish, &name)) + { + buffer_release(&elements); + return 0; + } + + if (string_equal(name.start, name.finish, var_name, var_name + 8)) + { + if (!xml_get_attribute_value(env_ptr->start, env_ptr->finish, "name", 4, &name) || + range_is_null_or_empty(&name)) + { + buffer_release(&elements); + return 0; + } + + static const char space = ' '; + uint8_t contains = string_contains(name.start, name.finish, &space, &space + 1); + + if ((contains && !buffer_push_back(environments, '"')) || + !buffer_append_data_from_range(environments, &name) || + (contains && !buffer_push_back(environments, '"')) || + !buffer_push_back(environments, '=')) + { + buffer_release(&elements); + return 0; + } + + if (xml_get_attribute_value(env_ptr->start, env_ptr->finish, "value", 5, &name)) + { + contains = string_contains(name.start, name.finish, &space, &space + 1); + + if ((contains && !buffer_push_back(environments, '"')) || + !buffer_append_data_from_range(environments, &name) || + (contains && !buffer_push_back(environments, '"'))) + { + buffer_release(&elements); + return 0; + } + } + + if (!buffer_push_back(environments, '\0')) + { + buffer_release(&elements); + return 0; + } + } + } + + buffer_release(&elements); + return 1; +} + +uint32_t millisecond_to_second(uint64_t millisecond) +{ + return (uint32_t)math_truncate(math_ceiling((double)millisecond / 1000)); +} + +#define PROGRAM_POSITION 0 +#define APPEND_POSITION 1 +#define BASE_DIR_POSITION 2 +#define COMMAND_LINE_POSITION 3 +#define OUTPUT_POSITION 4 +#define PID_PROPERTY_POSITION 5 +#define RESULT_PROPERTY_POSITION 6 +#define SPAWN_POSITION 7 +#define WORKING_DIR_POSITION 8 +#define FAIL_ON_ERROR_POSITION 9 +#define TIME_OUT_POSITION 10 +#define VERBOSE_POSITION 11 +#define ENVIRONMENT_POSITION 12 + +uint8_t exec_get_arguments_for_task( + void* project, + const void* target, + const char* attributes_start, + const char* attributes_finish, + const char* element_finish, + struct buffer* arguments) +{ + if (range_in_parts_is_null_or_empty(attributes_start, attributes_finish) || + NULL == element_finish || element_finish < attributes_finish || + NULL == arguments) + { + return 0; + } + + static const char* attributes[] = { "program", "append", "basedir", "commandline", + "output", "pidproperty", "resultproperty", "spawn", + "workingdir", "failonerror", "timeout", "verbose" + }; + static const uint8_t attributes_lengths[] = { 7, 6, 7, 11, + 6, 11, 14, 5, + 10, 11, 7, 7 + }; + /**/ + static const uint8_t default_append_value = 0; + static const char* default_basedir_value = NULL; + static const char* default_command_line_value = NULL; + static const char* default_output_value = NULL; + static const char* default_pid_property_value = NULL; + static const char* default_result_property_value = NULL; + static const uint8_t default_spawn_value = 0; + static const char* default_working_dir_value = NULL; + static const uint8_t default_fail_on_error_value = 1; + static const uint32_t default_time_out_value = 0; + static const uint8_t default_verbose_value = 0; + /**/ + struct buffer argument; + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_buffer(arguments, &argument, 1)) /*NOTE: reserve space for program.*/ + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_append_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_basedir_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_command_line_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_output_value, 0) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_pid_property_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_result_property_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_spawn_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!buffer_append_char(&argument, default_working_dir_value, 0) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_fail_on_error_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if ((0 < default_time_out_value && !int_to_string(default_time_out_value, &argument)) || + !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!bool_to_string(default_verbose_value, &argument) || !buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + if (!interpreter_get_arguments_from_xml_tag_record(project, target, attributes_start, attributes_finish, + attributes, attributes_lengths, sizeof(attributes_lengths) / sizeof(*attributes_lengths), arguments)) + { + return 0; + } + + SET_NULL_TO_BUFFER(argument); + + if (!range_in_parts_is_null_or_empty(attributes_finish, element_finish) && + !exec_get_environments(attributes_finish, element_finish, &argument)) + { + buffer_release(&argument); + return 0; + } + + if (!buffer_append_buffer(arguments, &argument, 1)) + { + buffer_release(&argument); + return 0; + } + + struct buffer* argument_value = buffer_buffer_data(arguments, VERBOSE_POSITION); + + if (NULL == argument_value) + { + return 0; + } + + /*TODO: verbose of current function should be set outside.*/ + uint8_t verbose = 0; + + if (!bool_parse(buffer_char_data(argument_value, 0), + buffer_char_data(argument_value, 0) + buffer_size(argument_value), &verbose)) + { + return 0; + } + + for (uint8_t index = PID_PROPERTY_POSITION; ; index = RESULT_PROPERTY_POSITION) + { + argument_value = buffer_buffer_data(arguments, index); + + if (NULL == argument_value) + { + return 0; + } + + if (buffer_size(argument_value)) + { + void* the_property = NULL; + + if (NULL == project) + { + return 0; + } + + if (!project_property_set_value(project, NULL, buffer_char_data(argument_value, 0), + (uint8_t)buffer_size(argument_value), NULL, 0, 0, 1, 0, verbose) || + !project_property_get_pointer(project, buffer_char_data(argument_value, 0), + (uint8_t)buffer_size(argument_value), + &the_property)) + { + return 0; + } + + if (!buffer_resize(argument_value, 0) || + !buffer_append(argument_value, the_property, sizeof(void*))) + { + return 0; + } + } + + if (RESULT_PROPERTY_POSITION == index) + { + break; + } + } + + argument_value = buffer_buffer_data(arguments, TIME_OUT_POSITION); + + if (NULL == argument_value) + { + return 0; + } + + if (buffer_size(argument_value)) + { + if (!buffer_push_back(argument_value, '\0')) + { + return 0; + } + + int64_t data = int64_parse(buffer_char_data(argument_value, 0)); + + if (!buffer_resize(argument_value, 0)) + { + return 0; + } + + if (1000 < data) + { + data = millisecond_to_second(data); + + if (data < 5) + { + data = 5; + } + + if (!int64_to_string((uint32_t)data, argument_value)) + { + return 0; + } + } + } + + return 1; +} + +uint8_t exec_evaluate_task(void* project, const void* target, + const char* attributes_start, const char* attributes_finish, + const char* element_finish) +{ + struct buffer arguments; + SET_NULL_TO_BUFFER(arguments); + + if (!buffer_resize(&arguments, 2 * UINT8_MAX) || !buffer_resize(&arguments, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!exec_get_arguments_for_task(project, target, + attributes_start, attributes_finish, element_finish, &arguments)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + uint8_t append; + struct range program; + struct range base_dir; + struct range command_line; + struct range output_file; + void* pid_property; + void* result_property; + struct range working_dir; + struct range environment_variables; + uint8_t spawn; + uint8_t fail_on_error; + int64_t time_out; + uint8_t verbose; + + if (!common_unbox_char_data(&arguments, PROGRAM_POSITION, 0, &program, 0)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!common_unbox_bool_data(&arguments, APPEND_POSITION, 0, &append)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!common_unbox_char_data(&arguments, BASE_DIR_POSITION, 0, &base_dir, 0)) + { + base_dir.start = base_dir.finish = NULL; + } + + if (!common_unbox_char_data(&arguments, COMMAND_LINE_POSITION, 0, &command_line, 0)) + { + command_line.start = command_line.finish = NULL; + } + + if (!common_unbox_char_data(&arguments, OUTPUT_POSITION, 0, &output_file, 0)) + { + output_file.start = output_file.finish = NULL; + } + + if (!common_unbox_char_data(&arguments, PID_PROPERTY_POSITION, 0, &working_dir, 0)) + { + pid_property = NULL; + } + else + { + pid_property = (void*)working_dir.start; + } + + if (!common_unbox_char_data(&arguments, RESULT_PROPERTY_POSITION, 0, &working_dir, 0)) + { + result_property = NULL; + } + else + { + result_property = (void*)working_dir.start; + } + + if (!common_unbox_bool_data(&arguments, SPAWN_POSITION, 0, &spawn)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!common_unbox_char_data(&arguments, WORKING_DIR_POSITION, 0, &working_dir, 0)) + { + working_dir.start = working_dir.finish = NULL; + } + + if (!common_unbox_bool_data(&arguments, FAIL_ON_ERROR_POSITION, 0, &fail_on_error)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + time_out = common_unbox_int64_data(&arguments, TIME_OUT_POSITION, 0); + + if (!common_unbox_bool_data(&arguments, VERBOSE_POSITION, 0, &verbose)) + { + buffer_release_with_inner_buffers(&arguments); + return 0; + } + + if (!common_unbox_char_data(&arguments, ENVIRONMENT_POSITION, 0, &environment_variables, 0)) + { + environment_variables.start = environment_variables.finish = NULL; + } + + spawn = exec(append, &program, &base_dir, &command_line, &output_file, + pid_property, result_property, &working_dir, &environment_variables, + spawn, (uint32_t)time_out, verbose); + /**/ + buffer_release_with_inner_buffers(&arguments); + /*TODO: comment about fail_on_error, if verbose mode, + and manipulate of return value. + return fail_on_error ? spawn : 1;*/ + return spawn; +} diff --git a/exec.h b/exec.h new file mode 100644 index 0000000..2ffc527 --- /dev/null +++ b/exec.h @@ -0,0 +1,32 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _EXEC_H_ +#define _EXEC_H_ + +#include + +struct range; + +uint8_t exec( + uint8_t append, + const struct range* program, + const struct range* base_dir, + const struct range* command_line, + const struct range* output_file, + void* pid_property, + void* result_property, + const struct range* working_dir, + const struct range* environment_variables, + uint8_t spawn, + uint32_t time_out, + uint8_t verbose); +uint8_t exec_evaluate_task(void* project, const void* target, + const char* attributes_start, const char* attributes_finish, + const char* element_finish); + +#endif diff --git a/file_system.c b/file_system.c new file mode 100644 index 0000000..c8deaa5 --- /dev/null +++ b/file_system.c @@ -0,0 +1,281 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "file_system.h" +#include "buffer.h" +#include "common.h" +#include "path.h" +#include "project.h" +#include "range.h" +#include "string_unit.h" + +#if defined(_WIN32) +#include +#include + +#include + +#define FIND_FILE_OBJECT_DATA(PATHW, DATA, RETURN, CLOSE_RESULT)\ + const HANDLE file_handle = FindFirstFileW((PATHW), (DATA)); \ + \ + if (INVALID_HANDLE_VALUE == file_handle) \ + { \ + return (RETURN); \ + } \ + \ + (CLOSE_RESULT) = FindClose(file_handle); + +uint8_t directory_exists_by_wchar_path(const wchar_t* path) +{ + if (NULL == path || L'\0' == *path) + { + return 0; + } + + WIN32_FIND_DATAW file_data; + BOOL close_return = 0; + FIND_FILE_OBJECT_DATA(path, &file_data, 0, close_return) + const uint8_t is_directory = (0 != (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + return close_return && is_directory; +} + +uint8_t path_to_pathW(const char* path, struct buffer* pathW) +{ + if (NULL == path || + '\0' == *path || + NULL == pathW) + { + return 0; + } + + int32_t length = (int32_t)strlen(path); + + if (path_is_path_rooted(path, path + length) && + !buffer_append_wchar_t((pathW), L"\\\\?\\", 4)) + { + return 0; + } + + const ptrdiff_t size = buffer_size(pathW); + + if (!buffer_append_wchar_t(pathW, NULL, length) || + !buffer_push_back_uint16(pathW, 0)) + { + return 0; + } + + wchar_t* w = (wchar_t*)buffer_data(pathW, size); + MULTI2WIDE(path, w, length) + return 0 < length; +} +#else + +#define _POSIXSOURCE 1 + +#include +#endif +uint8_t directory_exists(const char* path) +{ + if (NULL == path || '\0' == *path) + { + return 0; + } + +#if defined(_WIN32) + struct buffer pathW; + SET_NULL_TO_BUFFER(pathW); + + if (!path_to_pathW(path, &pathW)) + { + buffer_release(&pathW); + return 0; + } + + const uint8_t is_exists = directory_exists_by_wchar_path(buffer_wchar_t_data(&pathW, 0)); + buffer_release(&pathW); + return is_exists; +#else + DIR* dir = opendir(path); + + if (NULL == dir) + { + return 0; + } + + closedir(dir); + return 1; +#endif +} + +uint8_t directory_get_current_directory(const void* project, struct buffer* current_directory) +{ + if (!project_get_base_directory(project, NULL, current_directory)) + { + return path_get_directory_for_current_process(current_directory); + } + + return 1; +} + +uint8_t directory_get_logical_drives(struct buffer* drives) +{ +#if defined(_WIN32) + + if (NULL == drives) + { + return 0; + } + + const ptrdiff_t size = buffer_size(drives); + const DWORD count_of_characters = GetLogicalDriveStringsW(0, NULL); + + if (!count_of_characters || + !buffer_append_char(drives, NULL, count_of_characters) || + !buffer_append_wchar_t(drives, NULL, count_of_characters)) + { + return 0; + } + + wchar_t* w = (wchar_t*)buffer_data(drives, size + count_of_characters); + + if (!GetLogicalDriveStringsW(count_of_characters, w)) + { + return 0; + } + + DWORD count = count_of_characters; + char* m = (char*)buffer_data(drives, size); + WIDE2MULTI(w, m, count); + + if (!count) + { + return 0; + } + + return buffer_resize(drives, size + count_of_characters); +#else + return buffer_push_back(drives, path_delimiter()); +#endif +} + +uint8_t directory_get_parent_directory(const char* path_start, const char* path_finish, struct range* parent) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || NULL == parent) + { + return 0; + } + + const char del = path_delimiter(); + parent->start = path_start; + parent->finish = find_any_symbol_like_or_not_like_that(path_finish - 1, path_start, &del, 1, 1, -1); + + if (!string_trim(parent)) + { + return 0; + } + + return range_is_null_or_empty(parent); +} +#if defined(_WIN32) +uint8_t file_exists_by_wchar_path(const wchar_t* path) +{ + if (NULL == path) + { + return 0; + } + + WIN32_FIND_DATAW file_data; + BOOL close_return = 0; + FIND_FILE_OBJECT_DATA(path, &file_data, 0, close_return) + const uint8_t is_file = (0 == (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + return close_return && is_file; +} +#else +#include +#endif +uint8_t file_exists(const char* path) +{ + if (NULL == path || '\0' == *path) + { + return 0; + } + +#if defined(_WIN32) + struct buffer pathW; + SET_NULL_TO_BUFFER(pathW); + + if (!path_to_pathW(path, &pathW)) + { + buffer_release(&pathW); + return 0; + } + + const uint8_t is_exists = file_exists_by_wchar_path(buffer_wchar_t_data(&pathW, 0)); + buffer_release(&pathW); + return is_exists; +#else + struct stat file_status; + file_status.st_mode = 0; + return -1 != stat(path, &file_status) && (S_IFDIR != (file_status.st_mode & S_IFDIR)); +#endif +} + +uint64_t file_get_length(const char* path) +{ + if (NULL == path || '\0' == *path) + { + return 0; + } + +#if defined(_WIN32) + struct buffer pathW; + SET_NULL_TO_BUFFER(pathW); + + if (!path_to_pathW(path, &pathW)) + { + buffer_release(&pathW); + return 0; + } + + WIN32_FIND_DATAW file_data; + const HANDLE file_handle = FindFirstFileW(buffer_wchar_t_data(&pathW, 0), &file_data); + buffer_release(&pathW); + + if (INVALID_HANDLE_VALUE == file_handle) + { + return 0; + } + + if (!FindClose(file_handle)) + { + return 0; + } + + const uint8_t is_file = (0 == (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + + if (!is_file) + { + return 0; + } + + uint64_t length = file_data.nFileSizeHigh; + length *= ((uint64_t)(MAXDWORD) + (uint64_t)1); + length += file_data.nFileSizeLow; + return length; +#else + struct stat file_status; + file_status.st_size = 0; + + if (stat(path, &file_status) || + !(S_IFDIR != (file_status.st_mode & S_IFDIR))) + { + return 0; + } + + return file_status.st_size; +#endif +} diff --git a/file_system.h b/file_system.h new file mode 100644 index 0000000..ffd44b4 --- /dev/null +++ b/file_system.h @@ -0,0 +1,52 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _FILE_SYSTEM_H_ +#define _FILE_SYSTEM_H_ + +#if defined(_WIN32) +#include +#endif + +#include +#include + +struct buffer; +struct range; + +#if defined(_WIN32) +uint8_t directory_exists_by_wchar_path(const wchar_t* path); + +uint8_t file_exists_by_wchar_path(const wchar_t* path); +#endif + +uint8_t directory_exists(const char* path); +#if 0 +directory_get_creation_time +#endif +uint8_t directory_get_current_directory(const void* project, struct buffer* current_directory); +#if 0 +directory_get_directory_root->path_root +directory_get_last_access_time +directory_get_last_write_time +#endif +uint8_t directory_get_logical_drives(struct buffer* drives); +uint8_t directory_get_parent_directory(const char* path_start, const char* path_finish, struct range* parent); + +uint8_t file_exists(const char* path); +#if 0 +file_get_creation_time +file_get_last_access_time +file_get_last_write_time +#endif +uint64_t file_get_length(const char* path); +#if 0 +file_is_assembly +file_up_to_date +#endif + +#endif diff --git a/interpreter.c b/interpreter.c new file mode 100644 index 0000000..d27517e --- /dev/null +++ b/interpreter.c @@ -0,0 +1,972 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "interpreter.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "date_time.h" +#include "echo.h" +#include "environment.h" +#include "exec.h" +#include "math_unit.h" +#include "operating_system.h" +#include "path.h" +#include "project.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" +#include "target.h" +#include "version.h" +#include "xml.h" + +static const char* interpreter_string_enumeration_unit[] = +{ + "bool", "cygpath", "datetime", "directory", "dns", + "double", "environment", "file", "fileversioninfo", + "int", "long", "int64", "math", "operating-system", + "path", "platform", "program", "project", "property", + "string", "target", "task", "timespan", "version" +}; + +enum interpreter_enumeration_unit +{ + bool_, cygpath_, datetime_, directory_, dns_, + double_, environment_, file_, fileversioninfo_, + int_, long_, int64_, math_, operating_system_, + path_, platform_, program_, project_, property_, + string_, target_, task_, timespan_, version_, UNKNOWN_UNIT +}; + +uint8_t interpreter_get_unit(const char* name_space_start, const char* name_space_finish) +{ + return common_string_to_enum(name_space_start, name_space_finish, interpreter_string_enumeration_unit, + UNKNOWN_UNIT); +} + +uint8_t interpreter_get_value_from_quote(const struct range* quote, struct range* value) +{ + if (range_is_null_or_empty(quote) || NULL == value) + { + return 0; + } + + if (quote->finish == (value->start = find_any_symbol_like_or_not_like_that( + quote->start, quote->finish, "'", 1, 1, 1))) + { + return 0; + } + + ++value->start; + value->finish = find_any_symbol_like_or_not_like_that(value->start, quote->finish, "'", 1, 1, 1); + return '\'' == *value->finish; +} + +static const char* namespace_border = "::"; +static const ptrdiff_t namespace_border_length = 2; + +uint8_t interpreter_disassemble_function( + const struct range* function, + struct range* name_space, + struct range* name, + struct range* arguments_area) +{ + if (range_is_null_or_empty(function) || + NULL == name_space || + NULL == name || + NULL == arguments_area) + { + return 0; + } + + ptrdiff_t index = 0; + + if (-1 == (index = string_index_of(function->start, + function->finish, + namespace_border, + namespace_border + namespace_border_length))) + { + return 0; + } + + name_space->start = function->start; + name_space->finish = function->start + index; + /**/ + name->start = name_space->finish + namespace_border_length; + name->finish = find_any_symbol_like_or_not_like_that( + name->start, function->finish, "(", 1, 1, 1); + + if (function->finish == name->finish) + { + return 0; + } + + arguments_area->start = name->finish + 1; + arguments_area->finish = find_any_symbol_like_or_not_like_that( + function->finish, arguments_area->start, ")", 1, 1, -1); + /**/ + return ')' == *arguments_area->finish; +} + +uint8_t interpreter_get_function_from_argument( + struct range* argument_area) +{ + if (range_is_null_or_empty(argument_area)) + { + return 0; + } + + const char* finish = argument_area->finish; + argument_area->finish = find_any_symbol_like_or_not_like_that(argument_area->start, argument_area->finish, + "(", 1, 1, 1); + + if (finish == argument_area->finish) + { + return 0; + } + + char ch = '\0'; + uint8_t depth = 0; + + while (argument_area->finish < finish) + { + ++argument_area->finish; + ch = *argument_area->finish; + + if (')' == ch) + { + if (0 == depth) + { + ++argument_area->finish; + break; + } + + --depth; + } + + if ('(' == ch) + { + ++depth; + } + } + + if (')' != ch || 0 != depth) + { + return 0; + } + + return 1; +} + +uint8_t interpreter_evaluate_argument_area( + const void* project, const void* target, + const struct range* argument_area, struct buffer* output) +{ + ptrdiff_t index = 0; + const char* pos = argument_area->start; + + while (-1 != (index = string_index_of(pos, argument_area->finish, + namespace_border, + namespace_border + namespace_border_length))) + { + struct range function; + function.start = find_any_symbol_like_or_not_like_that(pos + index, pos, " \t", 2, 1, -1); + function.finish = argument_area->finish; + + if (!string_trim(&function) || + !interpreter_get_function_from_argument(&function) || + !buffer_append_char(output, pos, function.start - pos) || + !interpreter_evaluate_function(project, target, &function, output)) + { + return 0; + } + + pos = function.finish; + } + + return (pos != argument_area->start) ? buffer_append_char(output, pos, argument_area->finish - pos) : 1; +} + +uint8_t interpreter_get_value_for_argument( + const void* project, const void* target, + struct range* argument_area, struct buffer* values) +{ + struct buffer value; + SET_NULL_TO_BUFFER(value); + + if (range_is_null_or_empty(argument_area) || + NULL == values) + { + return 0; + } + + if (!string_trim(argument_area)) + { + return 0; + } + + if (!interpreter_evaluate_argument_area(project, target, argument_area, &value)) + { + return 0; + } + + if (!buffer_size(&value)) + { + if (!property_get_by_name(project, target, + argument_area->start, (uint8_t)range_size(argument_area), &value)) + { + if (!interpreter_get_value_from_quote(argument_area, argument_area) || + !buffer_append_data_from_range(&value, argument_area)) + { + return 0; + } + } + } + + /*TODO: value can be represent the quot or property name, recursion in this case should be used at else.*/ + return buffer_append_buffer(values, &value, 1); +} + +uint8_t interpreter_get_values_for_arguments( + const void* project, const void* target, + const struct range* arguments_area, struct buffer* values) +{ + if (range_is_null_or_empty(arguments_area) || NULL == values) + { + return 0; + } + + uint8_t count = 0; + uint8_t depth = 0; + struct range argument_area; + argument_area.start = arguments_area->start; + argument_area.finish = arguments_area->start; + + while (argument_area.finish < arguments_area->finish) + { + const char ch = *argument_area.finish; + + if (',' == ch && 0 == depth) + { + const char* pos = argument_area.finish + 1;/*TODO: MIN(pos, arguments_area->finish)*/ + + if (!interpreter_get_value_for_argument(project, target, &argument_area, values)) + { + return 0; + } + + ++count; + argument_area.start = pos; + argument_area.finish = pos + 1; + } + else if ('(' == ch) + { + if (UINT8_MAX == depth) + { + /*TODO:*/ + break; + } + + ++depth; + } + else if (')' == ch) + { + if (0 == depth) + { + /*TODO:*/ + break; + } + + --depth; + } + + ++argument_area.finish; + } + + if (!interpreter_get_value_for_argument(project, target, &argument_area, values)) + { + return 0; + } + + ++count; + return count; +} + +uint8_t interpreter_evaluate_function(const void* project, const void* target, const struct range* function, + struct buffer* return_of_function) +{ + struct buffer values; + SET_NULL_TO_BUFFER(values); + /**/ + struct range name_space; + struct range name; + struct range arguments_area; + + if (!interpreter_disassemble_function(function, &name_space, &name, &arguments_area)) + { + return 0; + } + + const uint8_t values_count = interpreter_get_values_for_arguments( + project, target, &arguments_area, &values); + + switch (interpreter_get_unit(name_space.start, name_space.finish)) + { + case bool_: + if (!bool_exec_function( + conversion_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case cygpath_: + if (!cygpath_exec_function(path_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case datetime_: + if (!datetime_exec_function( + datetime_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case directory_: + break; + + case dns_: + break; + + case double_: + if (!double_exec_function( + conversion_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case environment_: + if (!environment_exec_function( + environment_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case file_: + break; + + case fileversioninfo_: + break; + + case int_: + if (!int_exec_function( + conversion_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case long_: + if (!long_exec_function( + conversion_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case int64_: + if (!int64_exec_function( + conversion_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case math_: + if (!math_exec_function( + math_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case operating_system_: + if (!os_exec_function(os_get_function(name.start, name.finish), &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case path_: + if (!path_exec_function(project, + path_get_function(name.start, name.finish), &values, values_count, + return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case platform_: + if (!platform_exec_function(os_get_function(name.start, name.finish), &values, values_count, + return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case program_: + if (!program_exec_function(project_get_function(name.start, name.finish), &values, values_count, + return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case project_: + if (!project_exec_function( + project, target, + project_get_function(name.start, name.finish), &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case property_: + if (!property_exec_function( + project, target, + property_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case string_: + if (!string_exec_function( + string_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case target_: + break; + + case task_: + break; + + case timespan_: + if (!timespan_exec_function( + timespan_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case version_: + if (!version_exec_function( + version_get_function(name.start, name.finish), + &values, values_count, return_of_function)) + { + buffer_release_with_inner_buffers(&values); + return 0; + } + + break; + + case UNKNOWN_UNIT: + default: + buffer_release_with_inner_buffers(&values); + return 0; + } + + buffer_release_with_inner_buffers(&values); + return 1; +} + +uint8_t interpreter_evaluate_code(const void* project, const void* target, + const struct range* code, struct buffer* output) +{ + static const char* function_call_start = "${"; + static const char* function_call_finish = "}"; + + if (range_is_null_or_empty(code) || NULL == output) + { + return 0; + } + + const ptrdiff_t code_length = range_size(code); + struct buffer return_of_function; + SET_NULL_TO_BUFFER(return_of_function); + ptrdiff_t index = 0; + struct range function; + function.start = code->start; + function.finish = code->start; + const char* previous_pos = code->start; + + while (-1 != (index = string_index_of(function.start, code->finish, + function_call_start, function_call_start + 2))) + { + if (!buffer_resize(&return_of_function, 0)) + { + buffer_release(&return_of_function); + return 0; + } + + function.start += index; + function.finish = find_any_symbol_like_or_not_like_that( + function.start, code->finish, + function_call_finish, 1, 1, 1); + + if (function.finish == code->finish && function_call_finish[0] != *function.finish) + { + buffer_release(&return_of_function); + return 0; + } + + if (previous_pos < function.start && !buffer_append_char(output, previous_pos, function.start - previous_pos)) + { + buffer_release(&return_of_function); + return 0; + } + + function.start += 2; + + if (!interpreter_evaluate_function(project, target, &function, &return_of_function)) + { + buffer_release(&return_of_function); + return 0; + } + + if (!buffer_append(output, return_of_function.data, buffer_size(&return_of_function))) + { + buffer_release(&return_of_function); + return 0; + } + + previous_pos = 1 + function.finish; + function.start = previous_pos; + } + + buffer_release(&return_of_function); + return buffer_append_char(output, previous_pos, code_length - (previous_pos - code->start)); +} + +uint8_t interpreter_xml_tag_should_be_skip_by_if_or_unless(const void* project, const void* target, + const char* tag_start, const char* tag_finish, uint8_t* skip) +{ + static const char* if_str = "if"; + static const char* unless_str = "unless"; + /**/ + static const uint8_t if_length = 2; + static const uint8_t unless_length = 6; + + if (range_in_parts_is_null_or_empty(tag_start, tag_finish) || NULL == skip) + { + return 0; + } + + struct range if_and_unless; + + /**/ + struct buffer output_of_code; + + SET_NULL_TO_BUFFER(output_of_code); + + /**/ + const char* if_and_unless_strings[2]; + + if_and_unless_strings[0] = if_str; + + if_and_unless_strings[1] = unless_str; + + /**/ + uint8_t if_and_unless_lengths[2]; + + if_and_unless_lengths[0] = if_length; + + if_and_unless_lengths[1] = unless_length; + + /**/ + uint8_t bool_value_to_pass[] = { 1, 0 }; + + for (uint8_t j = 0; j < 2; ++j) + { + uint8_t bool_value = 0; + + if (xml_get_attribute_value(tag_start, tag_finish, + if_and_unless_strings[j], if_and_unless_lengths[j], &if_and_unless)) + { + if (!interpreter_evaluate_code(project, target, &if_and_unless, &output_of_code)) + { + buffer_release(&output_of_code); + return 0; + } + + const char* start = buffer_char_data(&output_of_code, 0); + + if (NULL == start) + { + buffer_release(&output_of_code); + return 0; + } + + const char* finish = buffer_char_data(&output_of_code, buffer_size(&output_of_code) - 1); + + if (NULL == finish) + { + buffer_release(&output_of_code); + return 0; + } + + if (!bool_parse(start, 1 + finish, &bool_value)) + { + buffer_release(&output_of_code); + return 0; + } + + if (!buffer_resize(&output_of_code, 0)) + { + buffer_release(&output_of_code); + return 0; + } + + if (bool_value_to_pass[j] == bool_value) + { + continue; + } + + buffer_release(&output_of_code); + *skip = 1; + return 1; + } + } + + buffer_release(&output_of_code); + *skip = 0; + return 1; +} + +uint8_t interpreter_get_arguments_from_xml_tag_record(const void* project, const void* target, + const char* start_of_attributes, const char* finish_of_attributes, + const char** attributes, const uint8_t* attributes_lengths, + uint8_t attributes_count, struct buffer* output) +{ + for (uint8_t i = 0; i < attributes_count; ++i) + { + struct buffer* argument = buffer_buffer_data(output, i); + + if (NULL == argument) + { + return 0; + } + + struct range attribute_value; + + if (!xml_get_attribute_value(start_of_attributes, finish_of_attributes, attributes[i], attributes_lengths[i], + &attribute_value)) + { + continue; + } + + if (!buffer_resize(argument, 0) || + (!range_is_null_or_empty(&attribute_value) && + !interpreter_evaluate_code(project, target, &attribute_value, argument))) + { + return 0; + } + } + + return 1; +} + +static const char* interpreter_task_str[] = +{ + "al", "asminfo", "attrib", "aximp", "call", "choose", "cl", "copy", "csc", + "delay-sign", "delete", "description", "echo", "exec", "fail", "foreach", "get", "gunzip", "if", + "ilasm", "ildasm", "include", "jsc", "lib", "license", "link", "loadfile", "loadtasks", "mail", "mc", "midl", + "mkdir", "move", "ndoc", "nunit2", "property", "rc", "readregistry", "regasm", + "regex", "regsvcs", "resgen", "script", "servicecontroller", "setenv", "sleep", "solution", "style", + "sysinfo", "tar", "tlbexp", "tlbimp", "touch", "trycatch", "tstamp", "untar", "unzip", "uptodate", "vbc", + "vjc", "xmlpeek", "xmlpoke", "zip" +}; + +enum interpreter_task +{ + al_, asminfo_, attrib_, aximp_, call_, choose_, cl_, copy_, csc_, + delay_sign_, delete_, description_, echo_, exec_, fail_, foreach_, get_, gunzip_, if_, + ilasm_, ildasm_, include_, jsc_, lib_, license_, link_, loadfile_, loadtasks_, mail_, mc_, midl_, + mkdir_, move_, ndoc_, nunit2_, property_task_, rc_, readregistry_, regasm_, + regex_, regsvcs_, resgen_, script_, servicecontroller_, setenv_, sleep_, solution_, style_, + sysinfo_, tar_, tlbexp_, tlbimp_, touch_, trycatch_, tstamp_, untar_, unzip_, uptodate_, vbc_, + vjc_, xmlpeek_, xmlpoke_, zip_, UNKNOWN_TASK +}; + +uint8_t interpreter_get_task(const char* task_name_start, const char* task_name_finish) +{ + return common_string_to_enum(task_name_start, task_name_finish, + interpreter_task_str, UNKNOWN_TASK); +} + +uint8_t interpreter_evaluate_task(void* project, const void* target, uint8_t command, + const char* attributes_start, const char* element_finish) +{ + if (UNKNOWN_TASK < command || range_in_parts_is_null_or_empty(attributes_start, element_finish)) + { + return 0; + } + + const char* attributes_finish = xml_get_tag_finish_pos(attributes_start, element_finish); + + switch (command) + { + case al_: + break; + + case asminfo_: + break; + + case attrib_: + break; + + case aximp_: + break; + + case call_: + break; + + case choose_: + break; + + case cl_: + break; + + case copy_: + break; + + case csc_: + break; + + case delay_sign_: + break; + + case delete_: + break; + + case description_: + break; + + case echo_: + return echo_evaluate_task(project, target, attributes_start, attributes_finish, element_finish); + + case exec_: + return exec_evaluate_task(project, target, attributes_start, attributes_finish, element_finish); + + case fail_: + break; + + case foreach_: + break; + + case get_: + break; + + case gunzip_: + break; + + case if_: + break; + + case ilasm_: + break; + + case ildasm_: + break; + + case include_: + break; + + case jsc_: + break; + + case lib_: + break; + + case license_: + break; + + case link_: + break; + + case loadfile_: + break; + + case loadtasks_: + break; + + case mail_: + break; + + case mc_: + break; + + case midl_: + break; + + case mkdir_: + break; + + case move_: + break; + + case ndoc_: + break; + + case nunit2_: + break; + + case property_task_: + break; + + case rc_: + break; + + case readregistry_: + break; + + case regasm_: + break; + + case regex_: + break; + + case regsvcs_: + break; + + case resgen_: + break; + + case script_: + break; + + case servicecontroller_: + break; + + case setenv_: + break; + + case sleep_: + break; + + case solution_: + break; + + case style_: + break; + + case sysinfo_: + break; + + case tar_: + break; + + case tlbexp_: + break; + + case tlbimp_: + break; + + case touch_: + break; + + case trycatch_: + break; + + case tstamp_: + break; + + case untar_: + break; + + case unzip_: + break; + + case uptodate_: + break; + + case vbc_: + break; + + case vjc_: + break; + + case xmlpeek_: + break; + + case xmlpoke_: + break; + + case zip_: + break; + + case UNKNOWN_TASK: + default: + break; + } + + return 0; +} diff --git a/interpreter.h b/interpreter.h new file mode 100644 index 0000000..f0dc0d8 --- /dev/null +++ b/interpreter.h @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _INTERPRETER_H_ +#define _INTERPRETER_H_ + +#include + +struct buffer; +struct range; + +uint8_t interpreter_get_value_from_quote(const struct range* quote, struct range* value); +uint8_t interpreter_disassemble_function(const struct range* function, + struct range* name_space, struct range* name, struct range* arguments_area); +uint8_t interpreter_get_values_for_arguments( + const void* project, const void* target, + const struct range* arguments_area, struct buffer* values); +uint8_t interpreter_evaluate_function(const void* project, const void* target, + const struct range* function, struct buffer* return_of_function); +uint8_t interpreter_evaluate_code(const void* project, const void* target, + const struct range* code, struct buffer* output); +uint8_t interpreter_xml_tag_should_be_skip_by_if_or_unless(const void* project, const void* target, + const char* tag_start, const char* tag_finish, + uint8_t* skip); +uint8_t interpreter_get_arguments_from_xml_tag_record(const void* project, const void* target, + const char* start_of_attributes, const char* finish_of_attributes, + const char** attributes, const uint8_t* attributes_lengths, + uint8_t attributes_count, struct buffer* output); +uint8_t interpreter_get_task(const char* task_name_start, const char* task_name_finish); +uint8_t interpreter_evaluate_task(void* project, const void* target, uint8_t command, + const char* attributes_start, const char* element_finish); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..cb7ffe5 --- /dev/null +++ b/main.c @@ -0,0 +1,205 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "argument_parser.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "date_time.h" +#include "echo.h" +#include "environment.h" +#include "exec.h" +#include "file_system.h" +#include "interpreter.h" +#include "math_unit.h" +#include "operating_system.h" +#include "path.h" +#include "project.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" +#include "target.h" +#include "version.h" +#include "xml.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef NDEBUG +#define _CRTDBG_MAP_ALLOC +#include +#endif +#endif + +#ifndef _WIN32 +#include + +static pid_t pid = 0; +#endif + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +#define LOGO "Program version "PROGRAM_VERSION"\n" \ + "The MIT License (MIT)\n" \ + "Copyright(c) 2019 https://github.com/TheVice/" +#define LOGO_LENGTH strlen(LOGO) +#define SAMPLE_USING "Sample using - [options] ..." /**/ +#define SAMPLE_USING_LENGTH 37 +#define OPTIONS "Options:\n" \ + "\t-buildfile: - set path to project file. Short form /f:.\n" \ + "\t-D: - define property. For example -D:\"property name\"=\"property value\".\n" \ + "\t-nologo - do not display program version, license and copyright information.\n" \ + "\t-help - print this message. Short form -h." +#define OPTIONS_LENGTH 260 + +uint8_t print_status(int status) +{ +#ifndef _WIN32 + + if (getpid() != pid) + { + return 0; + } + +#endif + return 8 == echo(0, Default, NULL, + status ? Info : Error, + status ? "SUCCESS." : "FAILURE.", + 8, 1, 0); +} + +#if defined(_MSC_VER) +int wmain(int argc, wchar_t** argv) +#else +int main(int argc, char** argv) +#endif +{ +#ifdef _WIN32 +#ifndef NDEBUG + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif +#endif +#ifndef _WIN32 + pid = getpid(); +#endif + /*uint64_t time_now = datetime_now();*/ + + /*if (argc < 2) + { + TODO: list current directory. + }*/ + + if (argc < 2) + { + if (!echo(0, Default, NULL, NoLevel, LOGO, LOGO_LENGTH, 1, 0) || + !echo(0, Default, NULL, Info, SAMPLE_USING, SAMPLE_USING_LENGTH, 1, 0) || + !echo(0, Default, NULL, Info, OPTIONS, OPTIONS_LENGTH, 1, 0)) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + +#if defined(_MSC_VER) + + if (!argument_parser_wchar_t(1, argc, argv)) +#else + if (!argument_parser_char(1, argc, argv)) +#endif + { + argument_parser_release(); + + if (!echo(0, Default, NULL, NoLevel, LOGO, LOGO_LENGTH, 1, 0)) + { + argc = 0; + } + + if (!echo(0, Default, NULL, Error, "Failed to parse command arguments.", 34, 1, 0)) + { + argc = 0; + } + + return EXIT_FAILURE; + } + + if (!argument_parser_get_no_logo()) + { + if (!echo(0, Default, NULL, NoLevel, LOGO, LOGO_LENGTH, 1, argument_parser_get_verbose())) + { + argument_parser_release(); + return EXIT_FAILURE; + } + } + + if (argument_parser_get_help() || NULL == argument_parser_get_build_file(0)) + { + if (!echo(0, Default, NULL, Info, SAMPLE_USING, SAMPLE_USING_LENGTH, 1, argument_parser_get_verbose()) || + !echo(0, Default, NULL, Info, OPTIONS, OPTIONS_LENGTH, 1, argument_parser_get_verbose())) + { + argc = 0; + } + + argument_parser_release(); + argc = 0 < argc; + return argc ? EXIT_SUCCESS : EXIT_FAILURE; + } + + for (argc = 0; ; ++argc) + { + const struct range* build_file = argument_parser_get_build_file(argc); + + if (NULL == build_file) + { + break; + } + + void* project = NULL; + + if (!project_new(&project)) + { + argc = 0; + break; + } + + if (!project_add_properties(project, NULL, argument_parser_get_properties(), argument_parser_get_verbose())) + { + project_unload(project); + argc = 0; + break; + } + + const uint8_t is_loaded = project_load_from_build_file(build_file->start, project, + argument_parser_get_verbose()); + + if (!is_loaded) + { + project_unload(project); + argc = 0; + break; + } + + project_unload(project); + } + + argument_parser_release(); + /*time_now = datetime_now() - time_now; + printf("Total time: %"PRId64" second(s).\n", time_now);*/ + argc = 0 < argc; + print_status(argc); + return argc ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/math_unit.c b/math_unit.c new file mode 100644 index 0000000..2fd626b --- /dev/null +++ b/math_unit.c @@ -0,0 +1,377 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "math_unit.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "range.h" +#include "string_unit.h" + +#include +#include + +double math_abs(double value) +{ + return value < 0 ? -value : value; +} + +double math_ceiling(double value) +{ + const double rest = modf(value, &value); + return (rest < 2 * DBL_EPSILON ? value : 1 + value); +} + +double math_floor(double value) +{ + const double rest = modf(value, &value); + return (rest < 0 ? value - 1 : value); +} + +double math_round(double value) +{ + const double rest = modf(value, &value); + + if (math_abs(value) < 1) + { + return (0.5 + 2 * DBL_EPSILON < math_abs(rest) ? (rest < 0 ? value - 1 : value + 1) : value); + } + + return (0.5 - 2 * DBL_EPSILON < math_abs(rest) ? (rest < 0 ? value - 1 : value + 1) : value); +} + +double math_acos(double value) +{ + return acos(value); +} + +double math_asin(double value) +{ + return asin(value); +} +double math_atan(double value) +{ + return atan(value); +} + +double math_atan2(double x_value, double y_value) +{ + return atan2(y_value, x_value); +} + +double math_cos(double value) +{ + return cos(value); +} + +double math_cosh(double value) +{ + return cosh(value); +} + +double math_exp(double value) +{ + return exp(value); +} + +double math_log(double value) +{ + return log(value); +} + +double math_log10(double value) +{ + return log10(value); +} + +double math_max(double value1, double value2) +{ + return value1 < value2 ? value2 : value1; +} + +double math_min(double value1, double value2) +{ + return value1 < value2 ? value1 : value2; +} + +double math_pow(double base_value, double exponent_value) +{ + return pow(base_value, exponent_value); +} + +int8_t math_sign(double value) +{ + return value < 0 ? -1 : 1; +} + +double math_sin(double value) +{ + return sin(value); +} + +double math_sinh(double value) +{ + return sinh(value); +} + +double math_sqrt(double value) +{ + return sqrt(value); +} + +double math_tan(double value) +{ + return tan(value); +} + +double math_tanh(double value) +{ + return tanh(value); +} + +double math_cot(double value) +{ + return 1 / tan(value); +} + +double math_coth(double value) +{ + return 1 / tanh(value); +} + +int64_t math_truncate(double value) +{ + const double rest = modf(value, &value); + (void)rest; + return (int64_t)value; +} + +static const double PI = 3.1415926535897931; +static const uint8_t D180 = 180; + +double math_degrees(double r) +{ + return r * D180 / PI; +} + +double math_radians(double d) +{ + return d * PI / D180; +} + +static const char* math_function_str[] = +{ + "abs", "ceiling", "floor", "round", "acos", "asin", "atan", "atan2", + "cos", "cosh", "exp", "log", "log10", "max", "min", "pow", "sign", + "sin", "sinh", "sqrt", "tan", "tanh", "cot", "coth", + "truncate", "PI", "E", "degrees", "radians", + "addition", "subtraction", "multiplication", "division", + "near", "less", "greater" +}; + +enum math_function +{ + abs_, ceiling_, floor_, round_, acos_, asin_, atan_, atan2_, + cos_, cosh_, exp_, log_, log10_, max_, min_, pow_, sign_, + sin_, sinh_, sqrt_, tan_, tanh_, cot_, coth_, + truncate_, PI_, E_, degrees_, radians_, + addition_, subtraction_, multiplication_, division_, + near_, less_, greater_, + UNKNOWN_MATH_FUNCTION +}; + +uint8_t math_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, math_function_str, UNKNOWN_MATH_FUNCTION); +} + +uint8_t math_double_near(double value1, double value2) +{ + static const double epsilon = 2 * DBL_EPSILON; + return (value2 - epsilon < value1 && value1 < value2 + epsilon); +} + +uint8_t math_double_near_to_zero(double value) +{ + return math_double_near(value, 0.0); +} + +uint8_t math_exec_function(uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, struct buffer* output) +{ + if (UNKNOWN_MATH_FUNCTION <= function || NULL == arguments || 2 < arguments_count || NULL == output) + { + return 0; + } + + struct range argument1; + + struct range argument2; + + argument1.start = argument2.start = argument1.finish = argument2.finish = NULL; + + double double_argument_1 = 0; + + double double_argument_2 = 0; + + if (1 == arguments_count) + { + if (!common_get_one_argument(arguments, &argument1, 1)) + { + return 0; + } + + double_argument_1 = double_parse(argument1.start); + } + else if (2 == arguments_count) + { + if (!common_get_two_arguments(arguments, &argument1, &argument2, 1)) + { + return 0; + } + + double_argument_1 = double_parse(argument1.start); + double_argument_2 = double_parse(argument2.start); + } + + switch (function) + { + case abs_: + return 1 == arguments_count && double_to_string(math_abs(double_argument_1), output); + + case ceiling_: + return 1 == arguments_count && double_to_string(math_ceiling(double_argument_1), output); + + case floor_: + return 1 == arguments_count && double_to_string(math_floor(double_argument_1), output); + + case round_: + return 1 == arguments_count && double_to_string(math_round(double_argument_1), output); + + case acos_: + if (1 != arguments_count || double_argument_1 < -1 || 1 < double_argument_1) + { + break; + } + + return double_to_string(math_acos(double_argument_1), output); + + case asin_: + if (1 != arguments_count || double_argument_1 < -1 || 1 < double_argument_1) + { + break; + } + + return double_to_string(math_asin(double_argument_1), output); + + case atan_: + return 1 == arguments_count && double_to_string(math_atan(double_argument_1), output); + + case atan2_: + return 2 == arguments_count && double_to_string(math_atan2(double_argument_1, double_argument_2), output); + + case cos_: + return 1 == arguments_count && double_to_string(math_cos(double_argument_1), output); + + case cosh_: + return 1 == arguments_count && double_to_string(math_cosh(double_argument_1), output); + + case exp_: + return 1 == arguments_count && double_to_string(math_exp(double_argument_1), output); + + case log_: + return 1 == arguments_count && double_to_string(math_log(double_argument_1), output); + + case log10_: + return 1 == arguments_count && double_to_string(math_log10(double_argument_1), output); + + case max_: + return 2 == arguments_count && double_to_string(math_max(double_argument_1, double_argument_2), output); + + case min_: + return 2 == arguments_count && double_to_string(math_min(double_argument_1, double_argument_2), output); + + case pow_: + return 2 == arguments_count && double_to_string(math_pow(double_argument_1, double_argument_2), output); + + case sign_: + return 1 == arguments_count && int_to_string(math_sign(double_argument_1), output); + + case sin_: + return 1 == arguments_count && double_to_string(math_sin(double_argument_1), output); + + case sinh_: + return 1 == arguments_count && double_to_string(math_sinh(double_argument_1), output); + + case sqrt_: + if (1 != arguments_count || double_argument_1 < 0) + { + return 0; + } + + return double_to_string(math_sqrt(double_argument_1), output); + + case tan_: + return 1 == arguments_count && double_to_string(math_tan(double_argument_1), output); + + case tanh_: + return 1 == arguments_count && double_to_string(math_tanh(double_argument_1), output); + + case cot_: + return 1 == arguments_count && double_to_string(math_cot(double_argument_1), output); + + case coth_: + return 1 == arguments_count && double_to_string(math_coth(double_argument_1), output); + + case truncate_: + return 1 == arguments_count && int64_to_string(math_truncate(double_argument_1), output); + + case PI_: + return !arguments_count && common_append_string_to_buffer("3.1415926535897931", output); + + case E_: + return !arguments_count && common_append_string_to_buffer("2.7182818284590451", output); + + case degrees_: + return 1 == arguments_count && double_to_string(math_degrees(double_argument_1), output); + + case radians_: + return 1 == arguments_count && double_to_string(math_radians(double_argument_1), output); + + case addition_: + return 2 == arguments_count && double_to_string(double_argument_1 + double_argument_2, output); + + case subtraction_: + return 2 == arguments_count && double_to_string(double_argument_1 - double_argument_2, output); + + case multiplication_: + return 2 == arguments_count && double_to_string(double_argument_1 * double_argument_2, output); + + case division_: + if (2 != arguments_count || math_double_near_to_zero(double_argument_2)) + { + break; + } + + return double_to_string(double_argument_1 / double_argument_2, output); + + case near_: + return 2 == arguments_count && bool_to_string(math_double_near(double_argument_1, double_argument_2), output); + + case less_: + return 2 == arguments_count && bool_to_string(double_argument_1 < double_argument_2, output); + + case greater_: + return 2 == arguments_count && bool_to_string(double_argument_1 > double_argument_2, output); + + case UNKNOWN_MATH_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/math_unit.h b/math_unit.h new file mode 100644 index 0000000..f7894f1 --- /dev/null +++ b/math_unit.h @@ -0,0 +1,59 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _MATH_UNIT_H_ +#define _MATH_UNIT_H_ + +#include + +struct buffer; + +double math_abs(double value); +double math_ceiling(double value); +double math_floor(double value); +double math_round(double value); + +double math_acos(double value); +double math_asin(double value); +double math_atan(double value); +double math_atan2(double x_value, double y_value); + +double math_cos(double value); +double math_cosh(double value); + +double math_exp(double value); +double math_log(double value); +double math_log10(double value); + +double math_max(double value1, double value2); +double math_min(double value1, double value2); + +double math_pow(double base_value, double exponent_value); + +int8_t math_sign(double value); + +double math_sin(double value); +double math_sinh(double value); + +double math_sqrt(double value); + +double math_tan(double value); +double math_tanh(double value); + +double math_cot(double value); +double math_coth(double value); + +int64_t math_truncate(double value); + +double math_degrees(double r); +double math_radians(double d); + +uint8_t math_get_function(const char* name_start, const char* name_finish); +uint8_t math_exec_function(uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, struct buffer* output); + +#endif diff --git a/operating_system.c b/operating_system.c new file mode 100644 index 0000000..55f64e2 --- /dev/null +++ b/operating_system.c @@ -0,0 +1,215 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "operating_system.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "environment.h" +#include "range.h" +#include "string_unit.h" + +uint8_t operating_system_parse(const char* start, const char* finish, struct OperatingSystem* os) +{ + if (range_in_parts_is_null_or_empty(start, finish) || + NULL == os || + sizeof(os->VersionString) / sizeof(*os->VersionString) < (size_t)(finish - start)) + { + return 0; + } + + static const char* windows_label = Win32NT_str; + + if (string_starts_with(start, finish, windows_label, windows_label + Win32NT_str_length)) + { + os->Platform = Win32; + } + else + { + os->Platform = Unix; + } + + /*TODO: os->Platform = MacOSX;*/ + + if (!version_parse(start, finish, &os->Version)) + { + return 0; + } + + for (uint16_t i = 0, count = sizeof(os->VersionString) / sizeof(*os->VersionString); i < count; ++i) + { + if (start + i < finish) + { + os->VersionString[i] = *(start + i); + } + else + { + os->VersionString[i] = '\0'; + break; + } + } + + return 1; +} + +enum PlatformID operating_system_get_platform(const struct OperatingSystem* os) +{ + if (NULL == os) + { + return UINT8_MAX; + } + + return os->Platform; +} + +struct Version operating_system_get_version(const struct OperatingSystem* os) +{ + if (NULL == os) + { + static struct Version ver; + ver.major = ver.minor = ver.build = ver.revision = 0; + return ver; + } + + return os->Version; +} + +const char* operating_system_to_string(const struct OperatingSystem* os) +{ + if (NULL == os) + { + return NULL; + } + + return os->VersionString; +} + +const char* platform_get_name() +{ + const struct OperatingSystem* os = environment_get_operating_system(); + + if (NULL == os) + { + return NULL; + } + + return os->VersionString; +} + +uint8_t platform_is_unix() +{ + return Unix == operating_system_get_platform(environment_get_operating_system()); +} + +uint8_t platform_is_windows() +{ + return Win32 == operating_system_get_platform(environment_get_operating_system()); +} + +static const char* os_function_str[] = +{ + "get-platform", "get-version", "to-string", "get-name", "is-unix", "is-windows" +}; + +enum os_function +{ + get_platform, get_version, to_string, get_name, is_unix, is_windows, + UNKNOWN_OS_FUNCTION +}; + +uint8_t os_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, os_function_str, UNKNOWN_OS_FUNCTION); +} + +uint8_t os_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_OS_FUNCTION <= function || NULL == arguments || 1 < arguments_count || NULL == output) + { + return 0; + } + + struct range argument; + + argument.start = argument.finish = NULL; + + if (1 == arguments_count && !common_get_one_argument(arguments, &argument, 0)) + { + return 0; + } + + struct OperatingSystem os; + + if (1 == arguments_count && !operating_system_parse(argument.start, argument.finish, &os)) + { + return 0; + } + + switch (function) + { + case get_platform: + { + switch (operating_system_get_platform(&os)) + { + case Win32: + return common_append_string_to_buffer("Win32", output); + + case Unix: + return common_append_string_to_buffer("Unix", output); + + /*TODO: case MacOSX:*/ + default: + break; + } + } + break; + + case get_version: + { + const struct Version ver = operating_system_get_version(&os); + return version_to_string(&ver, output); + } + + case to_string: + return common_append_string_to_buffer(operating_system_to_string(&os), output); + + case UNKNOWN_OS_FUNCTION: + default: + break; + } + + return 0; +} + +uint8_t platform_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_OS_FUNCTION <= function || NULL == arguments || arguments_count || NULL == output) + { + return 0; + } + + switch (function) + { + case get_name: + return common_append_string_to_buffer(platform_get_name(), output); + + case is_unix: + return bool_to_string(platform_is_unix(), output); + + case is_windows: + return bool_to_string(platform_is_windows(), output); + break; + + case UNKNOWN_OS_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/operating_system.h b/operating_system.h new file mode 100644 index 0000000..c740e62 --- /dev/null +++ b/operating_system.h @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _OPERATING_SYSTEM_H_ +#define _OPERATING_SYSTEM_H_ + +#include "version.h" + +#include + +enum PlatformID +{ + Win32, + Unix/*, + MacOSX*/ +}; + +struct OperatingSystem +{ + enum PlatformID Platform; + struct Version Version; +#if defined(_WIN32) + char VersionString[INT8_MAX + 1]; +#else + char VersionString[UINT8_MAX + 17]; +#endif +}; + +struct buffer; + +#define Win32NT_str "Microsoft Windows NT" +#define Win32NT_str_length 20 + +uint8_t operating_system_parse(const char* start, const char* finish, struct OperatingSystem* os); +enum PlatformID operating_system_get_platform(const struct OperatingSystem* os); +struct Version operating_system_get_version(const struct OperatingSystem* os); +const char* operating_system_to_string(const struct OperatingSystem* os); + +const char* platform_get_name(); +uint8_t platform_is_unix(); +uint8_t platform_is_windows(); + +uint8_t os_get_function(const char* name_start, const char* name_finish); +uint8_t os_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t platform_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/path.c b/path.c new file mode 100644 index 0000000..26c344d --- /dev/null +++ b/path.c @@ -0,0 +1,1117 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "path.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "environment.h" +#include "file_system.h" +#include "project.h" +#include "range.h" +#include "string_unit.h" + +#if defined(_WIN32) +#include +#else + +#define _POSIXSOURCE 1 + +#include +#include +#endif + +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +#ifndef L_tmpnam_s +#define L_tmpnam_s L_tmpnam +#endif + +static const char posix_delimiter = '/'; +static const char windows_delimiter = '\\'; + +#if defined(_WIN32) +static const char delimiter = '\\'; +#else +static const char delimiter = '/'; +#endif + +char path_delimiter() +{ + return delimiter; +} + +char path_posix_delimiter() +{ + return '/'; +} + +char path_windows_delimiter() +{ + return '\\'; +} + +uint8_t path_change_extension(const char* path_start, const char* path_finish, + const char* ext_start, const char* ext_finish, struct buffer* path) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish)) + { + return 0; + } + + struct range cur_ext; + + if (path_get_extension(path_start, path_finish, &cur_ext)) + { + if (string_equal(cur_ext.start, cur_ext.finish, ext_start, ext_finish)) + { + return buffer_append_char(path, path_start, path_finish - path_start); + } + + if (!buffer_append_char(path, path_start, cur_ext.start - path_start)) + { + return 0; + } + } + else + { + if (!buffer_append_char(path, path_start, path_finish - path_start)) + { + return 0; + } + } + + if (!range_in_parts_is_null_or_empty(ext_start, ext_finish)) + { + if (!buffer_append_char(path, ext_start, ext_finish - ext_start)) + { + return 0; + } + } + + return 1; +} + +uint8_t path_combine(const char* path1_start, const char* path1_finish, + const char* path2_start, const char* path2_finish, struct buffer* path) +{ + if (NULL == path1_start || NULL == path1_finish || + NULL == path2_start || NULL == path2_finish || + NULL == path || + path1_finish < path1_start || path2_finish < path2_start) + { + return 0; + } + + const ptrdiff_t size = buffer_size(path); + + if (!buffer_append_char(path, NULL, + path1_finish - path1_start + 1 + path2_finish - path2_start)) + { + return 0; + } + + if (!buffer_resize(path, size)) + { + return 0; + } + + if (!buffer_append_char(path, path1_start, path1_finish - path1_start)) + { + return 0; + } + + if (!buffer_push_back(path, delimiter)) + { + return 0; + } + + if (!buffer_append_char(path, path2_start, path2_finish - path2_start)) + { + return 0; + } + + ptrdiff_t new_size = buffer_size(path) - size; + replace_double_char_by_single((char*)buffer_data(path, size), &new_size, delimiter); + return buffer_resize(path, size + new_size); +} + +uint8_t path_get_directory_name(const char* path_start, const char* path_finish, struct range* directory) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || NULL == directory) + { + return 0; + } + + directory->finish = find_any_symbol_like_or_not_like_that( + path_finish - 1, path_start, &delimiter, 1, 1, -1); + directory->start = path_start; + + if (!string_trim(directory)) + { + return 0; + } + + if (delimiter == (*directory->start) && range_is_null_or_empty(directory)) + { + ++directory->finish; + } + + return !range_is_null_or_empty(directory); +} + +uint8_t path_get_extension(const char* path_start, const char* path_finish, struct range* ext) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || NULL == ext) + { + return 0; + } + + const char* file_name_start = find_any_symbol_like_or_not_like_that( + path_finish - 1, path_start, &delimiter, 1, 1, -1); + ext->start = find_any_symbol_like_or_not_like_that(path_finish - 1, file_name_start, ".", 1, 1, -1); + ext->finish = path_finish; + + if (!string_trim(ext) || '.' != (*ext->start)) + { + return 0; + } + + if (ext->start + 1 == ext->finish) + { + ext->start = ext->finish; + } + + return ext->start < ext->finish; +} + +uint8_t path_get_file_name(const char* path_start, const char* path_finish, struct range* file_name) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || + NULL == file_name) + { + return 0; + } + + const char* file_name_start = find_any_symbol_like_or_not_like_that(path_finish - 1, path_start, + &delimiter, 1, 1, -1); + + if (delimiter == (*file_name_start) && file_name_start < path_finish) + { + ++file_name_start; + } + + file_name->start = file_name_start; + file_name->finish = path_finish; + return string_trim(file_name); +} + +uint8_t path_get_file_name_without_extension(const char* path_start, const char* path_finish, + struct range* file_name) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || + NULL == file_name) + { + return 0; + } + + if (!path_get_file_name(path_start, path_finish, file_name)) + { + return 0; + } + + struct range ext; + + const uint8_t has_extension = path_get_extension(file_name->start, file_name->finish, &ext); + + if (has_extension) + { + file_name->finish = ext.start; + } + + return string_trim(file_name); +} + +uint8_t path_get_full_path(const char* root_start, const char* root_finish, + const char* path_start, const char* path_finish, struct buffer* full_path) +{ +#if defined(_WIN32) + static const char* upper_level = "\\.."; +#else + static const char* upper_level = "/.."; +#endif + static const ptrdiff_t upper_level_length = 3; + static const char tilde = '~'; + + if (NULL == path_start || + NULL == path_finish || + path_finish < path_start || + NULL == full_path) + { + return 0; + } + + const ptrdiff_t size = buffer_size(full_path); + + if (tilde == (*path_start)) + { + if (!environment_get_folder_path(UserProfile, full_path)) + { + return 0; + } + } + else if (!path_is_path_rooted(path_start, path_finish)) + { + if (range_in_parts_is_null_or_empty(root_start, root_finish)) + { + return 0; + } + + if (!buffer_append_char(full_path, root_start, root_finish - root_start)) + { + return 0; + } + } + + const ptrdiff_t start_path_length = buffer_size(full_path) - size; + + if (0 < start_path_length) + { + if (!buffer_append_char(full_path, NULL, start_path_length + path_finish - path_start + 1) || + !buffer_resize(full_path, size + start_path_length)) + { + return 0; + } + } + + char* start = NULL; + char* finish = NULL; + + if (0 < start_path_length) + { + start = (char*)buffer_data(full_path, size); + finish = start + start_path_length; + + if (!path_combine(start, finish, path_start, path_finish, full_path)) + { + return 0; + } + + start = (char*)buffer_data(full_path, size + 2 * start_path_length); + finish = (char*)(buffer_data(full_path, 0) + buffer_size(full_path)); + + if (NULL == start || !buffer_resize(full_path, size + start_path_length)) + { + return 0; + } + + if (!buffer_append_char(full_path, start, finish - start)) + { + return 0; + } + } + + start = (char*)buffer_data(full_path, size); + finish = 0 < buffer_size(full_path) ? (char*)(buffer_data(full_path, 0) + buffer_size(full_path)) : NULL; + ptrdiff_t index = 0; + const char* real_start = start; + + while (-1 != (index = string_index_of(start, finish, upper_level, upper_level + upper_level_length))) + { + if (0 == index) + { + return 0; + } + + const char* start_ = find_any_symbol_like_or_not_like_that(start + index - 1, real_start, &delimiter, 1, 1, + -1); + start = start + index + upper_level_length; + + if (!buffer_resize(full_path, size + start_ - real_start)) + { + return 0; + } + + if (start < finish) + { + if (!buffer_append_char(full_path, start, finish - start)) + { + return 0; + } + + start -= index + upper_level_length; + finish = (char*)(buffer_data(full_path, 0) + buffer_size(full_path)); + } + else + { + start = finish = NULL; + break; + } + } + + if (size == buffer_size(full_path)) + { + if (!buffer_append_char(full_path, path_start, path_finish - path_start)) + { + return 0; + } + } + + start = (char*)buffer_data(full_path, size); + finish = 0 < buffer_size(full_path) ? (char*)(buffer_data(full_path, 0) + buffer_size(full_path)) : NULL; + + if (!range_in_parts_is_null_or_empty(start, finish)) + { + if (string_contains(start, finish, &tilde, &tilde + 1) || + finish != find_any_symbol_like_or_not_like_that(start, finish, &tilde, 1, 1, 1)) + { + return 0; + } + } + + index = buffer_size(full_path) - size; + + if (0 < index) + { + const ptrdiff_t current_index = index; + replace_double_char_by_single(start, &index, delimiter); + + if (current_index != index && !buffer_resize(full_path, size + index)) + { + return 0; + } + } + + return 1; +} + +uint8_t path_get_path_root(const char* path_start, const char* path_finish, struct range* root) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || NULL == root) + { + return 0; + } + + root->start = path_start; +#if defined(_WIN32) + const ptrdiff_t path_length = path_finish - path_start; + + if (1 < path_length && ':' == path_start[1]) + { + if (2 < path_length && + (delimiter == path_start[2] || + posix_delimiter == path_start[2])) + { + root->finish = 3 + root->start; + } + else + { + root->finish = 2 + root->start; + } + + return 1; + } + +#else + + if (path_start[0] == delimiter) + { + root->finish = 1 + root->start; + return 1; + } + +#endif + return 0; +} + +uint8_t path_get_temp_file_name(struct buffer* temp_file_name) +{ + if (NULL == temp_file_name) + { + return 0; + } + + const ptrdiff_t size = buffer_size(temp_file_name); +#if !defined(_WIN32) + int fd = -1; + + if (!common_append_string_to_buffer("/tmp/fileXXXXXX", temp_file_name) || + !buffer_push_back(temp_file_name, '\0')) + { + return 0; + } + + char* temp_file_path = (char*)buffer_data(temp_file_name, size); + + if (-1 == (fd = mkstemp(temp_file_path))) + { + return 0; + } + + close(fd); + return buffer_resize(temp_file_name, size + strlen(temp_file_path)); +#else + + if (!buffer_append_char(temp_file_name, NULL, L_tmpnam_s)) + { + return 0; + } + + char* temp_file_path = (char*)buffer_data(temp_file_name, size); +#if __STDC_SEC_API__ + + if (0 != tmpnam_s(temp_file_path, L_tmpnam_s)) + { + return 0; + } + +#else + tmpnam(temp_file_path); +#endif + ptrdiff_t length = strlen(temp_file_path); + + if (length < 1) + { + return 0; + } + + if ('.' == temp_file_path[length - 1]) + { + static const char point = '.'; + const char* ptr = find_any_symbol_like_or_not_like_that( + temp_file_path + length - 2, temp_file_path, &point, 1, 0, -1); + + if (ptr == temp_file_path || delimiter == *ptr) + { + return 0; + } + + ++ptr; + length = ptr - temp_file_path; + } + + if (path_is_path_rooted(temp_file_path, temp_file_path + length)) + { + return buffer_resize(temp_file_name, size + length); + } + + const ptrdiff_t temp_path_start = buffer_size(temp_file_name); + + if (!path_get_temp_path(temp_file_name)) + { + return 0; + } + + const ptrdiff_t temp_path_finish = buffer_size(temp_file_name); + + if (!buffer_append(temp_file_name, NULL, (ptrdiff_t)2 + temp_path_finish - size)) + { + return 0; + } + + const char* temp_path = (const char*)buffer_data(temp_file_name, temp_path_start); + const char* temp_path_ = (const char*)buffer_data(temp_file_name, temp_path_finish); + temp_file_path = (char*)buffer_data(temp_file_name, size); + + if (!buffer_resize(temp_file_name, temp_path_finish) || + !path_combine( + temp_path, temp_path_, + temp_file_path, temp_file_path + length, + temp_file_name)) + { + return 0; + } + + length = buffer_size(temp_file_name) - temp_path_finish; +#if __STDC_SEC_API__ + + if (0 != memcpy_s(temp_file_path, length, temp_path_, length)) + { + return 0; + } + +#else + memcpy(temp_file_path, temp_path_, length); +#endif + return buffer_resize(temp_file_name, size + length) && buffer_push_back(temp_file_name, '\0'); +#endif +} + +uint8_t path_get_temp_path(struct buffer* temp_path) +{ + if (NULL == temp_path) + { + return 0; + } + +#if defined(_WIN32) + const ptrdiff_t size = buffer_size(temp_path); + + if (!buffer_append_wchar_t(temp_path, NULL, 2)) + { + return 0; + } + + wchar_t* w = (wchar_t*)buffer_data(temp_path, size); + DWORD length = GetTempPathW(2, w); + + if (length < 3) + { + return 0; + } + + if (!buffer_append_char(temp_path, NULL, length) || + !buffer_append_wchar_t(temp_path, NULL, length)) + { + return 0; + } + + char* m = (char*)buffer_data(temp_path, size); + w = (wchar_t*)buffer_data(temp_path, size + length); + length = GetTempPathW(length, w); + + if (!length || !buffer_resize(temp_path, size + length)) + { + return 0; + } + + WIDE2MULTI(w, m, length); + + if (length < 1) + { + return 0; + } + + ptrdiff_t length_ = buffer_size(temp_path) - size; + const char* m_ = find_any_symbol_like_or_not_like_that(m + length_ - 1, m, &delimiter, 1, 0, -1); + m_ = find_any_symbol_like_or_not_like_that(m_, m + length_, &delimiter, 1, 1, 1); + length_ = m_ - m; + return buffer_resize(temp_path, size + length_); +#else + return common_append_string_to_buffer("/tmp", temp_path); +#endif +} + +uint8_t path_has_extension(const char* path_start, const char* path_finish) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish)) + { + return 0; + } + + struct range ext; + + return path_get_extension(path_start, path_finish, &ext); +} + +uint8_t path_is_path_rooted(const char* path_start, const char* path_finish) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish)) + { + return 0; + } + +#if defined(_WIN32) + + if (1 < path_finish - path_start) + { + return ':' == (path_start)[1]; + } + +#endif + return (path_start[0] == posix_delimiter || path_start[0] == delimiter); +} + +uint8_t path_get_directory_for_current_process(struct buffer* path) +{ + if (NULL == path) + { + return 0; + } + + const ptrdiff_t size = buffer_size(path); +#if defined(_WIN32) + + if (!buffer_append_wchar_t(path, NULL, 2)) + { + return 0; + } + + wchar_t* w = (wchar_t*)buffer_data(path, size); + DWORD length = GetCurrentDirectoryW(2, w); + + if (length == 0) + { + return 0; + } + + if (2 < length) + { + if (!buffer_resize(path, size) || + !buffer_append_char(path, NULL, (ptrdiff_t)length + 1) || + !buffer_append_wchar_t(path, NULL, (ptrdiff_t)length + 1)) + { + return 0; + } + + w = (wchar_t*)buffer_data(path, size + sizeof(char) * ((ptrdiff_t)length + 1)); + length = GetCurrentDirectoryW(length + 1, w); + + if (length == 0) + { + return 0; + } + } + + if (!buffer_resize(path, size + length)) + { + return 0; + } + + char* m = (char*)buffer_data(path, size); + WIDE2MULTI(w, m, length); + return 0 < length; +#else + + while (1) + { + if (!buffer_append_char(path, NULL, FILENAME_MAX)) + { + return 0; + } + + char* ptr = (char*)buffer_data(path, size); + const ptrdiff_t length = buffer_size(path) - size; + + if (!getcwd(ptr, (int)length)) + { + continue; + } + + if (!buffer_resize(path, size + strlen(ptr))) + { + return 0; + } + + break; + } + + return 1; +#endif +} + +uint8_t path_get_directory_for_current_image(struct buffer* path) +{ + if (NULL == path) + { + return 0; + } + + const ptrdiff_t size = buffer_size(path); +#if defined(_WIN32) + + while (1) + { + if (!buffer_append_wchar_t(path, NULL, FILENAME_MAX)) + { + return 0; + } + + wchar_t* ptr = (wchar_t*)buffer_data(path, size); + const ptrdiff_t size_ = (buffer_size(path) - size) / sizeof(wchar_t); + const DWORD real_size = GetModuleFileNameW(NULL, ptr, (DWORD)size_); + + if (0 == real_size) + { + return 0; + } + + if (real_size < (DWORD)size_) /*TODO: move condition to while.*/ + { + if (!buffer_resize(path, size + real_size) || + !buffer_append_wchar_t(path, NULL, (ptrdiff_t)2 + real_size)) + { + return 0; + } + + ptr = (wchar_t*)buffer_data(path, size); + + if (!buffer_resize(path, size + real_size) || + !buffer_append_wchar_t(path, ptr, real_size)) + { + return 0; + } + + ptr = (wchar_t*)buffer_data(path, size + real_size); + char* m = (char*)buffer_data(path, size); + uint16_t c = (uint16_t)real_size; + WIDE2MULTI(ptr, m, c); + + if (!c) + { + return 0; + } + + struct range directory; + + if (!path_get_directory_name(m, m + real_size, &directory)) + { + return 0; + } + + if (!buffer_resize(path, size + range_size(&directory))) + { + return 0; + } + + break; + } + } + + return 1; +#elif __linux + + while (1) + { + if (!buffer_append_wchar_t(path, NULL, FILENAME_MAX)) + { + return 0; + } + + char* ptr = (char*)buffer_data(path, size); + const ptrdiff_t size_ = buffer_size(path) - size; + ptrdiff_t real_size = readlink("/proc/self/exe", ptr, size_); + + if (-1 == real_size) + { + return 0; + } + + if (real_size < size_) + { + struct range directory; + + if (!path_get_directory_name(ptr, ptr + real_size, &directory) || + !buffer_resize(path, size + range_size(&directory))) + { + return 0; + } + + break; + } + } + + return 1; +#else + (void)size; + /*TODO:*/ + return 0; +#endif +} + +uint8_t cygpath_get_dos_path(const char* path_start, const char* path_finish, struct buffer* path) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish) || + NULL == path) + { + return 0; + } + +#if defined(_WIN32) + const ptrdiff_t size = buffer_size(path); + uint16_t count = (uint16_t)(path_finish - path_start); + + if (!buffer_append_char(path, NULL, (ptrdiff_t)count + 1) || + !buffer_append_wchar_t(path, NULL, 2 * ((ptrdiff_t)count + 1))) + { + return 0; + } + + wchar_t* long_path = (wchar_t*)buffer_data(path, size + 1 + count); + + for (uint16_t i = 0; i < count; ++i)/*TODO: Use WIDE2MULTI*/ + { + long_path[i] = path_start[i]; + } + + long_path[count] = L'\0'; + ptrdiff_t index = size + 1 + count + sizeof(wchar_t) * ((ptrdiff_t)count + 1); + wchar_t* short_path = (wchar_t*)buffer_data(path, index); + count = (uint16_t)GetShortPathNameW(long_path, short_path, count + 1); + + if (!count) + { + return 0; + } + + char* m = (char*)buffer_data(path, size); + m[count] = '\0'; + index = count; + WIDE2MULTI(short_path, m, count) + + if (!count) + { + return 0; + } + + /*NOTE: for save terminate path in buffer + 1 to the index should be added.*/ + return buffer_resize(path, size + index); +#else + return 0; +#endif +} + +uint8_t cygpath_get_unix_path(char* path_start, char* path_finish) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish)) + { + return 0; + } + + for (; path_start < path_finish; ++path_start) + { + if (windows_delimiter == (*path_start)) + { + (*path_start) = posix_delimiter; + } + } + + return 1; +} + +uint8_t cygpath_get_windows_path(char* path_start, char* path_finish) +{ + if (range_in_parts_is_null_or_empty(path_start, path_finish)) + { + return 0; + } + + for (; path_start < path_finish; ++path_start) + { + if (posix_delimiter == (*path_start)) + { + (*path_start) = windows_delimiter; + } + } + + return 1; +} + +static const char* path_function_str[] = +{ + "change-extension", "combine", "get-directory-name", + "get-extension", "get-file-name", + "get-file-name-without-extension", "get-full-path", + "get-path-root", "get-temp-file-name", + "get-temp-path", "has-extension", "is-path-rooted", + "get-dos-path", "get-unix-path", "get-windows-path" +}; + +enum path_function +{ + change_extension, combine, get_directory_name, + get_extension, get_file_name, + get_file_name_without_extension, get_full_path, + get_path_root, get_temp_file_name, + get_temp_path, has_extension, is_path_rooted, + get_dos_path, get_unix_path, get_windows_path, + UNKNOWN_PATH_FUNCTION +}; + +uint8_t path_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, path_function_str, UNKNOWN_PATH_FUNCTION); +} + +uint8_t path_get_full_path_(const void* project, const struct range* path, struct buffer* output) +{ + const ptrdiff_t size = buffer_size(output); + + if (!directory_get_current_directory(project, output)) + { + return 0; + } + + if (range_is_null_or_empty(path) || (1 == range_size(path) && '.' == *(path->start))) + { + return 1; + } + + const ptrdiff_t size_ = buffer_size(output); + + if (!buffer_append(output, NULL, size_ - size + range_size(path) + 4)) + { + return 0; + } + + struct range base_dir; + + base_dir.start = (const char*)buffer_data(output, size); + + base_dir.finish = base_dir.start + size_ - size; + + return buffer_resize(output, size) && + path_get_full_path(base_dir.start, base_dir.finish, + path->start, path->finish, output); +} + +uint8_t path_exec_function(const void* project, uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_PATH_FUNCTION <= function || + NULL == arguments || + 2 < arguments_count || + NULL == output) + { + return 0; + } + + struct range argument1; + + struct range argument2; + + argument1.start = argument2.start = argument1.finish = argument2.finish = NULL; + + switch (arguments_count) + { + case 0: + break; + + case 1: + if (!common_get_one_argument(arguments, &argument1, 0)) + { + if (get_full_path == function) + { + argument1.start = argument1.finish = NULL; + break; + } + + return 0; + } + + break; + + case 2: + if (!common_get_two_arguments(arguments, &argument1, &argument2, 0)) + { + return 0; + } + + break; + + default: + return 0; + } + + switch (function) + { + case change_extension: + return (2 == arguments_count) && + path_change_extension(argument1.start, argument1.finish, argument2.start, argument2.finish, output); + + case combine: + return (2 == arguments_count) && + path_combine(argument1.start, argument1.finish, argument2.start, argument2.finish, output); + + case get_directory_name: + return (1 == arguments_count) && + path_get_directory_name(argument1.start, argument1.finish, &argument2) && + buffer_append_data_from_range(output, &argument2); + + case get_extension: + return (1 == arguments_count) && + path_get_extension(argument1.start, argument1.finish, &argument2) && + buffer_append_data_from_range(output, &argument2); + + case get_file_name: + return (1 == arguments_count) && + path_get_file_name(argument1.start, argument1.finish, &argument2) && + buffer_append_data_from_range(output, &argument2); + + case get_file_name_without_extension: + return (1 == arguments_count) && + path_get_file_name_without_extension(argument1.start, argument1.finish, &argument2) && + buffer_append_data_from_range(output, &argument2); + + case get_full_path: + return (1 == arguments_count) && path_get_full_path_(project, &argument1, output); + + case get_path_root: + return (1 == arguments_count) && + path_get_path_root(argument1.start, argument1.finish, &argument2) && + buffer_append_data_from_range(output, &argument2); + + case get_temp_file_name: + return !arguments_count && path_get_temp_file_name(output); + + case get_temp_path: + return !arguments_count && path_get_temp_path(output); + + case has_extension: + return (1 == arguments_count) && + bool_to_string(path_has_extension(argument1.start, argument1.finish), output); + + case is_path_rooted: + return (1 == arguments_count) && + bool_to_string(path_is_path_rooted(argument1.start, argument1.finish), output); + + case UNKNOWN_PATH_FUNCTION: + default: + break; + } + + return 0; +} + +uint8_t cygpath_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_PATH_FUNCTION <= function || + NULL == arguments || + 1 != arguments_count || + NULL == output) + { + return 0; + } + + struct range argument; + + if (!common_get_one_argument(arguments, &argument, 0)) + { + return 0; + } + + const ptrdiff_t size = buffer_size(output); + + switch (function) + { + case get_dos_path: + return cygpath_get_dos_path(argument.start, argument.finish, output); + + case get_unix_path: + return buffer_append_data_from_range(output, &argument) && + cygpath_get_unix_path((char*)buffer_data(output, size), + (char*)(buffer_data(output, size)) + range_size(&argument)); + + case get_windows_path: + return buffer_append_data_from_range(output, &argument) && + cygpath_get_windows_path((char*)buffer_data(output, size), + (char*)(buffer_data(output, size)) + range_size(&argument)); + + case UNKNOWN_PATH_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/path.h b/path.h new file mode 100644 index 0000000..2b6e544 --- /dev/null +++ b/path.h @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _PATH_H_ +#define _PATH_H_ + +#include + +struct buffer; +struct range; + +char path_delimiter(); +char path_posix_delimiter(); +char path_windows_delimiter(); +uint8_t path_change_extension(const char* path_start, const char* path_finish, + const char* ext_start, const char* ext_finish, struct buffer* path); +uint8_t path_combine(const char* path1_start, const char* path1_finish, + const char* path2_start, const char* path2_finish, struct buffer* path); +uint8_t path_get_directory_name(const char* path_start, const char* path_finish, struct range* directory); +uint8_t path_get_extension(const char* path_start, const char* path_finish, struct range* ext); +uint8_t path_get_file_name(const char* path_start, const char* path_finish, struct range* file_name); +uint8_t path_get_file_name_without_extension( + const char* path_start, const char* path_finish, struct range* file_name); +uint8_t path_get_full_path(const char* root_start, const char* root_finish, + const char* path_start, const char* path_finish, struct buffer* full_path); +uint8_t path_get_path_root(const char* path_start, const char* path_finish, struct range* root); +uint8_t path_get_temp_file_name(struct buffer* temp_file_name); +uint8_t path_get_temp_path(struct buffer* temp_path); +uint8_t path_has_extension(const char* path_start, const char* path_finish); +uint8_t path_is_path_rooted(const char* path_start, const char* path_finish); + +uint8_t path_get_directory_for_current_process(struct buffer* path); +uint8_t path_get_directory_for_current_image(struct buffer* path); + +uint8_t cygpath_get_dos_path(const char* path_start, const char* path_finish, struct buffer* path); +uint8_t cygpath_get_unix_path(char* path_start, char* path_finish); +uint8_t cygpath_get_windows_path(char* path_start, char* path_finish); + +uint8_t path_get_function(const char* name_start, const char* name_finish); +uint8_t path_exec_function(const void* project, + uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t cygpath_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/project.c b/project.c new file mode 100644 index 0000000..da3799b --- /dev/null +++ b/project.c @@ -0,0 +1,666 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "project.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "file_system.h" +#include "interpreter.h" +#include "path.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" +#include "target.h" +#include "version.h" +#include "xml.h" + +#include + +#define PROJECT_NAME "project.name" +#define PROJECT_NAME_LENGTH 12 +#define PROJECT_DEFAULT "project.default" +#define PROJECT_DEFAULT_LENGTH 15 +#define PROJECT_BASE_DIR "project.basedir" +#define PROJECT_BASE_DIR_LENGTH 15 +#define PROJECT_BUILD_FILE "project.buildfile" +#define PROJECT_BUILD_FILE_LENGTH 17 + +#define PROJECT_NAME_TAG "name" +#define PROJECT_NAME_TAG_LENGTH 4 +#define PROJECT_DEFAULT_TAG "default" +#define PROJECT_DEFAULT_TAG_LENGTH 7 +#define PROJECT_BASE_DIR_TAG "basedir" +#define PROJECT_BASE_DIR_TAG_LENGTH 7 + +struct project +{ + struct buffer description; + struct buffer properties; + struct buffer targets; +}; + +uint8_t project_property_get_pointer(const void* project, + const char* property_name, uint8_t property_name_length, + void** the_property) +{ + if (NULL == project || NULL == property_name || 0 == property_name_length) + { + return 0; + } + + /*TODO: validate project pointer.*/ + const struct project* pro = (const struct project*)project; + return property_get_pointer(&pro->properties, property_name, property_name_length, the_property); +} + +uint8_t project_property_set_value(void* project, const void* target, + const char* property_name, uint8_t property_name_length, + const char* property_value, ptrdiff_t property_value_length, + uint8_t dynamic, uint8_t overwrite, + uint8_t readonly, uint8_t verbose) +{ + if (NULL == project || NULL == property_name || 0 == property_name_length) + { + return 0; + } + + struct project* pro = (struct project*)project; + + return property_set_by_name(project, target, &pro->properties, property_name, property_name_length, + property_value, property_value_length, property_value_is_char_array, + dynamic, overwrite, readonly, verbose); +} + +uint8_t project_target_exists(const void* project, const char* name, uint8_t name_length) +{ + if (NULL == project || NULL == name || 0 == name_length) + { + return 0; + } + + const struct project* pro = (const struct project*)project; + return target_exists(&pro->targets, name, name_length); +} + +uint8_t project_get_base_directory(const void* project, const void* target, struct buffer* base_directory) +{ + return property_get_by_name(project, target, PROJECT_BASE_DIR, PROJECT_BASE_DIR_LENGTH, base_directory); +} + +uint8_t project_get_buildfile_path(const void* project, const void* target, struct buffer* build_file) +{ + return property_get_by_name(project, target, PROJECT_BUILD_FILE, PROJECT_BUILD_FILE_LENGTH, build_file); +} + +uint8_t project_get_buildfile_uri(const void* project, const void* target, struct buffer* build_file_uri) +{ + if (NULL == project || NULL == build_file_uri) + { + return 0; + } + + const ptrdiff_t size = buffer_size(build_file_uri); + + if (!common_append_string_to_buffer("file:///", build_file_uri)) + { + return 0; + } + + if (!project_get_buildfile_path(project, target, build_file_uri)) + { + return 0; + } + + char* path_start = (char*)buffer_data(build_file_uri, size); + char* path_finish = (char*)(buffer_data(build_file_uri, 0) + buffer_size(build_file_uri)); + + if (path_delimiter() != path_posix_delimiter()) + { + return cygpath_get_unix_path(path_start, path_finish); + } + + return 1; +} + +uint8_t project_get_default_target(const void* project, struct buffer* default_target) +{ + return property_get_by_name(project, NULL, PROJECT_DEFAULT, PROJECT_DEFAULT_LENGTH, + default_target); +} + +uint8_t project_get_name(const void* project, struct buffer* project_name) +{ + return property_get_by_name(project, NULL, PROJECT_NAME, PROJECT_NAME_LENGTH, + project_name); +} + +static struct project gProject; + +uint8_t project_new(void** project) +{ + /*NOTE: provide multi project support on a future release.*/ + if (NULL == project || NULL != (*project)) + { + return 0; + } + + SET_NULL_TO_BUFFER(gProject.description); + SET_NULL_TO_BUFFER(gProject.properties); + SET_NULL_TO_BUFFER(gProject.targets); + (*project) = &gProject; + return 1; +} + +uint8_t project_add_properties(void* project, const void* target, const void* properties, + uint8_t verbose) +{ + if (NULL == project || NULL == properties) + { + return 0; + } + + struct project* pro = (struct project*)project; + + return property_append(project, target, &pro->properties, properties, verbose); +} + +uint8_t project_set_property_from_attribute_if_such_exists( + struct project* project, const void* target, + const char* attributes_start, const char* attributes_finish, + const char* property_name, uint8_t property_name_length, + const char* attribute_name, uint8_t attribute_name_length, + uint8_t verbose) +{ + if (NULL == project || + NULL == attributes_start || + NULL == attributes_finish || + attributes_finish < attributes_start || + NULL == property_name || + 0 == property_name_length || + NULL == attribute_name || + 0 == attribute_name_length) + { + return 0; + } + + struct range attribute_value; + + if (xml_get_attribute_value(attributes_start, attributes_finish, + attribute_name, attribute_name_length, + &attribute_value)) + { + if (!property_set_by_name( + project, target, + &project->properties, + property_name, property_name_length, + attribute_value.start, range_size(&attribute_value), + property_value_is_char_array, + 0, 1, 1, verbose)) + { + return 0; + } + } + + return 1; +} + +const char* project_is_in_element(const struct range* element) +{ + static const char* project_str = "project"; + static const uint8_t project_length = 7; + /**/ + struct range tag_name; + tag_name.start = tag_name.finish = NULL; + + if (!xml_get_tag_name(element->start, element->finish, &tag_name) || + !string_equal(tag_name.start, tag_name.finish, project_str, project_str + project_length)) + { + return NULL; + } + + return tag_name.finish; +} + +uint8_t project_set_base_directory(struct project* project, void* tatget, + const char* attributes_start, + const char* attributes_finish, uint8_t verbose) +{ + return project_set_property_from_attribute_if_such_exists( + project, tatget, + attributes_start, attributes_finish, + PROJECT_BASE_DIR, PROJECT_BASE_DIR_LENGTH, + PROJECT_BASE_DIR_TAG, PROJECT_BASE_DIR_TAG_LENGTH, verbose); +} + +uint8_t project_set_buildfile_path(void* project, const void* target, + const char* build_file, ptrdiff_t build_file_length, uint8_t verbose) +{ + return project_property_set_value(project, target, PROJECT_BUILD_FILE, PROJECT_BUILD_FILE_LENGTH, + build_file, build_file_length, 0, 1, 1, verbose); +} + +uint8_t project_set_default_target(struct project* project, void* tatget, + const char* attributes_start, + const char* attributes_finish, uint8_t verbose) +{ + return project_set_property_from_attribute_if_such_exists( + project, tatget, + attributes_start, attributes_finish, + PROJECT_DEFAULT, PROJECT_DEFAULT_LENGTH, + PROJECT_DEFAULT_TAG, PROJECT_DEFAULT_TAG_LENGTH, verbose); +} + +uint8_t project_set_name(struct project* project, void* tatget, + const char* attributes_start, + const char* attributes_finish, uint8_t verbose) +{ + return project_set_property_from_attribute_if_such_exists( + project, tatget, + attributes_start, attributes_finish, + PROJECT_NAME, PROJECT_NAME_LENGTH, + PROJECT_NAME_TAG, PROJECT_NAME_TAG_LENGTH, verbose); +} + +uint8_t project_load_properties_and_targets(struct project* project, struct buffer* elements, uint8_t verbose) +{ + static const char* description_str = "description"; + static const char* property_str = "property"; + static const char* target_str = "target"; + /**/ + static const uint8_t description_length = 11; + static const uint8_t property_length = 8; + static const uint8_t target_length = 6; + /**/ + ptrdiff_t i = 0; + struct range* element = NULL; + + while (NULL != (element = buffer_range_data(elements, i++))) + { + struct range tag_name_or_content; + + if (!xml_get_tag_name(element->start, element->finish, &tag_name_or_content)) + { + i = 0; + break; + } + + if (string_equal(tag_name_or_content.start, tag_name_or_content.finish, + description_str, description_str + description_length)) + { + if (xml_get_element_value(element, &tag_name_or_content) && + !buffer_append_char(&project->description, + tag_name_or_content.start, + range_size(&tag_name_or_content))) + { + i = 0; + break; + } + + continue; + } + + uint8_t skip = 0; + + if (!interpreter_xml_tag_should_be_skip_by_if_or_unless(project, NULL, tag_name_or_content.finish, + element->finish, &skip)) + { + i = 0; + break; + } + + /*TODO: name of target to skip may be used for depends.*/ + if (skip) + { + continue; + } + + if (string_equal(tag_name_or_content.start, tag_name_or_content.finish, + property_str, property_str + property_length)) + { + if (!property_set_from_xml_tag_record(project, NULL, + &project->properties, + tag_name_or_content.finish, element->finish, verbose)) + { + i = 0; + break; + } + + continue; + } + else if (string_equal(tag_name_or_content.start, tag_name_or_content.finish, target_str, + target_str + target_length)) + { + if (!target_add_from_xml_tag_record(&project->targets, + tag_name_or_content.finish, element->finish)) + { + i = 0; + break; + } + + continue; + } + + if (!interpreter_evaluate_task(project, NULL, + interpreter_get_task(tag_name_or_content.start, tag_name_or_content.finish), + tag_name_or_content.finish, + element->finish)) + { + i = 0; + break; + } + } + + return 0 < i; +} + +uint8_t project_load_from_content(const char* content_start, const char* content_finish, + const char* base_dir, ptrdiff_t base_dir_length, + void* project, uint8_t verbose) +{ + if (range_in_parts_is_null_or_empty(content_start, content_finish) || + NULL == project) + { + return 0; + } + + struct buffer elements; + + SET_NULL_TO_BUFFER(elements); + + if (1 != xml_get_sub_nodes_elements(content_start, content_finish, &elements)) + { + buffer_release(&elements); + return 0; + } + + struct range* element = NULL; + + if (NULL == (element = buffer_range_data(&elements, 0))) + { + buffer_release(&elements); + return 0; + } + + const char* tag_name_finish = NULL; + + if (NULL == (tag_name_finish = project_is_in_element(element))) + { + buffer_release(&elements); + return 0; + } + + struct project* pro = (struct project*)project; + + const char* element_finish = element->finish; + + const char* attributes_finish = xml_get_tag_finish_pos(tag_name_finish, element_finish); + + if (!project_set_name(pro, NULL, tag_name_finish, attributes_finish, verbose)) + { + buffer_release(&elements); + return 0; + } + + if (!property_exists(&pro->properties, PROJECT_DEFAULT, PROJECT_DEFAULT_LENGTH)) + { + if (!project_set_default_target(pro, NULL, tag_name_finish, attributes_finish, verbose)) + { + buffer_release(&elements); + return 0; + } + } + + /*NOTE: path in tag may be not full - '.' for example.*/ + if (!project_set_base_directory(pro, NULL, tag_name_finish, attributes_finish, verbose)) + { + buffer_release(&elements); + return 0; + } + + if (!property_exists(&pro->properties, PROJECT_BASE_DIR, PROJECT_BASE_DIR_LENGTH)) + { + if (!project_property_set_value(project, NULL, + PROJECT_BASE_DIR, PROJECT_BASE_DIR_LENGTH, + base_dir, base_dir_length, + 0, 1, 1, verbose)) + { + buffer_release(&elements); + return 0; + } + } + + if (!buffer_resize(&elements, 0)) + { + buffer_release(&elements); + return 0; + } + + if (0 < xml_get_sub_nodes_elements(tag_name_finish, element_finish, &elements) && + !project_load_properties_and_targets(pro, &elements, verbose)) + { + buffer_release(&elements); + return 0; + } + + buffer_release(&elements); + return 1; +} + +uint8_t project_load_from_build_file(const char* path_to_build_file, void* project, uint8_t verbose) +{ + ptrdiff_t path_length = 0; + + if (NULL == path_to_build_file || + NULL == project || + 0 == (path_length = strlen(path_to_build_file))) + { + return 0; + } + + struct buffer content; + + SET_NULL_TO_BUFFER(content); + + if (!path_get_directory_for_current_process(&content)) + { + buffer_release(&content); + return 0; + } + + ptrdiff_t addition_path_length = buffer_size(&content); + + if (!buffer_append(&content, NULL, addition_path_length + path_length + 2) || + !buffer_resize(&content, addition_path_length)) + { + buffer_release(&content); + return 0; + } + + if (!path_get_full_path(buffer_char_data(&content, 0), buffer_char_data(&content, 0) + addition_path_length, + path_to_build_file, path_to_build_file + path_length, &content)) + { + buffer_release(&content); + return 0; + } + + path_length = buffer_size(&content) - addition_path_length; + /**/ + char* dst = buffer_char_data(&content, 0); + char* src = buffer_char_data(&content, addition_path_length); + + for (addition_path_length = 0; addition_path_length < path_length; ++addition_path_length) + { + (*dst) = (*src); + ++dst; + ++src; + } + + if (!buffer_resize(&content, path_length) || + !buffer_push_back(&content, '\0')) + { + buffer_release(&content); + return 0; + } + + if (!file_exists(buffer_char_data(&content, 0))) + { + buffer_release(&content); + return 0; + } + + path_length = buffer_size(&content); + + if (!project_set_buildfile_path(project, NULL, + buffer_char_data(&content, 0), + path_length, verbose)) + { + buffer_release(&content); + return 0; + } + + if (!read_file(buffer_char_data(&content, 0), &content)) + { + buffer_release(&content); + return 0; + } + + /*TODO:if (!project_set_default_target(project, NULL, "default=", "", verbose)) + { + buffer_release(&content); + return 0; + }*/ + struct range directory; + + if (!path_get_directory_name(buffer_char_data(&content, 0), + buffer_char_data(&content, 0) + path_length, &directory)) + { + buffer_release(&content); + return 0; + } + + if (!project_load_from_content(buffer_char_data(&content, 0) + path_length, + buffer_char_data(&content, 0) + buffer_size(&content), + directory.start, range_size(&directory), project, verbose)) + { + buffer_release(&content); + return 0; + } + + buffer_release(&content); + return 1; +} + +void project_unload(void* project) +{ + if (NULL == project || &gProject != project) + { + return; + } + + struct project* pro = (struct project*)project; + + buffer_release(&pro->description); + + property_clear(&pro->properties); + + target_clear(&pro->targets); +} + +static const char* project_function_str[] = +{ + "get-base-directory", "get-buildfile-path", + "get-buildfile-uri", "get-default-target", + "get-name", "version", "current-directory" +}; + +enum project_function +{ + get_base_directory, get_buildfile_path, + get_buildfile_uri, get_default_target, + get_name, version_, current_directory, + UNKNOWN_PROJECT_FUNCTION +}; + +uint8_t project_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, project_function_str, UNKNOWN_PROJECT_FUNCTION); +} + +uint8_t project_exec_function(const void* project, const void* target, + uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_PROJECT_FUNCTION <= function || + NULL == arguments || + arguments_count || + NULL == output) + { + return 0; + } + + switch (function) + { + case get_base_directory: + return project_get_base_directory(project, target, output); + + case get_buildfile_path: + return project_get_buildfile_path(project, target, output); + + case get_buildfile_uri: + return project_get_buildfile_uri(project, target, output); + + case get_default_target: + return project_get_default_target(project, output); + + case get_name: + return project_get_name(project, output); + + case UNKNOWN_PROJECT_FUNCTION: + default: + break; + } + + return 0; +} + +uint8_t program_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_PROJECT_FUNCTION <= function || + NULL == arguments || + arguments_count || + NULL == output) + { + return 0; + } + + switch (function) + { + case version_: + { + static const char* program_version = PROGRAM_VERSION; + struct Version version; + + if (!version_parse(program_version, program_version + PROGRAM_VERSION_LENGTH, &version)) + { + break; + } + + return version_to_string(&version, output); + } + + case current_directory: + return path_get_directory_for_current_process(output); + + case UNKNOWN_PROJECT_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/project.h b/project.h new file mode 100644 index 0000000..f61648d --- /dev/null +++ b/project.h @@ -0,0 +1,49 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _PROJECT_H_ +#define _PROJECT_H_ + +#include +#include + +struct buffer; + +uint8_t project_property_get_pointer(const void* project, + const char* property_name, uint8_t property_name_length, + void** the_property); +uint8_t project_property_set_value(void* project, const void* target, + const char* property_name, uint8_t property_name_length, + const char* property_value, ptrdiff_t property_value_length, + uint8_t dynamic, uint8_t overwrite, + uint8_t readonly, uint8_t verbose); + +uint8_t project_target_exists(const void* project, const char* name, uint8_t name_length); + +uint8_t project_get_base_directory(const void* project, const void* target, struct buffer* base_directory); +uint8_t project_get_buildfile_path(const void* project, const void* target, struct buffer* build_file); +uint8_t project_get_buildfile_uri(const void* project, const void* target, struct buffer* build_file_uri); +uint8_t project_get_default_target(const void* project, struct buffer* default_target); +uint8_t project_get_name(const void* project, struct buffer* project_name); + +uint8_t project_new(void** project); +uint8_t project_add_properties(void* project, const void* target, + const void* properties, uint8_t verbose); +uint8_t project_load_from_content(const char* content_start, const char* content_finish, + const char* base_dir, ptrdiff_t base_dir_length, + void* project, uint8_t verbose); +uint8_t project_load_from_build_file(const char* path_to_build_file, void* project, uint8_t verbose); +void project_unload(void* project); + +uint8_t project_get_function(const char* name_start, const char* name_finish); +uint8_t project_exec_function(const void* project, const void* target, + uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); +uint8_t program_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/property.c b/property.c new file mode 100644 index 0000000..da12ff5 --- /dev/null +++ b/property.c @@ -0,0 +1,600 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "property.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "interpreter.h" +#include "project.h" +#include "range.h" +#include "target.h" +#include "xml.h" + +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +struct property +{ + char name[INT8_MAX + 1]; + uint8_t name_length; + struct buffer value; + uint8_t dynamic; + uint8_t readonly; +}; + +uint8_t buffer_append_property(struct buffer* properties, const struct property* data, ptrdiff_t data_count) +{ + return buffer_append(properties, (const uint8_t*)data, sizeof(struct property) * data_count); +} + +struct property* buffer_property_data(const struct buffer* properties, ptrdiff_t data_position) +{ + return (struct property*)buffer_data(properties, sizeof(struct property) * data_position); +} + +uint8_t property_is_name_valid(const char* name, uint8_t name_length) +{ + if (NULL == name || 0 == name_length || INT8_MAX < name_length) + { + return 0; + } + + for (uint8_t i = 0; i < name_length; ++i) + { + const uint8_t is_letter = ('A' <= name[i] && name[i] <= 'Z') || ('a' <= name[i] && name[i] <= 'z'); + const uint8_t is_digit = ('0' <= name[i] && name[i] <= '9'); + const uint8_t is_underscore = ('_' == name[i]); + const uint8_t is_dash = ('-' == name[i]); + const uint8_t is_dot = ('.' == name[i]); + + if (!is_letter && !is_digit && !is_underscore && !is_dash && !is_dot) + { + return 0; + } + + if ((0 == i || name_length - 1 == i) && (is_dash || is_dot)) + { + return 0; + } + } + + return 1; +} + +uint8_t property_new(const void* project, const void* target, + struct buffer* properties, + const char* property_name, + uint8_t property_name_length, + const void* value, ptrdiff_t value_length, + enum data_type type_of_value, + uint8_t dynamic, uint8_t readonly, uint8_t verbose) +{ + if (NULL == properties || + !property_is_name_valid(property_name, property_name_length)) + { + return 0; + } + + struct property the_property; + +#if __STDC_SEC_API__ + if (0 != memcpy_s(the_property.name, INT8_MAX, property_name, property_name_length)) + { + return 0; + } + +#else + memcpy(the_property.name, property_name, property_name_length); +#endif + the_property.name[property_name_length] = '\0'; + the_property.name_length = property_name_length; + SET_NULL_TO_BUFFER(the_property.value); + the_property.dynamic = the_property.readonly = 0; + + if (!property_set_by_pointer(project, target, &the_property, value, value_length, type_of_value, dynamic, + readonly, verbose)) + { + buffer_release(&the_property.value); + return 0; + } + + return buffer_append_property(properties, &the_property, 1); +} + +uint8_t property_get_pointer(const struct buffer* properties, + const char* property_name, uint8_t property_name_length, + void** the_property) +{ + ptrdiff_t i = 0; + struct property* prop = NULL; + + if (NULL == properties || + !property_is_name_valid(property_name, property_name_length)) + { + return 0; + } + + while (NULL != (prop = buffer_property_data(properties, i++))) + { + if (property_name_length == prop->name_length && + 0 == memcmp(&prop->name, property_name, property_name_length)) + { + if (the_property) + { + (*the_property) = prop; + } + + return 1; + } + } + + return 0; +} + +uint8_t property_exists(const struct buffer* properties, const char* name, uint8_t name_length) +{ + if (NULL == properties || + NULL == name || + 0 == name_length) + { + return 0; + } + + return property_get_pointer(properties, name, name_length, NULL); +} + +uint8_t property_get_by_name(const void* project, const void* target, + const char* property_name, + uint8_t property_name_length, + struct buffer* output) +{ + if (NULL == output) + { + return 0; + } + + void* the_property = NULL; + + if (!project_property_get_pointer(project, property_name, property_name_length, &the_property)) + { + return 0; + } + + return property_get_by_pointer(project, target, the_property, output); +} + +uint8_t property_get_by_pointer(const void* project, const void* target, + const void* the_property, struct buffer* output) +{ + if (NULL == the_property || + NULL == output) + { + return 0; + } + + const struct property* prop = (const struct property*)the_property; + + /*TODO: struct property* prop = NULL; + + while (NULL != (prop = buffer_property_data(properties, i++))) + { + if (the_property == prop) + { + break; + } + }*/ + if (!buffer_size(&prop->value)) + { + return 1; + } + + if (prop->dynamic) + { + struct range code; + code.start = buffer_char_data(&prop->value, 0); + code.finish = code.start + buffer_size(&prop->value); + + if (!interpreter_evaluate_code(project, target, &code, output)) + { + return 0; + } + } + else + { + if (!buffer_append_data_from_buffer(output, &prop->value)) + { + return 0; + } + } + + return 1; +} + +uint8_t property_is_dynamic(const void* the_property, uint8_t* is_dynamic) +{ + if (NULL == the_property || + NULL == is_dynamic) + { + return 0; + } + + const struct property* prop = (const struct property*)the_property; + *is_dynamic = prop->dynamic; + return 1; +} + +uint8_t property_is_readonly(const void* the_property, uint8_t* is_readonly) +{ + if (NULL == the_property || + NULL == is_readonly) + { + return 0; + } + + const struct property* prop = (const struct property*)the_property; + *is_readonly = prop->readonly; + return 1; +} + +uint8_t property_set_by_name(const void* project, const void* target, + struct buffer* properties, + const char* name, uint8_t name_length, + const char* value, ptrdiff_t value_length, + enum data_type type_of_value, + uint8_t dynamic, uint8_t overwrite, + uint8_t readonly, uint8_t verbose) +{ + struct property* prop = NULL; + + if (NULL == properties || + !property_is_name_valid(name, name_length) || + NULL == value || value_length < 0) + { + return 0; + } + + if (property_get_pointer(properties, name, name_length, (void**)&prop)) + { + if (!overwrite) + { + return 1; + } + + return property_set_by_pointer(project, target, prop, + value, value_length, type_of_value, + dynamic, readonly, verbose); + } + + return property_new(project, target, properties, name, name_length, + value, value_length, type_of_value, + dynamic, readonly, verbose); +} + +uint8_t property_set_by_pointer(const void* project, const void* target, + void* the_property, + const void* value, ptrdiff_t value_length, + enum data_type type_of_value, + uint8_t dynamic, uint8_t readonly, uint8_t verbose) +{ + if (NULL == the_property || + NULL == value || + (0 < value_length && + property_value_is_char_array != type_of_value && + 0 < dynamic)) + { + return 0; + } + + (void)verbose;/*TODO: */ + struct property* prop = (struct property*)the_property; + + if (prop->readonly) + { + return 0; + } + + if (property_value_is_char_array != type_of_value && + !buffer_resize(&prop->value, 0)) + { + return 0; + } + + if (0 < value_length) + { + switch (type_of_value) + { + case property_value_is_char_array: + if (dynamic) + { + if (!buffer_resize(&prop->value, 0) || + !buffer_append_char(&prop->value, value, value_length)) + { + return 0; + } + } + else if (value) + { + struct range code; + code.start = value; + code.finish = code.start + value_length; + value_length = buffer_size(&prop->value); + + if (!interpreter_evaluate_code(project, target, &code, &prop->value)) + { + return 0; + } + + if (!value_length) + { + break; + } + + char* dst = buffer_char_data(&prop->value, 0); + char* src = buffer_char_data(&prop->value, value_length); + value_length = buffer_size(&prop->value) - value_length; + + for (ptrdiff_t i = 0; i < value_length; ++i) + { + (*dst) = (*src); + ++dst; + ++src; + } + +#if 0 +#if __STDC_SEC_API__ + + if (0 != memcpy_s(buffer_data(&prop->value, 0), + buffer_size(&prop->value) - value_length, + buffer_data(&prop->value, value_length), + buffer_size(&prop->value) - value_length)) + { + return 0; + } + +#else + memcpy(buffer_data(&prop->value, 0), + buffer_data(&prop->value, value_length), + buffer_size(&prop->value) - value_length); +#endif + /*NOTE: buffer_size(&prop->value) - value_length)*/ +#endif + + if (!buffer_resize(&prop->value, value_length)) + { + return 0; + } + } + + break; + + case property_value_is_integer: + if (!int64_to_string(*((int64_t*)value), &prop->value)) + { + return 0; + } + + break; + + case property_value_is_double: + if (!double_to_string(*((double*)value), &prop->value)) + { + return 0; + } + + break; + + default: + return 0; + } + } + + prop->dynamic = 0 < dynamic; + prop->readonly = 0 < readonly; + return 1; +} + +uint8_t property_set_from_xml_tag_record( + const void* project, const void* target, + struct buffer* properties, + const char* record_start, const char* record_finish, + uint8_t verbose) +{ + if (NULL == properties || + NULL == record_start || NULL == record_finish || + record_finish <= record_start) + { + return 0; + } + + static const char* name_str = "name"; + static const uint8_t name_length = 4; + static const char* value_str = "value"; + static const uint8_t value_length = 5; + /**/ + static const char* property_attributes[] = { "dynamic", "overwrite", "readonly", "failonerror", "verbose" }; + static const uint8_t property_attributes_lengths[] = { 7, 9, 8, 11, 7 }; + /**/ + struct range property_name; + struct range property_value; + + if (!xml_get_attribute_value(record_start, record_finish, name_str, name_length, &property_name)) + { + return 0; + } + + if (!xml_get_attribute_value(record_start, record_finish, value_str, value_length, &property_value)) + { + return 0; + } + + uint8_t dynamic = 0; + uint8_t overwrite = 1; + uint8_t readonly = 0; + uint8_t fail_on_error = 1; + uint8_t verbose_local = 0; + /**/ + uint8_t* value_pointers[5]; + value_pointers[0] = &dynamic; + value_pointers[1] = &overwrite; + value_pointers[2] = &readonly; + value_pointers[3] = &fail_on_error; + value_pointers[4] = &verbose_local; + + for (uint8_t i = 0; i < 5; ++i) + { + struct range attribute_value; + + if (!xml_get_attribute_value(record_start, record_finish, property_attributes[i], + property_attributes_lengths[i], &attribute_value)) + { + continue; + } + + if (!bool_parse(attribute_value.start, attribute_value.finish, value_pointers[i])) + { + return 0; + } + } + + if (!verbose && verbose_local) + { + verbose = verbose_local; + } + + return property_set_by_name(project, target, properties, + property_name.start, (uint8_t)(range_size(&property_name)), + property_value.start, range_size(&property_value), + property_value_is_char_array, + dynamic, overwrite, readonly, verbose); + /*TODO: explain fail_on_error factor, if verbose set, and return true. + return fail_on_error ? returned : 1;*/ +} + +uint8_t property_append(const void* project, const void* target, + struct buffer* target_properties, + const struct buffer* properties, uint8_t verbose) +{ + if (NULL == target_properties || NULL == properties) + { + return 0; + } + + ptrdiff_t i = 0; + struct property* prop = NULL; + + while (NULL != (prop = buffer_property_data(properties, i++))) + { + const void* value = buffer_size(&prop->value) ? buffer_data(&prop->value, 0) : (void*)prop; + + if (!property_new(project, target, target_properties, prop->name, prop->name_length, + value, buffer_size(&prop->value), + property_value_is_char_array, prop->dynamic, prop->readonly, verbose)) + { + return 0; + } + } + + return 1; +} + +void property_clear(struct buffer* properties) +{ + ptrdiff_t i = 0; + struct property* prop = NULL; + + if (NULL == properties) + { + return; + } + + while (NULL != (prop = buffer_property_data(properties, i++))) + { + buffer_release(&prop->value); + } + + buffer_release(properties); +} + +static const char* property_str[] = +{ + "exists", "get-value", + "is-dynamic", "is-readonly" +}; + +enum property_function +{ + property_exists_, property_get_value_, + property_is_dynamic_, property_is_readonly_, + UNKNOWN_PROPERTY +}; + +uint8_t property_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, property_str, UNKNOWN_PROPERTY); +} + +uint8_t property_exec_function(const void* project, const void* target, + uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, struct buffer* output) +{ + if (UNKNOWN_PROPERTY <= function || + NULL == arguments || + 1 != arguments_count || + NULL == output) + { + return 0; + } + + struct range argument; + + if (!common_get_one_argument(arguments, &argument, 0)) + { + return 0; + } + + const ptrdiff_t length = range_size(&argument); + + if (length < 1 || INT8_MAX < length) + { + return 0; + } + + struct property* prop = NULL; + + const uint8_t is_exists = project_property_get_pointer(project, argument.start, (uint8_t)length, + (void**)&prop); + + if (function != property_exists_ && !is_exists) + { + return 0; + } + + switch (function) + { + case property_exists_: + return bool_to_string(is_exists, output); + + case property_get_value_: + return is_exists && property_get_by_pointer(project, target, prop, output); + + case property_is_dynamic_: + return NULL != prop && bool_to_string(prop->dynamic, output); + + case property_is_readonly_: + return NULL != prop && bool_to_string(prop->readonly, output); + + case UNKNOWN_PROPERTY: + default: + break; + } + + return 0; +} diff --git a/property.h b/property.h new file mode 100644 index 0000000..490f620 --- /dev/null +++ b/property.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _PROPERTY_H_ +#define _PROPERTY_H_ + +#include +#include + +struct buffer; + +enum data_type { property_value_is_char_array, property_value_is_integer, property_value_is_double }; + +uint8_t property_get_pointer(const struct buffer* properties, + const char* property_name, uint8_t property_name_length, + void** the_property); + +uint8_t property_exists(const struct buffer* properties, const char* name, uint8_t name_length); +uint8_t property_get_by_name(const void* project, const void* target, + const char* property_name, + uint8_t property_name_length, + struct buffer* output); +uint8_t property_get_by_pointer(const void* project, const void* target, + const void* the_property, + struct buffer* output); +uint8_t property_is_dynamic(const void* the_property, uint8_t* is_dynamic); +uint8_t property_is_readonly(const void* the_property, uint8_t* is_readonly); + +uint8_t property_set_by_name(const void* project, const void* target, + struct buffer* properties, + const char* name, uint8_t name_length, + const char* value, ptrdiff_t value_length, + enum data_type type_of_value, + uint8_t dynamic, uint8_t overwrite, + uint8_t readonly, uint8_t verbose); +uint8_t property_set_by_pointer(const void* project, const void* target, + void* the_property, + const void* value, ptrdiff_t value_length, + enum data_type type_of_value, + uint8_t dynamic, + uint8_t readonly, uint8_t verbose); +uint8_t property_set_from_xml_tag_record( + const void* project, const void* target, + struct buffer* properties, + const char* record_start, const char* record_finish, + uint8_t verbose); +uint8_t property_append(const void* project, const void* target, + struct buffer* target_properties, + const struct buffer* properties, uint8_t verbose); +void property_clear(struct buffer* properties); + +uint8_t property_get_function(const char* name_start, const char* name_finish); +uint8_t property_exec_function(const void* project, const void* target, + uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, struct buffer* output); + +#endif diff --git a/range.c b/range.c new file mode 100644 index 0000000..d4b03b8 --- /dev/null +++ b/range.c @@ -0,0 +1,44 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "range.h" +#include "buffer.h" + +ptrdiff_t range_size(const struct range* range) +{ + return range_is_null_or_empty(range) ? 0 : range->finish - range->start; +} + +uint8_t range_is_null_or_empty(const struct range* range) +{ + return NULL == range || + range_in_parts_is_null_or_empty(range->start, range->finish); +} + +uint8_t range_in_parts_is_null_or_empty(const char* range_start, const char* range_finish) +{ + return NULL == range_start || + NULL == range_finish || + range_finish <= range_start; +} + +uint8_t buffer_append_data_from_range(struct buffer* storage, const struct range* data) +{ + return NULL != data && NULL != data->start && NULL != data->finish && + data->start <= data->finish && + buffer_append_char(storage, data->start, range_size(data)); +} + +uint8_t buffer_append_range(struct buffer* ranges, const struct range* data, ptrdiff_t data_count) +{ + return buffer_append(ranges, (const uint8_t*)data, sizeof(struct range) * data_count); +} + +struct range* buffer_range_data(const struct buffer* ranges, ptrdiff_t data_position) +{ + return (struct range*)buffer_data(ranges, sizeof(struct range) * data_position); +} diff --git a/range.h b/range.h new file mode 100644 index 0000000..e878946 --- /dev/null +++ b/range.h @@ -0,0 +1,31 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _RANGE_H_ +#define _RANGE_H_ + +#include +#include + +struct buffer; + +struct range +{ + const char* start; + const char* finish; +}; + +ptrdiff_t range_size(const struct range* range); +uint8_t range_is_null_or_empty(const struct range* range); +uint8_t range_in_parts_is_null_or_empty(const char* range_start, const char* range_finish); + +uint8_t buffer_append_data_from_range(struct buffer* storage, const struct range* data); + +uint8_t buffer_append_range(struct buffer* ranges, const struct range* data, ptrdiff_t data_count); +struct range* buffer_range_data(const struct buffer* ranges, ptrdiff_t data_position); + +#endif diff --git a/string_unit.c b/string_unit.c new file mode 100644 index 0000000..60bad00 --- /dev/null +++ b/string_unit.c @@ -0,0 +1,825 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "string_unit.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "range.h" + +#include +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +ptrdiff_t string_index_of_any(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish, int8_t step) +{ + if (NULL == input_start || NULL == input_finish || + input_finish < input_start || + NULL == value_start || NULL == value_finish || + value_finish < value_start || + (-1 != step && 1 != step)) + { + return -1; + } + + const ptrdiff_t input_length = input_finish - input_start; + const ptrdiff_t value_length = value_finish - value_start; + + if (input_length < value_length) + { + return -1; + } + + if (input_length == 0 && input_length == value_length) + { + return 0; + } + + if (input_length == value_length) + { + if (input_start == value_start) + { + return 0; + } + + return (0 == (memcmp(input_start, value_start, input_length)) ? 0 : -1); + } + + if (0 < step) + { + for (ptrdiff_t i = 0, count = 1 + input_length - value_length; i < count; ++i) + { + if (0 == memcmp(&input_start[i], value_start, value_length)) + { + return i; + } + } + } + else + { + for (ptrdiff_t i = input_length - value_length; i > -1; --i) + { + if (0 == memcmp(&input_start[i], value_start, value_length)) + { + return i; + } + } + } + + return -1; +} + +uint8_t string_contains(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish) +{ + return -1 != string_index_of_any(input_start, input_finish, value_start, value_finish, 1); +} + +uint8_t string_ends_with(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish) +{ + ptrdiff_t index = 0; + + if (-1 == (index = string_index_of_any(input_start, input_finish, value_start, value_finish, 1))) + { + return 0; + } + + const ptrdiff_t expected_index = (input_finish - input_start) - (value_finish - value_start); + return expected_index == index; +} + +ptrdiff_t string_get_length(const char* input_start, const char* input_finish) +{ + if (input_start == input_finish) + { + return 0; + } + + if (NULL == input_start || NULL == input_finish || input_finish < input_start) + { + return -1; + } + + return input_finish - input_start; +} + +ptrdiff_t string_index_of(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish) +{ + return string_index_of_any(input_start, input_finish, value_start, value_finish, 1); +} + +ptrdiff_t string_last_index_of(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish) +{ + return string_index_of_any(input_start, input_finish, value_start, value_finish, -1); +} +/*TODO:string_pad_left +string_pad_right*/ +ptrdiff_t string_get_index_of_first_non_equal_symbol(const char* input_a, ptrdiff_t a_length, + const char* input_b, ptrdiff_t b_length) +{ + if (NULL == input_a || + NULL == input_b || + a_length < 1 || + b_length < 1) + { + return 0; + } + + ptrdiff_t i = 0; + + for (; i < a_length && i < b_length; ++i) + { + if (input_a[i] != input_b[i]) + { + break; + } + } + + return i; +} +#if 0 +uint8_t string_replace_in_buffer(struct buffer* input_output, + const char* to_be_replaced, ptrdiff_t to_be_replaced_length, + const char* by_replacement, ptrdiff_t by_replacement_length) +{ + if (NULL == input_output || + NULL == to_be_replaced || + to_be_replaced_length < 1 || + (0 < by_replacement_length && NULL == by_replacement) || + (NULL != by_replacement && by_replacement_length < 0)) + { + return 0; + } + + ptrdiff_t size = buffer_size(input_output); + + if (!size) + { + return 1; + } + else if (to_be_replaced_length < by_replacement_length) + { + const ptrdiff_t delta = by_replacement_length - to_be_replaced_length; + const ptrdiff_t offset = string_get_index_of_first_non_equal_symbol( + to_be_replaced, to_be_replaced_length, + by_replacement, by_replacement_length); + /**/ + ptrdiff_t index = 0; + ptrdiff_t previous_index = -1; + char* start = buffer_char_data(input_output, 0); + char* finish = 1 + buffer_char_data(input_output, size - 1); + + while (previous_index < (index += string_index_of_any(start + index, finish, + to_be_replaced, to_be_replaced + to_be_replaced_length, 1))) + { + if (!buffer_append_char(input_output, NULL, delta)) + { + return 0; + } + + size = buffer_size(input_output); + start = buffer_char_data(input_output, 0); + finish = 1 + buffer_char_data(input_output, size - 1); +#if 0 +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&start[index + by_replacement_length], size - (index + by_replacement_length), + &start[index + to_be_replaced_length], size - delta - (index + to_be_replaced_length))) + { + return 0; + } + +#else + memcpy(&start[index + by_replacement_length], &start[index + to_be_replaced_length], + size - delta - (index + to_be_replaced_length)); +#endif +#else + char* dst = finish - 1; + const char* src = dst - delta; + + for (; start + index + to_be_replaced_length - 1 < src; --src, --dst) + { + dst[0] = src[0]; + } + +#endif +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&start[index + offset], to_be_replaced_length + delta - offset, + by_replacement + offset, by_replacement_length - offset)) + { + return 0; + } + +#else + memcpy(&start[index + offset], by_replacement + offset, by_replacement_length - offset); +#endif + index += by_replacement_length; + previous_index = index - 1; + } + } + else if (1 == to_be_replaced_length && + to_be_replaced_length == by_replacement_length) + { + if (to_be_replaced[0] == by_replacement[0]) + { + return 1; + } + + char* ptr = buffer_char_data(input_output, 0); + + for (ptrdiff_t i = 0; i < size; ++i) + { + if (to_be_replaced[0] == ptr[i]) + { + ptr[i] = by_replacement[0]; + } + } + } + else if (to_be_replaced_length == by_replacement_length) + { + if (string_equal(to_be_replaced, to_be_replaced + to_be_replaced_length, + by_replacement, by_replacement + by_replacement_length)) + { + return 1; + } + + const ptrdiff_t offset = string_get_index_of_first_non_equal_symbol( + to_be_replaced, to_be_replaced_length, + by_replacement, by_replacement_length); + ptrdiff_t index = 0; + ptrdiff_t previous_index = -1; + char* start = buffer_char_data(input_output, 0); + char* finish = 1 + buffer_char_data(input_output, size - 1); + + while (previous_index < (index += string_index_of_any(start + index, finish, + to_be_replaced, to_be_replaced + to_be_replaced_length, 1))) + { +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&start[index + offset], to_be_replaced_length - offset, + by_replacement + offset, by_replacement_length - offset)) + { + return 0; + } + +#else + memcpy(&start[index + offset], by_replacement + offset, by_replacement_length - offset); +#endif + index += to_be_replaced_length; + previous_index = index - 1; + } + + start += to_be_replaced_length; + } + else /*if (to_be_replaced_length > by_replacement_length)*/ + { + const ptrdiff_t delta = to_be_replaced_length - by_replacement_length; + const ptrdiff_t offset = string_get_index_of_first_non_equal_symbol( + to_be_replaced, to_be_replaced_length, + by_replacement, by_replacement_length); + ptrdiff_t index = 0; + ptrdiff_t previous_index = -1; + char* start = buffer_char_data(input_output, 0); + char* finish = 1 + buffer_char_data(input_output, size - 1); + + while (previous_index < (index += string_index_of_any(start + index, finish, + to_be_replaced, to_be_replaced + to_be_replaced_length, 1))) + { +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&start[index + offset], to_be_replaced_length - offset, + by_replacement, by_replacement_length - offset)) + { + return 0; + } + +#else + memcpy(&start[index + offset], by_replacement, by_replacement_length - offset); +#endif +#if __STDC_SEC_API__ + + if (0 != memcpy_s(&start[index + by_replacement_length], size - (index + by_replacement_length), + &start[index + to_be_replaced_length], size - (index + to_be_replaced_length))) + { + return 0; + } + +#else + memcpy(&start[index + by_replacement_length], &start[index + to_be_replaced_length], + size - (index + to_be_replaced_length)); +#endif + + if (!buffer_resize(input_output, size - delta)) + { + return 0; + } + + size = buffer_size(input_output); + start = buffer_char_data(input_output, 0); + finish = 1 + buffer_char_data(input_output, size - 1); + /**/ + index += to_be_replaced_length; + previous_index = index - 1; + } + } + + return 1; +} +#endif +uint8_t string_replace(const char* input_start, const char* input_finish, + const char* to_be_replaced_start, const char* to_be_replaced_finish, + const char* by_replacement_start, const char* by_replacement_finish, + struct buffer* output) +{ + if (NULL == input_start || NULL == input_finish || + NULL == to_be_replaced_start || NULL == to_be_replaced_finish || + NULL == output || input_finish <= input_start || + to_be_replaced_finish <= to_be_replaced_start) + { + return 0; + } + + const ptrdiff_t input_length = input_finish - input_start; + const ptrdiff_t size = buffer_size(output); + + if (!buffer_append_char(output, input_start, input_length)) + { + return 0; + } + + const ptrdiff_t to_be_replaced_length = to_be_replaced_finish - to_be_replaced_start; + const ptrdiff_t by_replacement_length = (NULL == by_replacement_start || NULL == by_replacement_finish || + by_replacement_finish < by_replacement_start) ? -1 : (by_replacement_finish - by_replacement_start); + + if (1 == to_be_replaced_length && to_be_replaced_length == by_replacement_length) + { + if (to_be_replaced_start[0] == by_replacement_start[0]) + { + return 1; + } + + for (ptrdiff_t i = 0; i < input_length; ++i) + { + if (to_be_replaced_start[0] == *((char*)buffer_data(output, size + i))) + { + *((char*)buffer_data(output, size + i)) = by_replacement_start[0]; + } + } + + return 1; + } + + if (!buffer_resize(output, size)) + { + return 0; + } + + const char* previous_position = input_start; + ptrdiff_t index = -1; + + while (-1 != (index = string_index_of_any( + previous_position, input_finish, to_be_replaced_start, to_be_replaced_finish, 1))) + { + if (!buffer_append_char(output, previous_position, index)) + { + return 0; + } + + if (-1 != by_replacement_length) + { + if (!buffer_append_char(output, by_replacement_start, by_replacement_length)) + { + return 0; + } + } + + previous_position += index + to_be_replaced_length; + } + + if (!buffer_append_char(output, previous_position, input_finish - previous_position)) + { + return 0; + } + + return 1; +} + +uint8_t string_starts_with(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish) +{ + return 0 == string_index_of_any(input_start, input_finish, value_start, value_finish, 1); +} + +uint8_t string_substring(const char* input, ptrdiff_t input_length, + ptrdiff_t index, ptrdiff_t length, struct buffer* output) +{ + if (NULL == input || NULL == output || + input_length < 0 || + index < 0 || + length < 0 || + input_length < index + length) + { + return 0; + } + + return buffer_append_char(output, &input[index], length); +} + +uint8_t string_to_lower(const char* input_start, const char* input_finish, char* output) +{ + if (NULL == input_start || NULL == input_finish || NULL == output || + input_finish <= input_start) + { + return 0; + } + + const char* pos = input_start; + + while (input_finish != pos) + { + *output = (char)tolower(*pos); + ++pos; + ++output; + } + + return 1; +} + +uint8_t string_to_upper(const char* input_start, const char* input_finish, char* output) +{ + if (NULL == input_start || NULL == input_finish || NULL == output || + input_finish <= input_start) + { + return 0; + } + + const char* pos = input_start; + + while (input_finish != pos) + { + *output = (char)toupper(*pos); + ++pos; + ++output; + } + + return 1; +} + +enum string_function +{ + contains, ends_with, get_length, index_of, last_index_of, + pad_left, pad_right, replace, starts_with, substring, + to_lower, to_upper, trim, trim_end, trim_start, + quote, un_quote, equal, empty, + UNKNOWN_STRING_FUNCTION +}; + +enum string_trim_mode { string_trim_mode_all = trim, string_trim_mode_end = trim_end, string_trim_mode_start = trim_start }; + +uint8_t string_trim_any(struct range* input_output, enum string_trim_mode mode) +{ + static const char chars_to_trim[] = { '\0', '\t', ' ', '\n' }; + + if (NULL != input_output && input_output->start == input_output->finish) + { + return 1; + } + + if (range_is_null_or_empty(input_output) || + (string_trim_mode_all != mode && string_trim_mode_end != mode && string_trim_mode_start != mode)) + { + return 0; + } + + if (string_trim_mode_all == mode || string_trim_mode_start == mode) + { + input_output->start = find_any_symbol_like_or_not_like_that(input_output->start, input_output->finish, + chars_to_trim, 4, 0, 1); + } + + if (input_output->start < input_output->finish && + (string_trim_mode_all == mode || string_trim_mode_end == mode)) + { + const char* pos = input_output->start; + const char* prev_pos = pos; + + while (input_output->finish > (pos = find_any_symbol_like_or_not_like_that( + pos, input_output->finish, chars_to_trim, 4, 0, 1))) + { + prev_pos = pos; + ++pos; + } + + uint8_t shift = 1; + + for (uint8_t i = 0; i < 4; ++i) + { + if (chars_to_trim[i] == (*prev_pos)) + { + shift = 0; + break; + } + } + + input_output->finish = prev_pos + shift; + } + + return 1; +} + +uint8_t string_trim(struct range* input_output) +{ + return string_trim_any(input_output, string_trim_mode_all); +} + +uint8_t string_trim_end(struct range* input_output) +{ + return string_trim_any(input_output, string_trim_mode_end); +} + +uint8_t string_trim_start(struct range* input_output) +{ + return string_trim_any(input_output, string_trim_mode_start); +} + +uint8_t string_quote(const char* input_start, const char* input_finish, + struct buffer* output) +{ + if (NULL == output) + { + return 0; + } + + if (range_in_parts_is_null_or_empty(input_start, input_finish)) + { + return buffer_push_back(output, '"') && buffer_push_back(output, '"'); + } + + return buffer_push_back(output, '"') && + buffer_append_char(output, input_start, input_finish - input_start) && + buffer_push_back(output, '"'); +} + +uint8_t string_un_quote(struct range* input_output) +{ + if (NULL == input_output || + NULL == input_output->start || + NULL == input_output->finish || + input_output->finish < input_output->start) + { + return 0; + } + + input_output->start = find_any_symbol_like_or_not_like_that( + input_output->start, input_output->finish, "\"", 1, 0, 1); + input_output->finish = find_any_symbol_like_or_not_like_that( + input_output->finish - 1, input_output->start, "\"", 1, 0, -1); + + if (input_output->start < input_output->finish) + { + input_output->finish++; + } + + return 1; +} + +uint8_t string_equal(const char* input_1_start, const char* input_1_finish, + const char* input_2_start, const char* input_2_finish) +{ + if (NULL == input_1_start || NULL == input_1_finish || + NULL == input_2_start || NULL == input_2_finish || + input_1_finish < input_1_start || input_2_finish < input_2_start || + input_1_finish - input_1_start != input_2_finish - input_2_start) + { + return 0; + } + + if (0 == input_1_finish - input_1_start) + { + return 1; + } + + if (input_1_start == input_2_start) + { + return 1; + } + + return (0 == memcmp(input_1_start, input_2_start, input_1_finish - input_1_start)); +} + +static const char* string_function_str[] = +{ + "contains", "ends-with", "get-length", "index-of", "last-index-of", + "pad-left", "pad-right", "replace", "starts-with", "substring", + "to-lower", "to-upper", "trim", "trim-end", "trim-start", + "quote", "un-quote", "equal", "empty" +}; + +uint8_t string_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, string_function_str, UNKNOWN_STRING_FUNCTION); +} + +uint8_t string_exec_function(uint8_t function, + const struct buffer* arguments, uint8_t arguments_count, struct buffer* output) +{ + if (UNKNOWN_STRING_FUNCTION <= function || NULL == arguments || 3 < arguments_count || NULL == output) + { + return 0; + } + + const ptrdiff_t current_output_size = buffer_size(output); + struct range argument1; + struct range argument2; + struct range argument3; + argument1.start = argument2.start = argument3.start = argument1.finish = argument2.finish = argument3.finish = + NULL; + + switch (arguments_count) + { + case 0: + break; + + case 1: + if (!common_get_one_argument(arguments, &argument1, 0)) + { + /*NOTE: allowed to string unit, where empty string can be as input.*/ + argument1.start = argument1.finish = (const char*)&argument1; + } + + break; + + case 2: + if (!common_get_two_arguments(arguments, &argument1, &argument2, substring == function ? 1 : 0)) + { + if (NULL == argument1.start) + { + argument1.start = argument1.finish = (const char*)&argument1; + } + + if (NULL == argument2.start) + { + argument2.start = argument2.finish = (const char*)&argument2; + } + } + + break; + + case 3: + if (!common_get_three_arguments(arguments, &argument1, &argument2, &argument3, substring == function ? 1 : 0)) + { + if (NULL == argument1.start) + { + argument1.start = argument1.finish = (const char*)&argument1; + } + + if (NULL == argument2.start) + { + argument2.start = argument2.finish = (const char*)&argument2; + } + + if (NULL == argument3.start) + { + argument3.start = argument3.finish = (const char*)&argument3; + } + } + + break; + + default: + return 0; + } + + switch (function) + { + case contains: + return (2 == arguments_count) && + bool_to_string(string_contains(argument1.start, argument1.finish, argument2.start, argument2.finish), output); + + case ends_with: + return (2 == arguments_count) && + bool_to_string(string_ends_with(argument1.start, argument1.finish, argument2.start, argument2.finish), + output); + + case get_length: + return (1 == arguments_count) && + int64_to_string(string_get_length(argument1.start, argument1.finish), output); + + case index_of: + return (2 == arguments_count) && + int64_to_string(string_index_of_any(argument1.start, argument1.finish, argument2.start, argument2.finish, 1), + output); + + case last_index_of: + return (2 == arguments_count) && + int64_to_string(string_index_of_any(argument1.start, argument1.finish, argument2.start, argument2.finish, -1), + output); + + /*TODO: + case pad_left: + break; + + case pad_right: + break; + */ + + case replace: + return (3 == arguments_count) && string_replace( + argument1.start, argument1.finish, + argument2.start, argument2.finish, + argument3.start, argument3.finish, + output); + + case starts_with: + return (2 == arguments_count) && + bool_to_string(string_starts_with(argument1.start, argument1.finish, argument2.start, argument2.finish), + output); + + case substring: + { + if (2 != arguments_count && 3 != arguments_count) + { + break; + } + else if (2 == arguments_count && range_is_null_or_empty(&argument2)) + { + break; + } + else if (3 == arguments_count && (range_is_null_or_empty(&argument2) || range_is_null_or_empty(&argument3))) + { + break; + } + + if (range_is_null_or_empty(&argument1)) + { + return 1; + } + + const ptrdiff_t input_length = range_size(&argument1) - 1; + const ptrdiff_t index = (ptrdiff_t)int64_parse(argument2.start); + const ptrdiff_t length = (3 == arguments_count) ? (ptrdiff_t)int64_parse(argument3.start) : + (input_length - index); + /**/ + return string_substring(argument1.start, input_length, index, length, output); + } + + case to_lower: + if (1 != arguments_count || (argument1.start != argument1.finish && + !buffer_append_char(output, NULL, range_size(&argument1)))) + { + break; + } + + return string_to_lower(argument1.start, argument1.finish, (char*)buffer_data(output, current_output_size)); + + case to_upper: + if (1 != arguments_count || (argument1.start != argument1.finish && + !buffer_append_char(output, NULL, range_size(&argument1)))) + { + break; + } + + return string_to_upper(argument1.start, argument1.finish, (char*)buffer_data(output, current_output_size)); + + case trim: + case trim_end: + case trim_start: + return (1 == arguments_count) && + string_trim_any(&argument1, function) && + buffer_append_char(output, argument1.start, range_size(&argument1)); + + case quote: + return (1 == arguments_count) && + string_quote(argument1.start, argument1.finish, output); + + case un_quote: + return (1 == arguments_count) && + string_un_quote(&argument1) && + buffer_append_data_from_range(output, &argument1); + + case equal: + return (2 == arguments_count) && + bool_to_string(string_equal(argument1.start, argument1.finish, argument2.start, argument2.finish), output); + + case empty: + return (1 == arguments_count) && bool_to_string(range_is_null_or_empty(&argument1), output); + + case UNKNOWN_STRING_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/string_unit.h b/string_unit.h new file mode 100644 index 0000000..dad8215 --- /dev/null +++ b/string_unit.h @@ -0,0 +1,56 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _STRING_UNIT_H_ +#define _STRING_UNIT_H_ + +#include +#include + +struct buffer; +struct range; + +uint8_t string_contains(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish); +uint8_t string_ends_with(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish); +ptrdiff_t string_get_length(const char* input_start, const char* input_finish); +ptrdiff_t string_index_of(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish); +ptrdiff_t string_last_index_of(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish); +/*TODO: string_pad_left +string_pad_right*/ +#if 0 +uint8_t string_replace_in_buffer(struct buffer* input_output, + const char* to_be_replaced, ptrdiff_t to_be_replaced_length, + const char* by_replacement, ptrdiff_t by_replacement_length); +#endif +uint8_t string_replace(const char* input_start, const char* input_finish, + const char* to_be_replaced_start, const char* to_be_replaced_finish, + const char* by_replacement_start, const char* by_replacement_finish, + struct buffer* output); +uint8_t string_starts_with(const char* input_start, const char* input_finish, + const char* value_start, const char* value_finish); +uint8_t string_substring(const char* input, ptrdiff_t input_length, + ptrdiff_t index, ptrdiff_t length, struct buffer* output); +uint8_t string_to_lower(const char* input_start, const char* input_finish, char* output); +uint8_t string_to_upper(const char* input_start, const char* input_finish, char* output); +uint8_t string_trim(struct range* input_output); +uint8_t string_trim_end(struct range* input_output); +uint8_t string_trim_start(struct range* input_output); +uint8_t string_quote(const char* input_start, const char* input_finish, + struct buffer* output); +uint8_t string_un_quote(struct range* input_output); +uint8_t string_equal(const char* input_1_start, const char* input_1_finish, + const char* input_2_start, const char* input_2_finish); + +uint8_t string_get_function(const char* name_start, const char* name_finish); +uint8_t string_exec_function(uint8_t function, const struct buffer* arguments, + uint8_t arguments_count, struct buffer* output); + +#endif diff --git a/target.c b/target.c new file mode 100644 index 0000000..efb96de --- /dev/null +++ b/target.c @@ -0,0 +1,279 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "target.h" +#include "buffer.h" +#include "common.h" +#include "property.h" +#include "range.h" +#include "string_unit.h" +#include "xml.h" + +#include + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +struct target +{ + char name[INT8_MAX + 1]; + uint8_t name_length; + /**/ + struct buffer description; + struct buffer depends; + struct buffer content; + /**/ + uint8_t has_executed; +}; + +struct depend +{ + const struct target* target; + char name[INT8_MAX + 1]; + uint8_t name_length; +}; + +uint8_t buffer_append_target(struct buffer* targets, const struct target* data, ptrdiff_t data_count) +{ + return buffer_append(targets, (const uint8_t*)data, sizeof(struct target) * data_count); +} + +struct target* buffer_target_data(const struct buffer* targets, ptrdiff_t data_position) +{ + return (struct target*)buffer_data(targets, sizeof(struct target) * data_position); +} + +uint8_t target_exists(const struct buffer* targets, + const char* name, uint8_t name_length) +{ + void* trg = NULL; + return target_get(targets, name, name_length, &trg); +} + +uint8_t target_get_current_target(const void* target, const char** name, ptrdiff_t* name_length) +{ + if (NULL == target) + { + return 0; + } + + const struct target* trg = (const struct target*)target; + *name = trg->name; + *name_length = trg->name_length; + return 1; +} + +uint8_t target_has_executed(const struct buffer* targets, + const char* name, uint8_t name_length) +{ + void* trg = NULL; + + if (!target_get(targets, name, name_length, &trg)) + { + return 0; + } + + return 0 < ((struct target*)trg)->has_executed; +} + +uint8_t target_get(const struct buffer* targets, const char* name, + uint8_t name_length, void** target) +{ + if (NULL == targets || NULL == name || 0 == name_length || NULL == target) + { + return 0; + } + + ptrdiff_t i = 0; + struct target* target_ = NULL; + + while (NULL != (target_ = buffer_target_data(targets, i++))) + { + if (name_length == target_->name_length && + 0 == memcmp(&target_->name, name, name_length)) + { + (*target) = target_; + return 1; + } + } + + return 0; +} + +uint8_t target_add_depend(const struct range* depend_name, struct buffer* depends) +{ + if (range_is_null_or_empty(depend_name)) + { + return 0; + } + + struct depend depend_; + + depend_.target = NULL; + + depend_.name_length = (uint8_t)range_size(depend_name); + +#if __STDC_SEC_API__ + if (0 != memcpy_s(depend_.name, INT8_MAX, depend_name->start, depend_.name_length)) + { + return 0; + } + +#else + + if (INT8_MAX < depend_.name_length) + { + return 0; + } + + memcpy(depend_.name, depend_name->start, depend_.name_length); +#endif + return buffer_append(depends, (const uint8_t*)&depend_, sizeof(struct depend)); +} + +uint8_t target_add(struct buffer* targets, + const struct range* name, const struct range* description, + const struct range* depends, const struct range* content) +{ + if (NULL == targets || range_is_null_or_empty(name) || range_is_null_or_empty(content)) + { + return 0; + } + + struct target new_target; + + SET_NULL_TO_BUFFER(new_target.description); + + SET_NULL_TO_BUFFER(new_target.depends); + + SET_NULL_TO_BUFFER(new_target.content); + + new_target.name_length = (uint8_t)range_size(name); + + if (target_exists(targets, name->start, new_target.name_length)) + { + return 0; + } + +#if __STDC_SEC_API__ + + if (0 != memcpy_s(new_target.name, INT8_MAX, name->start, new_target.name_length)) + { + return 0; + } + +#else + + if (INT8_MAX < new_target.name_length) + { + return 0; + } + + memcpy(new_target.name, name->start, new_target.name_length); +#endif + new_target.name[new_target.name_length] = '\0'; + + if (!range_is_null_or_empty(description)) + { + if (!buffer_append_char(&new_target.description, description->start, + description->finish - description->start)) + { + return 0; + } + } + + if (!range_is_null_or_empty(depends)) + { + struct range depend_; + depend_.start = depends->start; + depend_.finish = depends->finish; + + while (depends->finish != (depend_.finish = find_any_symbol_like_or_not_like_that(depend_.finish, + depends->finish, ",", 1, 1, 1))) + { + const char* pos = depend_.finish + 1; + + if (!string_trim(&depend_) || !target_add_depend(&depend_, &new_target.depends)) + { + return 0; + } + + depend_.start = pos; + depend_.finish = pos; + } + + if (!string_trim(&depend_) || !target_add_depend(&depend_, &new_target.depends)) + { + return 0; + } + } + + if (!buffer_append_char(&new_target.content, content->start, content->finish - content->start)) + { + return 0; + } + + new_target.has_executed = 0; + return buffer_append_target(targets, &new_target, 1); +} + +uint8_t target_add_from_xml_tag_record(struct buffer* targets, + const char* record_start, const char* record_finish) +{ + struct range name; + struct range description; + struct range depends; + struct range content; + /**/ + const char* target_attributes[] = { "name", "description", "depends" }; + const uint8_t target_attributes_lengths[] = { 4, 11, 7 }; + /**/ + struct range* attribute_values[3]; + attribute_values[0] = &name; + attribute_values[1] = &description; + attribute_values[2] = &depends; + + for (uint8_t i = 0; i < 3; ++i) + { + if (!xml_get_attribute_value(record_start, record_finish, target_attributes[i], + target_attributes_lengths[i], attribute_values[i])) + { + if (!i) + { + return 0; + } + + attribute_values[i]->start = attribute_values[i]->finish = NULL; + continue; + } + } + + content.start = 1 + xml_get_tag_finish_pos(record_start, record_finish); + content.finish = record_finish; + return target_add(targets, &name, &description, &depends, &content); +} + +void target_clear(struct buffer* targets) +{ + ptrdiff_t i = 0; + struct target* target = NULL; + + if (NULL == targets) + { + return; + } + + while (NULL != (target = buffer_target_data(targets, i++))) + { + buffer_release(&target->description); + buffer_release(&target->depends); + buffer_release(&target->content); + } + + buffer_release(targets); +} diff --git a/target.h b/target.h new file mode 100644 index 0000000..934c135 --- /dev/null +++ b/target.h @@ -0,0 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _TARGET_H_ +#define _TARGET_H_ + +#include +#include + +struct buffer; +struct range; + +uint8_t target_exists(const struct buffer* targets, const char* name, uint8_t name_length); +uint8_t target_get_current_target(const void* target, const char** name, ptrdiff_t* name_length); +uint8_t target_has_executed(const struct buffer* targets, const char* name, uint8_t name_length); + +uint8_t target_get(const struct buffer* targets, const char* name, uint8_t name_length, void** target); +uint8_t target_add(struct buffer* targets, + const struct range* name, const struct range* description, + const struct range* depends, const struct range* content); +uint8_t target_add_from_xml_tag_record(struct buffer* targets, + const char* record_start, const char* record_finish); +void target_clear(struct buffer* targets); + +#endif diff --git a/version.c b/version.c new file mode 100644 index 0000000..c3cc1dc --- /dev/null +++ b/version.c @@ -0,0 +1,250 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "version.h" +#include "buffer.h" +#include "common.h" +#include "conversion.h" +#include "range.h" + +#include +/*#include */ + +#if !defined(__STDC_SEC_API__) +#define __STDC_SEC_API__ ((__STDC_LIB_EXT1__) || (__STDC_SECURE_LIB__) || (__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_SECURE_LIB__)) +#endif + +uint8_t version_parse(const char* input_start, const char* input_finish, struct Version* version) +{ + if (range_in_parts_is_null_or_empty(input_start, input_finish) || NULL == version) + { + return 0; + } + + uint32_t* ver_in_parts[4]; + ver_in_parts[0] = &version->major; + ver_in_parts[1] = &version->minor; + ver_in_parts[2] = &version->build; + ver_in_parts[3] = &version->revision; + /**/ + const uint8_t count = sizeof(ver_in_parts) / sizeof(*ver_in_parts); + uint8_t i = 0; + + for (input_start = find_any_symbol_like_or_not_like_that(input_start, input_finish, "0123456789", 10, 1, 1); + input_start < input_finish && i < count; ++i, ++input_start) + { + if (input_finish == input_start || + input_start + 1 == find_any_symbol_like_or_not_like_that(input_start, input_start + 1, "0123456789", 10, 1, + 1)) + { + if (i == 0) + { + return 0; + } + + break; + } + + *(ver_in_parts[i]) = (uint32_t)int_parse(input_start); + input_start = find_any_symbol_like_or_not_like_that(input_start + 1, input_finish, ".", 1, 1, 1); + } + + if (i == 0) + { + return 0; + } + + while (i < count) + { + *(ver_in_parts[i++]) = 0; + } + + return 1; +} + +uint8_t version_to_char_array(const struct Version* version, char* output) +{ + if (NULL == version || NULL == output) + { + return 0; + } + +#if __STDC_SEC_API__ + const uint8_t length = (uint8_t)sprintf_s( + output, 64, "%u.%u.%u.%u", version->major, version->minor, version->build, version->revision); +#else + const uint8_t length = (uint8_t)sprintf( + output, "%u.%u.%u.%u", version->major, version->minor, version->build, version->revision); +#endif + return length; +} + +uint8_t version_to_string(const struct Version* version, struct buffer* output) +{ + if (NULL == version || NULL == output) + { + return 0; + } + + const ptrdiff_t size = buffer_size(output); + + if (!buffer_append_char(output, NULL, 64)) + { + return 0; + } + + char* ptr = (char*)buffer_data(output, size); + return buffer_resize(output, size + version_to_char_array(version, ptr)); +} + +uint8_t version_less(const struct Version* a, const struct Version* b) +{ + if (NULL == a || + NULL == b) + { + return 0; + } + + if (a->major < b->major) + { + return 1; + } + else if (a->major == b->major && a->minor < b->minor) + { + return 1; + } + else if (a->major == b->major && a->minor == b->minor && a->build < b->build) + { + return 1; + } + else if (a->major == b->major && a->minor == b->minor && a->build == b->build && a->revision < b->revision) + { + return 1; + } + + return 0; +} + +uint8_t version_greater(const struct Version* a, const struct Version* b) +{ + if (NULL == a || + NULL == b) + { + return 0; + } + + if (a->major > b->major) + { + return 1; + } + else if (a->major == b->major && a->minor > b->minor) + { + return 1; + } + else if (a->major == b->major && a->minor == b->minor && a->build > b->build) + { + return 1; + } + else if (a->major == b->major && a->minor == b->minor && a->build == b->build && a->revision > b->revision) + { + return 1; + } + + return 0; +} + +static const char* version_function_str[] = +{ + "parse", "to-string", "get-major", "get-minor", "get-build", "get-revision", "less", "greater" +}; + +enum version_function +{ + parse, to_string, get_major, get_minor, get_build, get_revision, less_, greater_, + UNKNOWN_VERSION_FUNCTION +}; + +uint8_t version_get_function(const char* name_start, const char* name_finish) +{ + return common_string_to_enum(name_start, name_finish, version_function_str, UNKNOWN_VERSION_FUNCTION); +} + +uint8_t version_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output) +{ + if (UNKNOWN_VERSION_FUNCTION <= function || + NULL == arguments || + (1 != arguments_count && 2 != arguments_count) || + NULL == output) + { + return 0; + } + + struct range argument_1; + + struct range argument_2; + + argument_1.start = argument_2.start = argument_1.finish = argument_2.finish = NULL; + + if (1 == arguments_count && !common_get_one_argument(arguments, &argument_1, 1)) + { + return 0; + } + + if (2 == arguments_count && !common_get_two_arguments(arguments, &argument_1, &argument_2, 1)) + { + return 0; + } + + struct Version version_1; + + struct Version version_2; + + version_1.major = version_2.major = version_1.minor = version_2.minor = version_1.build = version_2.build = + version_1.revision = version_2.revision = 0; + + if (!version_parse(argument_1.start, argument_1.finish, &version_1)) + { + return 0; + } + + if (2 == arguments_count && !version_parse(argument_2.start, argument_2.finish, &version_2)) + { + return 0; + } + + switch (function) + { + case parse: + case to_string: + return 1 == arguments_count && version_to_string(&version_1, output); + + case get_major: + return 1 == arguments_count && int_to_string(version_1.major, output); + + case get_minor: + return 1 == arguments_count && int_to_string(version_1.minor, output); + + case get_build: + return 1 == arguments_count && int_to_string(version_1.build, output); + + case get_revision: + return 1 == arguments_count && int_to_string(version_1.revision, output); + + case less_: + return 2 == arguments_count && bool_to_string(version_less(&version_1, &version_2), output); + + case greater_: + return 2 == arguments_count && bool_to_string(version_greater(&version_1, &version_2), output); + + case UNKNOWN_VERSION_FUNCTION: + default: + break; + } + + return 0; +} diff --git a/version.h b/version.h new file mode 100644 index 0000000..2fc0179 --- /dev/null +++ b/version.h @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#include + +#if !defined(PROGRAM_VERSION) +#define PROGRAM_VERSION "YYYY.MM.DD.?" +#endif +#if !defined(PROGRAM_VERSION_LENGTH) +#define PROGRAM_VERSION_LENGTH 12 +#endif + +struct Version +{ + uint32_t major; + uint32_t minor; + uint32_t build; + uint32_t revision; +}; + +struct buffer; + +uint8_t version_parse(const char* input_start, const char* input_finish, struct Version* version); +uint8_t version_to_char_array(const struct Version* version, char* output); +uint8_t version_to_string(const struct Version* version, struct buffer* output); + +uint8_t version_less(const struct Version* a, const struct Version* b); +uint8_t version_greater(const struct Version* a, const struct Version* b); + +uint8_t version_get_function(const char* name_start, const char* name_finish); +uint8_t version_exec_function(uint8_t function, const struct buffer* arguments, uint8_t arguments_count, + struct buffer* output); + +#endif diff --git a/xml.c b/xml.c new file mode 100644 index 0000000..e4988e4 --- /dev/null +++ b/xml.c @@ -0,0 +1,286 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#include "xml.h" +#include "buffer.h" +#include "common.h" +#include "range.h" +#include "string_unit.h" + +uint8_t go_to_comment_end_if_it_exists(const char** start, const char* finish) +{ + static const char* comment_start = ""; + static const uint8_t comment_end_length = 3; + /**/ + start_ += comment_start_length; + const ptrdiff_t index = string_index_of(start_, finish, comment_end, comment_end + comment_end_length); + + if (-1 == index) + { + return 0; + } + + start_ += index + comment_end_length; + + if (finish == (start_ = find_any_symbol_like_or_not_like_that(start_, finish, "<", 1, 1, 1))) + { + return 0; + } + + *start = start_; + return go_to_comment_end_if_it_exists(start, finish); + } + + return finish != start_; +} + +const char* xml_get_tag_finish_pos(const char* start, const char* finish) +{ + if (range_in_parts_is_null_or_empty(start, finish)) + { + return finish; + } + + while (start != finish) + { + if ('>' == *start) + { + break; + } + else if ('"' == *start) + { + start = find_any_symbol_like_or_not_like_that(start + 1, finish, "\"", 1, 1, 1); + + if (start == finish) + { + break; + } + } + + ++start; + } + + return start; +} + +uint16_t xml_get_sub_nodes_elements(const char* start, const char* finish, struct buffer* elements) +{ + static const char tag_close = '/'; + + if (NULL == start || NULL == finish || finish < start || NULL == elements) + { + return 0; + } + + uint16_t count = 0; + uint8_t depth = 0; + const char* pos = start; + + while (finish != pos) + { + while (finish != pos) + { + if ('<' == *pos) + { + break; + } + + ++pos; + } + + if (!go_to_comment_end_if_it_exists(&pos, finish)) + { + return count; + } + else + { + ++pos; + } + + const char* tag_finish_pos = xml_get_tag_finish_pos(pos, finish); + + if ('?' == (*pos)) + { + pos = tag_finish_pos; + continue; + } + + if (tag_close == (*pos)) + { + if (0 == depth) + { + break; + } + + --depth; + } + else + { + if (0 == depth) + { + struct range element; + element.start = pos; + element.finish = tag_finish_pos; + + if (!buffer_append_range(elements, &element, 1)) + { + return 0; + } + + if (count < UINT16_MAX) + { + ++count; + } + else + { + return 0; + } + } + + const char* tag_finish_prev_pos = tag_finish_pos; + --tag_finish_prev_pos; + + if (tag_close != (*tag_finish_prev_pos)) + { + if (depth < UINT8_MAX) + { + ++depth; + } + else + { + return 0; + } + } + } + + if (0 == depth) + { + struct range* element = buffer_range_data(elements, count - 1); + element->finish = tag_finish_pos; + } + + pos = tag_finish_pos; + } + + return count; +} + +uint8_t xml_get_tag_name(const char* start, const char* finish, struct range* name) +{ + static const char tab_space_close_tag[] = { '\t', ' ', '/', '>', '\r', '\n' }; + + if (NULL == start || NULL == finish || NULL == name || finish < start) + { + return 0; + } + + name->start = start; + name->finish = find_any_symbol_like_or_not_like_that(start, finish, tab_space_close_tag, 6, 1, 1); + return start < name->finish; +} + +uint8_t xml_get_attribute_value(const char* start, const char* finish, + const char* attribute, ptrdiff_t attribute_length, struct range* value) +{ + if (range_in_parts_is_null_or_empty(start, finish) || NULL == attribute || + 0 == attribute_length || NULL == value || (finish - start) < attribute_length) + { + return 0; + } + + const char* pos = start; + + while (finish != (pos = (find_any_symbol_like_or_not_like_that(pos, finish, "=", 1, 1, 1)))) + { + const char* key_finish = find_any_symbol_like_or_not_like_that(pos, start, "= \t", 3, 0, -1); + const char* key_start = find_any_symbol_like_or_not_like_that(key_finish, start, " \t", 2, 1, -1); + ++key_finish; + const char ch = *key_start; + + if (' ' == ch || '\t' == ch) + { + ++key_start; + } + + if (string_equal(key_start, key_finish, attribute, attribute + attribute_length)) + { + value->start = find_any_symbol_like_or_not_like_that(pos, finish, "\"", 1, 1, 1); + + if (finish == value->start) + { + break; + } + + ++value->start; + value->finish = find_any_symbol_like_or_not_like_that(value->start, finish, "\"", 1, 1, 1); + return 1; + } + + ++pos; + } + + return 0; +} + +uint8_t xml_is_attribute_exists(const char* start, const char* finish, const char* attribute, + ptrdiff_t attribute_length) +{ + struct range attribute_value; + return xml_get_attribute_value(start, finish, attribute, attribute_length, &attribute_value); +} + +uint8_t xml_get_element_value_from_parts(const char* element_start, const char* element_finish, + struct range* value) +{ + if (range_in_parts_is_null_or_empty(element_start, element_finish) || NULL == value) + { + return 0; + } + + value->start = find_any_symbol_like_or_not_like_that(element_start, element_finish, ">", 1, 1, 1); + value->finish = element_finish; + + if (value->finish == value->start) + { + return 0; + } + + if (element_start < value->start && ('/' == *(value->start - 1))) + { + value->finish = value->start; + } + else + { + ++value->start; + value->finish = find_any_symbol_like_or_not_like_that(value->finish, value->start, "<", 1, 1, -1); + } + + return value->start <= value->finish; +} + +uint8_t xml_get_element_value(const struct range* element, struct range* value) +{ + if (range_is_null_or_empty(element) || NULL == value) + { + return 0; + } + + return xml_get_element_value_from_parts(element->start, element->finish, value); +} diff --git a/xml.h b/xml.h new file mode 100644 index 0000000..7740008 --- /dev/null +++ b/xml.h @@ -0,0 +1,31 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 https://github.com/TheVice/ + * + */ + +#ifndef _XML_H_ +#define _XML_H_ + +#include +#include + +/*TODO: xmlpeek +xmlpoke*/ + +struct buffer; +struct range; + +const char* xml_get_tag_finish_pos(const char* start, const char* finish); +uint16_t xml_get_sub_nodes_elements(const char* start, const char* finish, struct buffer* elements); +uint8_t xml_get_tag_name(const char* start, const char* finish, struct range* name); +uint8_t xml_get_attribute_value(const char* start, const char* finish, + const char* attribute, ptrdiff_t attribute_length, struct range* value); +uint8_t xml_is_attribute_exists(const char* start, const char* finish, + const char* attribute, ptrdiff_t attribute_length); +uint8_t xml_get_element_value_from_parts(const char* element_start, const char* element_finish, + struct range* value); +uint8_t xml_get_element_value(const struct range* element, struct range* value); + +#endif