From c095d7d5b6ec9201482b9854ef35d3c2af97449a Mon Sep 17 00:00:00 2001 From: Sun Dro Date: Wed, 22 Jun 2022 17:02:20 +0400 Subject: [PATCH] Initial commit --- .gitignore | 52 ++ .travis.yml | 20 + CMakeLists.txt | 71 ++ LICENSE | 22 + Makefile | 64 ++ README | 13 + build.sh | 41 ++ clean.sh | 31 + cmake/FindRT.cmake | 39 + examples/Makefile | 92 +++ examples/api.c | 179 +++++ examples/array.c | 161 +++++ examples/events.c | 303 ++++++++ examples/example.json | 43 ++ examples/files.c | 59 ++ examples/json.c | 259 +++++++ examples/list.c | 107 +++ examples/ntp.c | 31 + examples/run.sh | 20 + examples/server.c | 114 +++ examples/statcov.c | 128 ++++ examples/strings.c | 210 ++++++ examples/thread.c | 49 ++ examples/xcrypt.c | 349 +++++++++ examples/xhttp.c | 501 +++++++++++++ examples/xjson.c | 114 +++ examples/xlog.c | 90 +++ examples/xpass.c | 1054 +++++++++++++++++++++++++++ examples/xsrc.c | 368 ++++++++++ examples/xtop.c | 1587 +++++++++++++++++++++++++++++++++++++++++ install.sh | 4 + media/mdtp.c | 325 +++++++++ media/mdtp.h | 104 +++ media/ntp.c | 95 +++ media/ntp.h | 25 + media/rtp.c | 142 ++++ media/rtp.h | 59 ++ media/ts.c | 498 +++++++++++++ media/ts.h | 216 ++++++ media/xapi.c | 437 ++++++++++++ media/xapi.h | 92 +++ smake.json | 16 + src/addr.c | 282 ++++++++ src/addr.h | 110 +++ src/array.c | 519 ++++++++++++++ src/array.h | 99 +++ src/crypt.c | 911 +++++++++++++++++++++++ src/crypt.h | 130 ++++ src/errex.c | 116 +++ src/errex.h | 30 + src/event.c | 419 +++++++++++ src/event.h | 149 ++++ src/hash.c | 155 ++++ src/hash.h | 63 ++ src/http.c | 1081 ++++++++++++++++++++++++++++ src/http.h | 176 +++++ src/list.c | 221 ++++++ src/list.h | 64 ++ src/sock.c | 1505 ++++++++++++++++++++++++++++++++++++++ src/sock.h | 267 +++++++ src/sync.c | 243 +++++++ src/sync.h | 82 +++ src/thread.c | 216 ++++++ src/thread.h | 80 +++ src/xaes.c | 711 ++++++++++++++++++ src/xaes.h | 42 ++ src/xbuf.c | 504 +++++++++++++ src/xbuf.h | 92 +++ src/xcli.c | 547 ++++++++++++++ src/xcli.h | 106 +++ src/xcpu.c | 157 ++++ src/xcpu.h | 31 + src/xfs.c | 1248 ++++++++++++++++++++++++++++++++ src/xfs.h | 187 +++++ src/xjson.c | 1242 ++++++++++++++++++++++++++++++++ src/xjson.h | 145 ++++ src/xlog.c | 469 ++++++++++++ src/xlog.h | 202 ++++++ src/xmap.c | 293 ++++++++ src/xmap.h | 74 ++ src/xstd.h | 140 ++++ src/xstr.c | 1395 ++++++++++++++++++++++++++++++++++++ src/xstr.h | 203 ++++++ src/xtime.c | 318 +++++++++ src/xtime.h | 88 +++ src/xtop.c | 535 ++++++++++++++ src/xtop.h | 150 ++++ src/xtype.c | 108 +++ src/xtype.h | 55 ++ src/xver.c | 45 ++ src/xver.h | 28 + 91 files changed, 24217 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100755 build.sh create mode 100755 clean.sh create mode 100644 cmake/FindRT.cmake create mode 100644 examples/Makefile create mode 100644 examples/api.c create mode 100644 examples/array.c create mode 100644 examples/events.c create mode 100644 examples/example.json create mode 100644 examples/files.c create mode 100644 examples/json.c create mode 100644 examples/list.c create mode 100644 examples/ntp.c create mode 100755 examples/run.sh create mode 100644 examples/server.c create mode 100644 examples/statcov.c create mode 100644 examples/strings.c create mode 100644 examples/thread.c create mode 100644 examples/xcrypt.c create mode 100644 examples/xhttp.c create mode 100644 examples/xjson.c create mode 100644 examples/xlog.c create mode 100644 examples/xpass.c create mode 100644 examples/xsrc.c create mode 100644 examples/xtop.c create mode 100755 install.sh create mode 100644 media/mdtp.c create mode 100644 media/mdtp.h create mode 100644 media/ntp.c create mode 100644 media/ntp.h create mode 100644 media/rtp.c create mode 100644 media/rtp.h create mode 100644 media/ts.c create mode 100644 media/ts.h create mode 100644 media/xapi.c create mode 100644 media/xapi.h create mode 100644 smake.json create mode 100644 src/addr.c create mode 100644 src/addr.h create mode 100644 src/array.c create mode 100644 src/array.h create mode 100644 src/crypt.c create mode 100644 src/crypt.h create mode 100644 src/errex.c create mode 100644 src/errex.h create mode 100644 src/event.c create mode 100644 src/event.h create mode 100644 src/hash.c create mode 100644 src/hash.h create mode 100644 src/http.c create mode 100644 src/http.h create mode 100644 src/list.c create mode 100644 src/list.h create mode 100644 src/sock.c create mode 100644 src/sock.h create mode 100644 src/sync.c create mode 100644 src/sync.h create mode 100644 src/thread.c create mode 100644 src/thread.h create mode 100644 src/xaes.c create mode 100644 src/xaes.h create mode 100644 src/xbuf.c create mode 100644 src/xbuf.h create mode 100644 src/xcli.c create mode 100644 src/xcli.h create mode 100644 src/xcpu.c create mode 100644 src/xcpu.h create mode 100644 src/xfs.c create mode 100644 src/xfs.h create mode 100644 src/xjson.c create mode 100644 src/xjson.h create mode 100644 src/xlog.c create mode 100644 src/xlog.h create mode 100644 src/xmap.c create mode 100644 src/xmap.h create mode 100644 src/xstd.h create mode 100644 src/xstr.c create mode 100644 src/xstr.h create mode 100644 src/xtime.c create mode 100644 src/xtime.h create mode 100644 src/xtop.c create mode 100644 src/xtop.h create mode 100644 src/xtype.c create mode 100644 src/xtype.h create mode 100644 src/xver.c create mode 100644 src/xver.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6127b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a42182c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +os: + - linux + - osx +language: c +compiler: + - clang + - gcc +sudo: required +dist: trusty +script: + - mkdir build + - cd build + - cmake .. + - make +addons: + coverity_scan: + project: + name: "kala13x/libxutils" + notification_email: sundro.kala@gmail.com + branch_pattern: coverity_scan diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c455c97 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) +project(libxutils LANGUAGES C) + +IF (WIN32) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /W3") +ELSE() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -Wall") +ENDIF() + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +find_package(OpenSSL) +IF(OpenSSL_FOUND) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XUTILS_USE_SSL") +ENDIF() + +IF (APPLE) +find_package(RT REQUIRED) +ENDIF() + +SET(HEADER_DST "include/xutils") +SET(SOURCE_DIR "./src") +SET(MEDIA_DIR "./media") +include_directories(${SOURCE_DIR} ${MEDIA_DIR}) + +add_library(xutils STATIC + ${SOURCE_DIR}/addr.c + ${SOURCE_DIR}/array.c + ${SOURCE_DIR}/crypt.c + ${SOURCE_DIR}/xtime.c + ${SOURCE_DIR}/event.c + ${SOURCE_DIR}/list.c + ${SOURCE_DIR}/hash.c + ${SOURCE_DIR}/xlog.c + ${SOURCE_DIR}/xmap.c + ${SOURCE_DIR}/xstr.c + ${SOURCE_DIR}/xtype.c + ${SOURCE_DIR}/xver.c + ${SOURCE_DIR}/xbuf.c + ${SOURCE_DIR}/xtop.c + ${SOURCE_DIR}/xcli.c + ${SOURCE_DIR}/errex.c + ${SOURCE_DIR}/http.c + ${SOURCE_DIR}/sock.c + ${SOURCE_DIR}/sync.c + ${SOURCE_DIR}/xcpu.c + ${SOURCE_DIR}/thread.c + ${SOURCE_DIR}/xjson.c + ${SOURCE_DIR}/xfs.c + ${MEDIA_DIR}/mdtp.c + ${MEDIA_DIR}/ntp.c + ${MEDIA_DIR}/rtp.c + ${MEDIA_DIR}/ts.c +) + +install(TARGETS xutils DESTINATION lib) + +install(DIRECTORY "${SOURCE_DIR}/" + DESTINATION "${HEADER_DST}" + FILES_MATCHING + PATTERN "*.h" +) + +install(DIRECTORY "${MEDIA_DIR}/" + DESTINATION "${HEADER_DST}" + FILES_MATCHING + PATTERN "*.h" +) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c041eee --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2015-2021 Sun Dro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a5c92e --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +#################################### +# Automatically generated by SMake # +# https://github.com/kala13x/smake # +#################################### + +CFLAGS = -g -O2 -Wall -D_XUTILS_USE_SSL -I./src -I./media +LIBS = -lpthread -lssl -lcrypto +NAME = libxutils.a +ODIR = ./obj +OBJ = o + +OBJS = errex.$(OBJ) \ + event.$(OBJ) \ + hash.$(OBJ) \ + http.$(OBJ) \ + list.$(OBJ) \ + sync.$(OBJ) \ + thread.$(OBJ) \ + xcpu.$(OBJ) \ + xlog.$(OBJ) \ + xmap.$(OBJ) \ + xtime.$(OBJ) \ + xtype.$(OBJ) \ + xver.$(OBJ) \ + sock.$(OBJ) \ + xtop.$(OBJ) \ + xcli.$(OBJ) \ + addr.$(OBJ) \ + xfs.$(OBJ) \ + xjson.$(OBJ) \ + xaes.$(OBJ) \ + array.$(OBJ) \ + crypt.$(OBJ) \ + xbuf.$(OBJ) \ + xstr.$(OBJ) \ + mdtp.$(OBJ) \ + ntp.$(OBJ) \ + rtp.$(OBJ) \ + ts.$(OBJ) \ + xapi.$(OBJ) + +OBJECTS = $(patsubst %,$(ODIR)/%,$(OBJS)) +INSTALL_INC = /usr/local/include/xutils +INSTALL_BIN = /usr/local/lib +VPATH = ./src:./media + +.c.$(OBJ): + @test -d $(ODIR) || mkdir -p $(ODIR) + $(CC) $(CFLAGS) -c -o $(ODIR)/$@ $< $(LIBS) + +$(NAME):$(OBJS) + $(AR) rcs -o $(ODIR)/$(NAME) $(OBJECTS) + +.PHONY: install +install: + @test -d $(INSTALL_BIN) || mkdir -p $(INSTALL_BIN) + @install -m 0755 $(ODIR)/$(NAME) $(INSTALL_BIN)/ + @test -d $(INSTALL_INC) || mkdir -p $(INSTALL_INC) + @cp -r ./src/*.h $(INSTALL_INC)/ + @cp -r ./media/*.h $(INSTALL_INC)/ + +.PHONY: clean +clean: + $(RM) $(ODIR)/$(NAME) $(OBJECTS) diff --git a/README b/README new file mode 100644 index 0000000..47c19cd --- /dev/null +++ b/README @@ -0,0 +1,13 @@ + xUtils C Library release 2.x (c) Sun Dro + +This directory contains the sources of the xUtils C Library. See +the file "src/xver.h" to check out what release version you have. + +The xUtils C Library is the general purpose, cross-platform library for all +Linux / Unix and Windows operating systems. It provides the implementations +of various functionality to make some routines easier for all the programs +written in C and C-compatible languages such as C++, Rust, and Objective C. + +The xUtils C Library is free software. you can redistribute it and / or +modify it under the terms of The MIT License (See LICENSE file for more +information). diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..f064795 --- /dev/null +++ b/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +LIB_CRYPTO=/usr/lib64/libcrypto.so +LIB_SSL=/usr/lib64/libssl.so + +if [ -f "LIB_CRYPTO" ] && [ -f "LIB_SSL" ]; then + export XUTILS_USE_SSL=y +fi + +./clean.sh + +CPU_COUNT=`cat /proc/cpuinfo | grep processor -c` +MAKE_TOOL=0 + +if [ ! -z "$1" ]; then + MAKE_TOOL=$1 +fi + +if [ $MAKE_TOOL == 0 ]; then + echo "Specify build tool (cmake / smake)" + echo "example: $0 smake" + exit 1 +fi + +# Generate Makefile and build library +$MAKE_TOOL . && make -j $CPU_COUNT + +if [ ! -z "$2" ]; then + if [ $2 == "--examples" ]; then + cd examples + make -j $CPU_COUNT + cd .. + elif [ $2 == "--install" ]; then + sudo make install + cd examples + make -j $CPU_COUNT + sudo make install + fi +fi + +exit 0 diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..e6e9f38 --- /dev/null +++ b/clean.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +cd examples +make clean +cd .. + +make clean + +if [ -d ./obj ]; then + rm -rf ./obj +fi + +if [ -d ./build ]; then + rm -rf ./build +fi + +if [ -d ./CMakeFiles ]; then + rm -rf ./CMakeFiles +fi + +if [ -f ./CMakeCache.txt ]; then + rm -f ./CMakeCache.txt +fi + +if [ -f ./cmake_install.cmake ]; then + rm -f ./cmake_install.cmake +fi + +if [ -f ./install_manifest.txt ]; then + rm -f ./install_manifest.txt +fi diff --git a/cmake/FindRT.cmake b/cmake/FindRT.cmake new file mode 100644 index 0000000..db7c4de --- /dev/null +++ b/cmake/FindRT.cmake @@ -0,0 +1,39 @@ +# FindRT.cmake - Try to find the RT library +# Once done this will define +# +# RT_FOUND - System has rt +# RT_INCLUDE_DIR - The rt include directory +# RT_LIBRARIES - The libraries needed to use rt +# RT_DEFINITIONS - Compiler switches required for using rt +# +# Also creates an import target called RT::RT + +find_path (RT_INCLUDE_DIR NAMES time.h + PATHS + /usr + /usr/local + /opt + PATH_SUFFIXES +) + +find_library(RT_LIBRARIES NAMES rt + PATHS + /usr + /usr/local + /opt +) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(rt DEFAULT_MSG RT_LIBRARIES RT_INCLUDE_DIR) + +mark_as_advanced(RT_INCLUDE_DIR RT_LIBRARIES) + +if (NOT TARGET RT::RT) + add_library(RT::RT INTERFACE IMPORTED) + + set_target_properties(RT::RT PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${RT_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${RT_LIBRARIES} + ) +endif() \ No newline at end of file diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..a7839d3 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,92 @@ +CFLAGS = -g -O2 -Wall +LIBS = -lxutils -lpthread -lm +LIBS += -lssl -lcrypto + +EXECS = array \ + files \ + events \ + thread \ + statcov \ + strings \ + server \ + json \ + xtop \ + xlog \ + xcrypt \ + xpass \ + xhttp \ + xjson \ + xsrc \ + list \ + ntp \ + api + +all: + $(MAKE) $(EXECS) + +files: + $(CC) $(CFLAGS) -o files files.c $(LIBS) + +server: + $(CC) $(CFLAGS) -o server server.c $(LIBS) + +statcov: + $(CC) $(CFLAGS) -o statcov statcov.c $(LIBS) + +xlog: + $(CC) $(CFLAGS) -o xlog xlog.c $(LIBS) + +thread: + $(CC) $(CFLAGS) -o thread thread.c $(LIBS) + +xtop: + $(CC) $(CFLAGS) -o xtop xtop.c $(LIBS) + +array: + $(CC) $(CFLAGS) -o array array.c $(LIBS) + +xhttp: + $(CC) $(CFLAGS) -o xhttp xhttp.c $(LIBS) + +strings: + $(CC) $(CFLAGS) -o strings strings.c $(LIBS) + +json: + $(CC) $(CFLAGS) -o json json.c $(LIBS) + +xjson: + $(CC) $(CFLAGS) -o xjson xjson.c $(LIBS) + +events: + $(CC) $(CFLAGS) -o events events.c $(LIBS) + +list: + $(CC) $(CFLAGS) -o list list.c $(LIBS) + +ntp: + $(CC) $(CFLAGS) -o ntp ntp.c $(LIBS) + +xsrc: + $(CC) $(CFLAGS) -o xsrc xsrc.c $(LIBS) + +xcrypt: + $(CC) $(CFLAGS) -o xcrypt xcrypt.c $(LIBS) + +xpass: + $(CC) $(CFLAGS) -o xpass xpass.c $(LIBS) + +api: + $(CC) $(CFLAGS) -o api api.c $(LIBS) + +.PHONY: install +install: + install -m 0755 xcrypt /usr/bin/ + install -m 0755 xpass /usr/bin/ + install -m 0755 xjson /usr/bin/ + install -m 0755 xhttp /usr/bin/ + install -m 0755 xtop /usr/bin/ + install -m 0755 xsrc /usr/bin/ + +.PHONY: clean +clean: + $(RM) $(EXECS) $(OBJS) diff --git a/examples/api.c b/examples/api.c new file mode 100644 index 0000000..0f73042 --- /dev/null +++ b/examples/api.c @@ -0,0 +1,179 @@ +/*! + * @file libxutils/examples/events.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of high performance event based non-blocking HTTP server. + * The xUtils library will use poll() or epoll() depending on the operating system. + */ + +#include +#include +#include +#include +#include + +static int g_nInterrupted = 0; + +void signal_callback(int sig) +{ + if (sig == SIGINT) printf("\n"); + g_nInterrupted = 1; +} + +void print_status(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + const char *pStr = XAPI_GetStatusStr(pCtx); + int nFD = pData ? (int)pData->nFD : XSTDERR; + + if (pCtx->eCbType == XAPI_CB_STATUS) + xlogn("%s: fd(%d)", pStr, nFD); + else if (pCtx->eCbType == XAPI_CB_ERROR) + xloge("%s: fd(%d), errno(%d)", pStr, nFD, errno); +} + +int handle_request(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + + xlogn("Received request: fd(%d), buff(%zu)", + (int)pData->nFD, pHandle->dataRaw.nUsed); + + char *pHeader = XHTTP_GetHeaderRaw(pHandle); + if (pHeader != NULL) + { + xlogi("Raw request header:\n\n%s", pHeader); + free(pHeader); + } + + return XAPI_SetEvents(pData, XPOLLOUT); +} + +int write_data(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + pHandle->eType = XHTTP_RESPONSE; + pHandle->nStatusCode = 200; + + if (XHTTP_AddHeader(pHandle, "Server", "xutils/%s", XUtils_VersionShort()) < 0 || + XHTTP_AddHeader(pHandle, "Content-Type", "text/plain") < 0) + { + xloge("Failed to initialize HTTP response: %s", strerror(errno)); + return XSTDERR; + } + + char sBody[XSTR_TINY]; + size_t nLen = xstrncpy(sBody, sizeof(sBody), "Here is your response."); + + if (XHTTP_Assemble(pHandle, (const uint8_t*)sBody, nLen) == NULL) + { + xloge("Failed to assemble HTTP response: %s", strerror(errno)); + return XSTDERR; + } + + xlogn("Sending response: fd(%d), buff(%zu)", + (int)pData->nFD, pHandle->dataRaw.nUsed); + + char *pHeader = XHTTP_GetHeaderRaw(pHandle); + if (pHeader != NULL) + { + xlogi("Raw response header:\n\n%s", pHeader); + free(pHeader); + } + + return XSTDOK; +} + +int http_callback(xhttp_t *pHttp, xhttp_ctx_t *pCbCtx) +{ + xapi_data_t *pData = (xapi_data_t*)pHttp->pUserCtx; + const char *pMessage = (const char*)pCbCtx->pData; + + switch (pCbCtx->eCbType) + { + case XHTTP_STATUS: + xlogd("%s: fd(%d)", pMessage, (int)pData->nFD); + return XSTDOK; + case XHTTP_ERROR: + xloge("%s: fd(%d)", pMessage, (int)pData->nFD); + return XSTDERR; + default: + break; + } + + return XSTDUSR; +} + +int init_data(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + uint16_t nCallbacks = XHTTP_ERROR | XHTTP_STATUS; + XHTTP_SetCallback(pHandle, http_callback, pData, nCallbacks); + xlogn("Accepted connection: fd(%d)", (int)pData->nFD); + return XAPI_SetEvents(pData, XPOLLIN); +} + +int service_callback(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + switch (pCtx->eCbType) + { + case XAPI_CB_ERROR: + case XAPI_CB_STATUS: + print_status(pCtx, pData); + break; + case XAPI_CB_REQUEST: + return handle_request(pCtx, pData); + case XAPI_CB_WRITE: + return write_data(pCtx, pData); + case XAPI_CB_ACCEPTED: + return init_data(pCtx, pData); + case XAPI_CB_CLOSED: + xlogn("Connection closed: fd(%d)", (int)pData->nFD); + break; + case XAPI_CB_COMPLETE: + xlogn("Response sent: fd(%d)", (int)pData->nFD); + return XSTDERR; + case XAPI_CB_INTERRUPT: + if (g_nInterrupted) return XSTDERR; + break; + default: + break; + } + + return XSTDOK; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xlog_timing(XLOG_TIME); + xlog_setfl(XLOG_ALL); + xlog_ident(XTRUE); + + int nSignals[2]; + nSignals[0] = SIGTERM; + nSignals[1] = SIGINT; + XSig_Register(nSignals, 2, signal_callback); + + if (argc < 2) + { + xlog("Usage: %s [address] [port]", argv[0]); + xlog("Example: %s 127.0.0.1 6969", argv[0]); + return 1; + } + + xapi_t api; + api.callback = service_callback; + api.pUserCtx = &api; + + if (XAPI_StartListener(&api, argv[1], atoi(argv[2])) < 0) return XSTDERR; + xlogn("Socket started listen to port: %d", atoi(argv[2])); + + xevent_status_t status; + do status = XAPI_Service(&api, 100); + while (status == XEVENT_STATUS_SUCCESS); + + XAPI_Destroy(&api); + return 0; +} \ No newline at end of file diff --git a/examples/array.c b/examples/array.c new file mode 100644 index 0000000..15ac170 --- /dev/null +++ b/examples/array.c @@ -0,0 +1,161 @@ +/* + * examples/array.c + * + * Copyleft (C) 2018 Sun Dro (a.k.a. kala13x) + * + * Test example source of working with arrays + */ + +#include +#include + +typedef struct { + int key; + const char* str; +} TestStruct; + +void PrintArrayInfo(xarray_t *pArr) +{ + int nArraySize = XArray_Size(pArr); + int nUsedSize = XArray_Used(pArr); + + printf("Array Size(%d), Used Size(%d)\n", nArraySize, nUsedSize); + printf("==================================\n\n"); +} + +void PrintEverything2(xarray_t *pArr) +{ + unsigned int i; + for (i = 0; i < pArr->nUsed; i++) + { + TestStruct *pSt = (TestStruct *)XArray_GetData(pArr, i); + printf("Element %u: %s k(%d)\n", i, pSt->str, pSt->key); + } + + PrintArrayInfo(pArr); +} + +void PrintEverything(xarray_t *pArr) +{ + unsigned int i; + for (i = 0; i < pArr->nUsed; i++) + printf("Element %u: %s\n", i, (char*)XArray_GetData(pArr, i)); + + PrintArrayInfo(pArr); +} + +int ComparatorStrings(const void *pData1, const void *pData2, void *pCtx) +{ + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + return (strcmp((char*)pFirst->pData, (char*)pSecond->pData) > 0); +} + +int ComparatorCostum(const void *pData1, const void *pData2, void *pCtx) +{ + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + + TestStruct *pSt1 = pFirst->pData; + TestStruct *pSt2 = pSecond->pData; + + return strcmp((char*)pSt1->str, (char*)pSt2->str); +} + +int main() +{ + /* Create the xarray_t */ + xarray_t array; + XArray_Init(&array, 5, 0); + printf("Initialized the array\n"); + PrintEverything(&array); + + printf("Adding elements to the array\n"); + XArray_AddData(&array, "first element", strlen("first element") + 1); + XArray_AddData(&array, "second element", strlen("second element") + 1); + XArray_AddData(&array, "third element", strlen("third element") + 1); + PrintEverything(&array); + + printf("Adding another elements to the array\n"); + XArray_AddData(&array, "lorem", strlen("lorem") + 1); + XArray_AddData(&array, "ipsum", strlen("ipsum") + 1); + XArray_AddData(&array, "dolor", strlen("dolor") + 1); + XArray_AddData(&array, "last element", strlen("last element") + 1); + PrintEverything(&array); + + printf("Inserting elements to the array\n"); + XArray_InsertData(&array, 3, "inserted element 1", strlen("inserted element 1") + 1); + XArray_InsertData(&array, 4, "inserted element 2", strlen("inserted element 2") + 1); + XArray_InsertData(&array, 5, "inserted element 3", strlen("inserted element 3") + 1); + PrintEverything(&array); + + printf("Sorting elements by size\n"); + XArray_SortBy(&array, XARRAY_SORTBY_SIZE); + PrintEverything(&array); + + printf("Sorting elements by alphabet\n"); + XArray_BubbleSort(&array, ComparatorStrings, NULL); + PrintEverything(&array); + + printf("Removing elements from the first\n"); + xarray_data_t *pTmpData = XArray_Remove(&array, 0); + XArray_FreeData(pTmpData); + + pTmpData = XArray_Remove(&array, 0); + XArray_FreeData(pTmpData); + + pTmpData = XArray_Remove(&array, 0); + XArray_FreeData(pTmpData); + + PrintEverything(&array); + XArray_Clear(&array); + + printf("Cleared the array\n"); + PrintEverything(&array); + + TestStruct st1 = { .key = 1, .str = "test1"}; + TestStruct st2 = { .key = 2, .str = "test2"}; + TestStruct st3 = { .key = 4, .str = "test3"}; + TestStruct st4 = { .key = 3, .str = "test4"}; + TestStruct st5 = { .key = 5, .str = "test5"}; + + printf("Adding elements to the array\n"); + XArray_AddDataKey(&array, &st1, 0, 1); + XArray_AddDataKey(&array, &st2, 0, 2); + XArray_AddDataKey(&array, &st3, 0, 4); + XArray_AddDataKey(&array, &st4, 0, 3); + XArray_AddDataKey(&array, &st5, 0, 5); + + PrintEverything2(&array); + + printf("Searching element by key\n"); + int nIndex = XArray_SentinelSearch(&array, 4); + if (nIndex >= 0) + { + TestStruct *pData = (TestStruct*)XArray_GetData(&array, nIndex); + if (pData) printf("Found element: %s\n", pData->str); + } + + printf("Sorting elements by alphabet\n"); + XArray_Sort(&array, ComparatorCostum, NULL); + PrintEverything2(&array); + + printf("Sorting elements by key\n"); + XArray_SortBy(&array, XARRAY_SORTBY_KEY); + PrintEverything2(&array); + + printf("Removing element from the first\n"); + pTmpData = XArray_Remove(&array, 0); + XArray_FreeData(pTmpData); + PrintEverything2(&array); + + /* Clear the array and print info */ + XArray_Clear(&array); + printf("Cleared the array\n"); + PrintEverything2(&array); + + /* We have not destructor at C, so clean up memory by ourselves */ + XArray_Destroy(&array); + + return 0; +} \ No newline at end of file diff --git a/examples/events.c b/examples/events.c new file mode 100644 index 0000000..f6fa47c --- /dev/null +++ b/examples/events.c @@ -0,0 +1,303 @@ +/*! + * @file libxutils/examples/events.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of high performance event based non-blocking HTTP server. + * The xUtils library will use poll() or epoll() depending on the operating system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int g_nInterrupted = 0; + +void signal_callback(int sig) +{ + if (sig == 2) printf("\n"); + g_nInterrupted = 1; +} + +void clear_event(xevent_data_t *pEvData) +{ + if (pEvData != NULL) + { + if (pEvData->pContext != NULL) + { + XHTTP_Clear((xhttp_t*)pEvData->pContext); + pEvData->pContext = NULL; + } + + if (pEvData->nFD >= 0 && pEvData->bIsOpen) + { + shutdown(pEvData->nFD, XSHUT_RDWR); + xclosesock(pEvData->nFD); + pEvData->nFD = XSOCK_INVALID; + pEvData->bIsOpen = XFALSE; + } + } +} + +int handle_request(xevent_data_t *pEvData) +{ + xhttp_t *pHandle = (xhttp_t*)pEvData->pContext; + xlogn("Received request: fd(%d), buff(%zu)", (int)pEvData->nFD, pHandle->dataRaw.nUsed); + + pHandle->dataRaw.pData[pHandle->nHeaderLength - 1] = XSTR_NUL; + xlogi("Request header:\n\n%s", (char*)pHandle->dataRaw.pData); + + if (XHTTP_Recycle(pHandle) < 0) + { + xloge("Failed to recycle HTTP handle: %s", strerror(errno)); + pEvData->pContext = NULL; + return XEVENTS_DISCONNECT; + } + + const char *pVer = XUtils_VersionShort(); + pHandle->eType = XHTTP_RESPONSE; + pHandle->nStatusCode = 200; + + if (XHTTP_AddHeader(pHandle, "Server", "xutils/%s", pVer) < 0 || + XHTTP_AddHeader(pHandle, "Content-Type", "text/plain") < 0) + { + xloge("Failed to initialize HTTP response: %s", strerror(errno)); + pEvData->pContext = NULL; + return XEVENTS_DISCONNECT; + } + + char sBody[XSTR_TINY]; + size_t nLen = xstrncpy(sBody, sizeof(sBody), "Here is your response."); + + if (XHTTP_Assemble(pHandle, (const uint8_t*)sBody, nLen) == NULL) + { + xloge("Failed to assemble HTTP response: %s", strerror(errno)); + pEvData->pContext = NULL; + return XEVENTS_DISCONNECT; + } + + xlogn("Sending response: fd(%d), buff(%zu)", + (int)pEvData->nFD, pHandle->dataRaw.nUsed); + + char cSave = pHandle->dataRaw.pData[pHandle->nHeaderLength - 1]; + pHandle->dataRaw.pData[pHandle->nHeaderLength - 1] = XSTR_NUL; + xlogi("Response header:\n\n%s", (char*)pHandle->dataRaw.pData); + pHandle->dataRaw.pData[pHandle->nHeaderLength - 1] = cSave; + + pEvData->pContext = pHandle; + return XEVENTS_CONTINUE; +} + +int read_event(xevents_t *pEvents, xevent_data_t *pEvData) +{ + if (pEvents == NULL || pEvData == NULL) return XEVENTS_DISCONNECT; + xsock_t *pListener = (xsock_t*)pEvents->pUserSpace; + + if (pListener->nFD == pEvData->nFD) + { + xsock_t newSock; + + if (XSock_Accept(pListener, &newSock) == XSOCK_INVALID || + XSock_NonBlock(&newSock, XTRUE) == XSOCK_INVALID) + { + xlogw("%s", XSock_ErrStr(&newSock)); + return XEVENTS_CONTINUE; + } + + xhttp_t *pRequest = XHTTP_Alloc(XHTTP_DUMMY, XSTDNON); + if (pRequest == NULL) + { + xloge("Can not allocate memory for HTTP request: %d", errno); + XSock_Close(&newSock); + return XEVENTS_CONTINUE; + } + + if (XEvents_RegisterEvent(pEvents, pRequest, newSock.nFD, XPOLLIN, 0) == NULL) + { + xloge("Failed to register event for FD: %d (%s)", newSock.nFD, strerror(errno)); + XSock_Close(&newSock); + XHTTP_Clear(pRequest); + return XEVENTS_CONTINUE; + } + + xlogn("Accepted connection: fd(%d)", (int)newSock.nFD); + return XEVENTS_ACCEPT; + } + else + { + xsock_t clientSock; + XSock_Init(&clientSock, XSOCK_TCP_PEER, pEvData->nFD, XTRUE); + + xhttp_t *pHandle = (xhttp_t*)pEvData->pContext; + xhttp_status_t eStatus = XHTTP_Receive(pHandle, &clientSock); + + if (eStatus == XHTTP_COMPLETE) + { + int nStatus = handle_request(pEvData); + if (nStatus != XEVENTS_CONTINUE) return nStatus; + + xevent_status_t eStatus = XEvents_Modify(pEvents, pEvData, XPOLLOUT); + if (eStatus != XEVENT_STATUS_SUCCESS) + { + xloge("%s: %s", XEvents_Status(eStatus), strerror(errno)); + return XEVENTS_DISCONNECT; + } + + return XEVENTS_CONTINUE; + } + else if (eStatus == XHTTP_ERRREAD) + { + const char *pError = XSock_ErrStr(&clientSock); + + if (clientSock.eStatus == XSOCK_EOF) + xlogn("%s (%d)", pError, pEvData->nFD); + else if (clientSock.eStatus != XSOCK_ERR_NONE) + xloge("%s (%s)", pError, strerror(errno)); + + pEvData->bIsOpen = XFALSE; + return XEVENTS_DISCONNECT; + } + else if (eStatus != XHTTP_PARSED && + eStatus != XHTTP_INCOMPLETE) + { + xloge("%s", XHTTP_GetStatusStr(eStatus)); + return XEVENTS_DISCONNECT; + } + + xlogd("RX complete: fd(%d), buff(%zu)", + (int)pEvData->nFD, pHandle->dataRaw.nUsed); + } + + return XEVENTS_CONTINUE; +} + +int write_event(xevents_t *pEvents, xevent_data_t *pEvData) +{ + if (pEvents == NULL || pEvData == NULL) return XEVENTS_DISCONNECT; + xhttp_t *pResponse = (xhttp_t*)pEvData->pContext; + + xbyte_buffer_t *pBuffer = &pResponse->dataRaw; + if (!pBuffer->nUsed) return XEVENTS_DISCONNECT; + + xsock_t socket; + XSock_Init(&socket, XSOCK_TCP_PEER, pEvData->nFD, XTRUE); + + int nSent = XSock_Write(&socket, pBuffer->pData, pBuffer->nUsed); + if (nSent <= 0) + { + xloge("%s (%s)", XSock_ErrStr(&socket), strerror(errno)); + pEvData->bIsOpen = XFALSE; + return XEVENTS_DISCONNECT; + } + + int nDataLeft = XByteBuffer_Advance(pBuffer, nSent); + xlogd("TX complete: fd(%d), len(%d), left(%d)", + (int)pEvData->nFD, nSent, nDataLeft); + + return nDataLeft ? XEVENTS_CONTINUE : XEVENTS_DISCONNECT; +} + +int event_callback(void *events, void* data, XSOCKET fd, int reason) +{ + xevent_data_t *pData = (xevent_data_t*)data; + xevents_t *pEvents = (xevents_t*)events; + + switch(reason) + { + case XEVENT_INTERRUPT: + xlogi("Interrupted by signal"); + if (g_nInterrupted) return XEVENTS_BREAK; + break; + case XEVENT_CLEAR: + xlogn("Closing connection: fd(%d)", (int)fd); + clear_event(pData); + break; + case XEVENT_READ: + xlogd("RX callback: fd(%d)", (int)fd); + return read_event(pEvents, pData); + case XEVENT_WRITE: + xlogd("TX callback: fd(%d)", (int)fd); + return write_event(pEvents, pData); + case XEVENT_HUNGED: + xlogw("Connection hunged: fd(%d)", (int)fd); + return XEVENTS_DISCONNECT; + case XEVENT_CLOSED: + xlogn("Connection closed: fd(%d)", (int)fd); + return XEVENTS_DISCONNECT; + case XEVENT_DESTROY: + xlogi("Service destroyed"); + break; + default: + break; + } + + return XEVENTS_CONTINUE; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xlog_timing(XLOG_TIME); + xlog_setfl(XLOG_ALL); + xlog_ident(XTRUE); + + /* Register interrupt/termination signals */ + int nSignals[2]; + nSignals[0] = SIGTERM; + nSignals[1] = SIGINT; + XSig_Register(nSignals, 2, signal_callback); + + /* Used variables */ + xevent_status_t status; + xevents_t events; + xsock_t socket; + + /* Check valid args */ + if (argc < 2) + { + xlog("Usage: %s [address] [port]", argv[0]); + xlog("Example: %s 127.0.0.1 6969", argv[0]); + return 1; + } + + /* Create server socket */ + XSock_Create(&socket, XSOCK_TCP_SERVER, argv[1], atoi(argv[2])); + if (socket.nFD == XSOCK_INVALID) + { + xloge("%s", XSock_ErrStr(&socket)); + return 1; + } + + xlogi("Socket started listen to port: %d", atoi(argv[2])); + + /* Create event instance */ + status = XEvents_Create(&events, 0, &socket, event_callback, XTRUE); + if (status != XEVENT_STATUS_SUCCESS) + { + xloge("%s", XEvents_Status(status)); + XSock_Close(&socket); + return 1; + } + + /* Add listener socket to the event instance */ + if (XEvents_RegisterEvent(&events, NULL, socket.nFD, XPOLLIN, 0) == NULL) + { + xloge("Failed to register listener event"); + XEvents_Destroy(&events); + XSock_Close(&socket); + return 1; + } + + /* Main service loop */ + while (status == XEVENT_STATUS_SUCCESS) + status = XEvents_Service(&events, 100); + + XEvents_Destroy(&events); + return 0; +} \ No newline at end of file diff --git a/examples/example.json b/examples/example.json new file mode 100644 index 0000000..ee68209 --- /dev/null +++ b/examples/example.json @@ -0,0 +1,43 @@ +[ + { + "bigobj": { + "nullItem": null, + "testobj1": { + "testobj2": { + "testbool1": true, + "teststr1": "example string 1" + }, + "testfloat1": 60.900002, + "testint1": 66 + }, + "testobj3": { + "testarray2": [ + true, + 69, + 69.900002 + ], + "testbool2": true, + "teststr2": "example string 2" + }, + "testarray1": [ + { + "testint2": -67, + "testfloat2": 61.900002 + }, + { + "testint2": 68, + "testfloat2": 62.900002 + }, + { + "testint2": -69, + "testfloat2": 63.969690 + } + ], + "emptyObject": {}, + "emptyArray": [], + "emptyArrObj": [ + {} + ] + } + } +] diff --git a/examples/files.c b/examples/files.c new file mode 100644 index 0000000..dba2d10 --- /dev/null +++ b/examples/files.c @@ -0,0 +1,59 @@ +/* + * examples/files.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * File and folder operations. + */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + XLog_Init("test", XLOG_ALL, 0); + + /* Check args */ + if (argc < 3) + { + printf("Usage: %s [source] [destination]\n", argv[0]); + printf("Example: %s src.txt dest.txt\n", argv[0]); + return 0; + } + + xfile_t srcFile, dstFile; + XFile_Open(&srcFile, argv[1], NULL, NULL); + XFile_Open(&dstFile, argv[2], "cwt", NULL); + + int nRet = XFile_Copy(&srcFile, &dstFile); + if (nRet < 0) XLog_Error("Can not copy file: %d", errno); + + XFile_Close(&srcFile); + XFile_Close(&dstFile); + + XFile_Open(&srcFile, argv[1], NULL, NULL); + XLog_Debug("Lines: %d", XFile_GetLineCount(&srcFile)); + + XFile_Seek(&srcFile, 0, SEEK_SET); + char sLine[2048]; + int nLineNo = 2; + + if (XFile_ReadLine(&srcFile, sLine, sizeof(sLine), nLineNo) > 0) + XLog_Debug("Line (%d): %s", nLineNo, sLine); + + XFile_Close(&srcFile); + + xdir_t dir; + XDir_Open(&dir, "./"); + + char sFile[256]; + while(XDir_Read(&dir, sFile, sizeof(sFile)) > 0) + { + XLog_Info("Found file: %s", sFile); + sFile[0] = '\0'; + } + + XDir_Close(&dir); + return 0; +} \ No newline at end of file diff --git a/examples/json.c b/examples/json.c new file mode 100644 index 0000000..36ab849 --- /dev/null +++ b/examples/json.c @@ -0,0 +1,259 @@ +/* + * examples/json.c + * + * Copyleft (C) 2018 Sun Dro (a.k.a. kala13x) + * + * Example file for working with json. + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + xlog_defaults(); + xlog_cfg_t cfg; + + xlog_get(&cfg); + cfg.nUseHeap = 1; + cfg.nFlush = 1; + xlog_set(&cfg); + + if (argc < 2) + { + xloge("Please specify json file"); + return 1; + } + +//////////////////////////////////////////////////////// +// CREATE JSON FILE +//////////////////////////////////////////////////////// + + xjson_obj_t *pRootArray = XJSON_NewArray(NULL, 0); + if (pRootArray != NULL) + { + xjson_obj_t *pRootObject = XJSON_NewObject(NULL, 0); + if (pRootObject != NULL) + { + xjson_obj_t *pBigObj = XJSON_NewObject("bigobj", 0); + if (pBigObj != NULL) + { + xjson_obj_t *pTestObj1 = XJSON_NewObject("testobj1", 0); + if (pTestObj1 != NULL) + { + xjson_obj_t *pTestObj2 = XJSON_NewObject("testobj2", 0); + if (pTestObj2 != NULL) + { + XJSON_AddObject(pTestObj2, XJSON_NewBool("testbool1", 1)); + XJSON_AddObject(pTestObj2, XJSON_NewString("emptyString", "")); + XJSON_AddObject(pTestObj2, XJSON_NewString("teststr1", "example string 1")); + XJSON_AddObject(pTestObj1, pTestObj2); + } + + XJSON_AddObject(pTestObj1, XJSON_NewFloat("testfloat1", 60.900002)); + XJSON_AddObject(pTestObj1, XJSON_NewInt("testint1", 66)); + XJSON_AddObject(pBigObj, pTestObj1); + } + + xjson_obj_t *pTestObj3 = XJSON_NewObject("testobj3", 0); + if (pTestObj3 != NULL) + { + xjson_obj_t *pTestArr2 = XJSON_NewArray("testarray2", 0); + if (pTestArr2 != NULL) + { + XJSON_AddObject(pTestArr2, XJSON_NewBool(NULL, 1)); + XJSON_AddObject(pTestArr2, XJSON_NewInt(NULL, 69)); + XJSON_AddObject(pTestArr2, XJSON_NewFloat(NULL, 69.900002)); + XJSON_AddObject(pTestObj3, pTestArr2); + } + + XJSON_AddObject(pTestObj3, XJSON_NewString("teststr2", "example string 2")); + XJSON_AddObject(pTestObj3, XJSON_NewBool("testbool2", 1)); + XJSON_AddObject(pBigObj, pTestObj3); + } + + xjson_obj_t *pTestArr1 = XJSON_NewArray("testarray1", 0); + if (pTestArr1 != NULL) + { + xjson_obj_t *pArrObj = XJSON_NewObject(NULL, 0); + if (pArrObj != NULL) + { + XJSON_AddObject(pArrObj, XJSON_NewFloat("testfloat2", 61.900002)); + XJSON_AddObject(pArrObj, XJSON_NewInt("testint2", -67)); + XJSON_AddObject(pTestArr1, pArrObj); + } + + pArrObj = XJSON_NewObject(NULL, 0); + if (pArrObj != NULL) + { + XJSON_AddObject(pArrObj, XJSON_NewFloat("testfloat2", 62.900002)); + XJSON_AddObject(pArrObj, XJSON_NewInt("testint2", 68)); + XJSON_AddObject(pTestArr1, pArrObj); + } + + pArrObj = XJSON_NewObject(NULL, 0); + if (pArrObj != NULL) + { + XJSON_AddObject(pArrObj, XJSON_NewFloat("testfloat2", 63.96969002)); + XJSON_AddObject(pArrObj, XJSON_NewInt("testint2", -69)); + XJSON_AddObject(pTestArr1, pArrObj); + } + + XJSON_AddObject(pBigObj, pTestArr1); + } + + XJSON_AddObject(pBigObj, XJSON_NewObject("emptyObject", 0)); + XJSON_AddObject(pBigObj, XJSON_NewArray("emptyArray", 0)); + XJSON_AddObject(pBigObj, XJSON_NewNull("nullItem")); + + xjson_obj_t *pArrObj = XJSON_NewArray("emptyArrObj", 0); + if (pArrObj != NULL) + { + XJSON_AddObject(pArrObj, XJSON_NewObject(NULL, 0)); + XJSON_AddObject(pBigObj, pArrObj); + } + + XJSON_AddObject(pRootObject, pBigObj); + } + + XJSON_AddObject(pRootArray, pRootObject); + } + + xjson_writer_t linter; + XJSON_InitWriter(&linter, NULL, 1); // Dynamic allocation + linter.nTabSize = 4; // Enable linter and set tab size (4 spaces) + + /* Dump objects directly */ + if (XJSON_WriteObject(pRootArray, &linter)) + { + xfile_t *pFile = XFile_New(argv[1], "w", NULL); + if (pFile != NULL) + { + XFile_Write(pFile, linter.pData, linter.nLength); + XFile_Clean(pFile); + } + + XJSON_DestroyWriter(&linter); + } + + XJSON_FreeObject(pRootArray); + } + +//////////////////////////////////////////////////////// +// PARSE JSON FILE +//////////////////////////////////////////////////////// + + xjson_t json; + size_t nSize; + + char *pBuffer = (char*)XPath_Load(argv[1], &nSize); + if (pBuffer == NULL) + { + xloge("Can't read file: %s (%s)", + argv[1], strerror(errno)); + return 0; + } + + if (!XJSON_Parse(&json, pBuffer, nSize)) + { + char sError[XMSG_MID]; + XJSON_GetErrorStr(&json, sError, sizeof(sError)); + xloge("Failed to parse JSON: %s", sError); + + XJSON_Destroy(&json); + free(pBuffer); + return 0; + } + + xjson_obj_t *pBigObj = XJSON_GetObject(XJSON_GetArrayItem(json.pRootObj, 0), "bigobj"); + if (pBigObj != NULL) + { + xjson_obj_t *pTestObj1 = XJSON_GetObject(pBigObj, "testobj1"); + if (pTestObj1 != NULL) + { + xjson_obj_t *pIntObj = XJSON_GetObject(pTestObj1, "testint1"); + if (pIntObj != NULL) xlog("testint1: %d", XJSON_GetInt(pIntObj)); + + xjson_obj_t *pFloatObj = XJSON_GetObject(pTestObj1, "testfloat1"); + if (pFloatObj != NULL) xlog("testfloat1: %f", XJSON_GetFloat(pFloatObj)); + + xjson_obj_t *pTestObj2 = XJSON_GetObject(pTestObj1, "testobj2"); + if (pTestObj2 != NULL) + { + xjson_obj_t *pBoolObj = XJSON_GetObject(pTestObj2, "testbool1"); + if (pBoolObj != NULL) xlog("testbool1: %s", XJSON_GetBool(pBoolObj) ? "true" : "false"); + + xjson_obj_t *pStrObj = XJSON_GetObject(pTestObj2, "teststr1"); + if (pStrObj != NULL) xlog("teststr1: %s", XJSON_GetString(pStrObj)); + } + } + + xjson_obj_t *pArrObj = XJSON_GetObject(pBigObj, "testarray1"); + if (pArrObj != NULL) + { + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pArrObj, i); + if (pArrItemObj != NULL) + { + xjson_obj_t *pArrItem = XJSON_GetObject(pArrItemObj, "testint2"); + if (pArrItem != NULL) xlog("testint2: %d", XJSON_GetInt(pArrItem)); + + pArrItem = XJSON_GetObject(pArrItemObj, "testfloat2"); + if (pArrItem != NULL) xlog("testfloat2: %f", XJSON_GetFloat(pArrItem)); + } + } + } + + xjson_obj_t *pTestObj3 = XJSON_GetObject(pBigObj, "testobj3"); + if (pTestObj3 != NULL) + { + xjson_obj_t *pArrObj = XJSON_GetObject(pTestObj3, "testarray2"); + if (pArrObj != NULL) + { + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pArrObj, i); + if (pArrItemObj != NULL) xlog("testarray2: %s", (char*)pArrItemObj->pData); + } + } + + xjson_obj_t *pBoolObj = XJSON_GetObject(pTestObj3, "testbool2"); + if (pBoolObj != NULL) xlog("testbool2: %s", XJSON_GetBool(pBoolObj) ? "true" : "false"); + + xjson_obj_t *pStringObj = XJSON_GetObject(pTestObj3, "teststr2"); + if (pStringObj != NULL) xlog("teststr2: %s", XJSON_GetString(pStringObj)); + } + } + +//////////////////////////////////////////////////////// +// MINIFY JSON FILE +//////////////////////////////////////////////////////// + + /* Dump json from xjson_t object */ + if (XJSON_Write(&json, pBuffer, nSize)) + xlog("\nMinify:\n%s\n", pBuffer); + +//////////////////////////////////////////////////////// +// LINT JSON FILE +//////////////////////////////////////////////////////// + + xjson_writer_t writer; + XJSON_InitWriter(&writer, NULL, nSize); // Dynamic allocation + writer.nTabSize = 4; // Enable linter and set tab size (4 spaces) + + /* Dump objects directly */ + if (XJSON_WriteObject(json.pRootObj, &writer)) + { + xlog("Lint:\n%s\n", writer.pData); + XJSON_DestroyWriter(&writer); + } + + XJSON_Destroy(&json); + free(pBuffer); + return 0; +} \ No newline at end of file diff --git a/examples/list.c b/examples/list.c new file mode 100644 index 0000000..779791b --- /dev/null +++ b/examples/list.c @@ -0,0 +1,107 @@ +/* + * examples/list.c + * + * Copyleft (C) 2020 Sun Dro (a.k.a. kala13x) + * + * Example file for working with linked list. + */ + +#include +#include + +typedef struct { + char *pName; + int nNumber; +} UserData; + +void clearCallback(void *pUser, void *pData) +{ + xlogd("clearing: %s", (char*)pData); + free(pData); +} + +int searchCallback(void *pUserPtr, xlist_t *pNode) +{ + char *pString = (char*)pNode->pData; + char *pSearch = (char*)pUserPtr; + return !strncmp(pSearch, pString, strlen(pSearch)); +} + +int costumOpreation(void *pUserPtr, xlist_t *pNode) +{ + UserData *pUsrData = (UserData*)pUserPtr; + char *pString = (char*)pNode->pData; + + if (!strncmp(pUsrData->pName, pString, strlen(pUsrData->pName))) + { + xlogd("renaming: %s -> %d", pString, pUsrData->nNumber); + snprintf(pString, pNode->nSize, "%d", pUsrData->nNumber); + return -1; /* Stop search*/ + } + + return 0; +} + +void displayAllNodes(xlist_t *pNode) +{ + xlist_t *pHead = XList_GetHead(pNode); + while (pHead != NULL) + { + xlogi("node: %s", pHead->pData); + pHead = pHead->pNext; + } +} + +int main(int argc, char *argv[]) +{ + xlog_defaults(); + xlog_timing(XLOG_TIME); + xlog_enable(XLOG_DEBUG | XLOG_INFO); + xlog_separator("|"); + xlog_ident(1); + + char *pFirst = strdup("first node"); + char *pSecond = strdup("second node"); + char *pThird = strdup("third node"); + char *pFourth = strdup("fourth node"); + char *pFifth = strdup("fifth node"); + + /* Create linked list and append new nodes */ + xlist_t *pNode = XList_New(pSecond, strlen(pSecond), clearCallback, NULL); + pNode = XList_PushBack(pNode, pThird, strlen(pThird)); + pNode = XList_PushBack(pNode, pFourth, strlen(pFourth)); + pNode = XList_PushBack(pNode, pFifth, strlen(pFifth)); + XList_PushFront(pNode, pFirst, strlen(pFirst)); + + /* Search third node */ + xlist_t *pFound = XList_Search(pNode, (void*)"third", searchCallback); + if (pFound != NULL) xlogd("found node: %s", (char*)pFound->pData); + + /* Iterate while list and print data */ + displayAllNodes(pNode); + + /* Search and remove fourth node */ + XList_Remove(pNode, (void*)"fourth", searchCallback); + + /* Search and rename nodes */ + UserData userData; + userData.pName = "first"; + userData.nNumber = 1; + XList_Search(pNode, &userData, costumOpreation); + userData.pName = "second"; + userData.nNumber = 2; + XList_Search(pNode, &userData, costumOpreation); + userData.pName = "third"; + userData.nNumber = 3; + XList_Search(pNode, &userData, costumOpreation); + userData.pName = "fifth"; + userData.nNumber = 5; + XList_Search(pNode, &userData, costumOpreation); + + /* Iterate while list and print data */ + displayAllNodes(pNode); + + /* Free all allocated data */ + XList_Clear(pNode); + return 0; +} \ No newline at end of file diff --git a/examples/ntp.c b/examples/ntp.c new file mode 100644 index 0000000..49f3a93 --- /dev/null +++ b/examples/ntp.c @@ -0,0 +1,31 @@ +/*! + * @file libxutils/examples/ntp.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * @brief Implementation of NTP functionality. + */ + +#include +#include +#include +#include + +#define NTP_SERVER "europe.pool.ntp.org" + +int main() +{ + xlog_defaults(); + xtime_t time; + + if (XNTP_GetDate(NTP_SERVER, 0, &time) > 0) + { + char sTime[32]; + XTime_ToHstr(&time, sTime, sizeof(sTime)); + xlog("Received NTP Date: %s", sTime); + return 0; + } + + xloge("Can not get time from NTP server: %s", NTP_SERVER); + return 1; +} diff --git a/examples/run.sh b/examples/run.sh new file mode 100755 index 0000000..1357f19 --- /dev/null +++ b/examples/run.sh @@ -0,0 +1,20 @@ +export XUTILS_USE_SSL=y + +cd .. && ./build.sh smake +sudo make install +cd examples + +if [ ! -z "$1" ]; then + FILE=${1/%.c/} + make clean + make $FILE + + valgrind --leak-check=full \ + --show-leak-kinds=all \ + --show-error-list=yes \ + --track-origins=yes \ + ./$FILE $2 $3 $4 $5 $6 $7 $8 $9 +else + make clean + make +fi diff --git a/examples/server.c b/examples/server.c new file mode 100644 index 0000000..1772a54 --- /dev/null +++ b/examples/server.c @@ -0,0 +1,114 @@ +/* + * examples/server.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * Simplest echo server based on XSocket. + */ + +#include +#include +#include +#include +#include +#include +#include + +void packet_callback(xpacket_t *pPacket, uint8_t nType) +{ + if (nType == XPACKET_CB_UPDATE) + { + xjson_obj_t *pPayloadObj = XJSON_GetOrCreateObject(pPacket->pHeaderObj, "payload", 1); + if (pPayloadObj != NULL) + { + XJSON_AddBool(pPayloadObj, "crypted", 1); + XJSON_AddString(pPayloadObj, "payloadType", "text/plain"); + } + + xjson_obj_t *pInfoObj = XJSON_GetOrCreateObject(pPacket->pHeaderObj, "cmd", 1); + if (pInfoObj != NULL) + { + XJSON_AddString(pInfoObj, "cmdType", "shell"); + XJSON_AddString(pInfoObj, "command", "systemctl"); + + xjson_obj_t *pArrObj = XJSON_GetOrCreateArray(pInfoObj, "arguments", 1); + if (pArrObj != NULL) + { + XJSON_AddString(pArrObj, NULL, "status"); + XJSON_AddString(pArrObj, NULL, "sshd"); + } + } + } +} + +int main(int argc, char *argv[]) +{ + int cpus[2] = { 0, 1 }; /* Run only on the first two CPU */ + XCPU_SetAffinity(cpus, 2, XCPU_CALLER_PID); + + xlog_defaults(); + char buf[1024]; + + if (argc <= 2) + { + xlog("Usage: %s [address] [port]\n", argv[0]); + xlog("Example: %s 127.0.0.1 6969\n", argv[0]); + return 1; + } + + xsock_t sock; + if (XSock_Create(&sock, XSOCK_TCP_SERVER, argv[1], atoi(argv[2])) == XSOCK_INVALID) + { + XLog_Error("%s", XSock_ErrStr(&sock)); + return 1; + } + + xlogi("Socket started listen to port: %d", atoi(argv[2])); + while (sock.nFD != XSOCK_INVALID) + { + xsock_t sock2; + buf[0] = '\0'; + + if (XSock_Accept(&sock, &sock2) == XSOCK_INVALID) + { + XLog_Error("%s", XSock_ErrStr(&sock)); + continue; + } + + int nRead = XSock_Read(&sock2, buf, sizeof(buf)); + if (nRead > 0) + { + buf[nRead] = '\0'; + xlogi("Received: %s", buf); + + const char *pTestString = "test_password"; + size_t nLength = strlen(pTestString); + + char *pCrypted = XCrypt_MD5((const uint8_t*)pTestString, nLength); + if (pCrypted == NULL) + { + xloge("Failed to encrypt payload"); + XSock_Close(&sock2); + break; + } + + xpacket_t packet; + XPacket_Init(&packet, (uint8_t*)pCrypted, XMD5_LENGTH); + packet.header.eType = XPACKET_TYPE_MULTY; + packet.header.nTimeStamp = XTime_GetUsec(); + packet.header.nSessionID = rand(); + packet.header.nPacketID = rand(); + packet.callback = packet_callback; + XPacket_Assemble(&packet); + + XSock_Send(&sock2, packet.rawData.pData, packet.rawData.nUsed); + XPacket_Clear(&packet); + free(pCrypted); + } + + XSock_Close(&sock2); + } + + XSock_Close(&sock); + return 0; +} \ No newline at end of file diff --git a/examples/statcov.c b/examples/statcov.c new file mode 100644 index 0000000..c7a44ec --- /dev/null +++ b/examples/statcov.c @@ -0,0 +1,128 @@ +/*! + * @file libxutils/examples/statcov.c + * + * 2020-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Parse and print COVID-19 case + * statistics from https://stopcov.ge/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STOPCOV_LINK "https://stopcov.ge/" +#define XSPACE_CHAR " " +#define XSTART_POS "numver\">" +#define XEND_POS "" +#define XTAB_SIZE 4 + +typedef struct { + int nQuarantined; + int nSupervision; + int nConfirmed; + int nRecovered; + int nDeaths; +} covid_cases_t; + +int COVID_ParseCase(const char *pSource, size_t nLength, const char *pCase) +{ + int nPosit = xstrnsrc(pSource, nLength, pCase, 0); + if (nPosit < 0) return XSTDNON; + + const char *pOffset = &pSource[nPosit]; + XCHAR(sNumber, XSTR_MIN); + XCHAR(sToken, XSTR_MIN); + + size_t i, nLen = xstrncuts(XARG_SIZE(sToken), pOffset, XSTART_POS, XEND_POS); + if (!nLen) return XSTDERR; + + xarray_t *pArr = xstrsplit(sToken, XSPACE_CHAR); + if (pArr == NULL) return atoi(sToken); + + for (i = 0; i < pArr->nUsed; i++) + { + const char *pData = (const char*)XArray_GetData(pArr, i); + if (pData != NULL) xstrncatf(sNumber, XSTR_AVAIL(sNumber), "%s", pData); + } + + XArray_Destroy(pArr); + return atoi(sNumber); +} + +int COVID_ParseResponse(xhttp_t *pHttp, covid_cases_t *pCovCases) +{ + if (pHttp->nStatusCode != 200) + { + pHttp->dataRaw.pData[pHttp->nHeaderLength - 1] = '\0'; + xlogi("Response header:\n%s\n", pHttp->dataRaw.pData); + return XSTDNON; + } + + const char *pContent = (const char *)XHTTP_GetBody(pHttp); + size_t nBodyLen = XHTTP_GetBodySize(pHttp); + + pCovCases->nConfirmed = COVID_ParseCase(pContent, nBodyLen, "დადასტურებული შემთხვევა"); + pCovCases->nRecovered = COVID_ParseCase(pContent, nBodyLen, "მათ შორის გამოჯანმრთელებული"); + pCovCases->nQuarantined = COVID_ParseCase(pContent, nBodyLen, "კარანტინის რეჟიმში"); + pCovCases->nSupervision = COVID_ParseCase(pContent, nBodyLen, "მეთვალყურეობის ქვეშ"); + pCovCases->nDeaths = COVID_ParseCase(pContent, nBodyLen, "მათ შორის გარდაცვლილი"); + return pCovCases->nConfirmed; +} + +int COVID_PrintCases(covid_cases_t *pCovCases) +{ + xjson_obj_t *pRootObject = XJSON_NewObject(NULL, 0); + if (pRootObject != NULL) + { + XJSON_AddInt(pRootObject, "confirmed", pCovCases->nConfirmed); + XJSON_AddInt(pRootObject, "recovered", pCovCases->nRecovered); + XJSON_AddInt(pRootObject, "quarantined", pCovCases->nQuarantined); + XJSON_AddInt(pRootObject, "supervision", pCovCases->nSupervision); + XJSON_AddInt(pRootObject, "deaths", pCovCases->nDeaths); + + xjson_writer_t linter; + XJSON_InitWriter(&linter, NULL, XSTR_MIN); + linter.nTabSize = XTAB_SIZE; + + if (XJSON_WriteObject(pRootObject, &linter)) xlog("%s", linter.pData); + else xloge("Failed to write JSON object (%s)", strerror(errno)); + + XJSON_DestroyWriter(&linter); + XJSON_FreeObject(pRootObject); + return XSTDOK; + } + + xloge("Failed to allocate memory for JSON obj: %s", strerror(errno)); + return XSTDERR; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xlog_setfl(XLOG_ALL); + + xhttp_status_t stat; + covid_cases_t cases; + xhttp_t hdr; + + stat = XHTTP_SoloPerform(&hdr, XHTTP_GET, STOPCOV_LINK, NULL, 0); + if (stat != XHTTP_COMPLETE) + { + xloge("%s", XHTTP_GetStatusStr(stat)); + XHTTP_Clear(&hdr); + return 1; + } + + if (COVID_ParseResponse(&hdr, &cases) > 0) COVID_PrintCases(&cases); + else xloge("Response does not contain COVID-19 case statistics"); + + XHTTP_Clear(&hdr); + XSock_DeinitSSL(); + return 0; +} \ No newline at end of file diff --git a/examples/strings.c b/examples/strings.c new file mode 100644 index 0000000..b3f60dc --- /dev/null +++ b/examples/strings.c @@ -0,0 +1,210 @@ +/* + * examples/strings.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * Example file for working with strings. + */ + +#include +#include +#include +#include + +#define UPPER_STRING "TEST STRING WITH UPPER CASE" +#define LOVER_STRING "test string with lower case" + +int main() +{ + xlog_defaults(); + + char *ptest = xstracpy("the very %s test %s %d %s %.2f", "first", "string", 1, "2", 3.0); + if (ptest != NULL) + { + // test + xlog_cfg_t cfg; + xlog_get(&cfg); + cfg.nUseHeap = 1; + xlog_set(&cfg); + + xlog("Test: %s", ptest); + free(ptest); + + cfg.nUseHeap = 0; + xlog_set(&cfg); + } + + + char str1[128], str2[128]; + xlog("Initial strings: 1(%s) and 2(%s)", LOVER_STRING, UPPER_STRING); + + char *ptr = xstrrep(UPPER_STRING, "UPPER", "LOWER"); + xlog("Replaced word \"UPPER\" with \"LOWER\" in string 2: %s", ptr); + + xstrncase(str1, sizeof(str1), XSTR_LOWER, ptr); + xlog("Changed from upper to lower case 2: %s", str1); + free(ptr); + + ptr = xstrrep(LOVER_STRING, "lower", "upper"); + xlog("Replaced word \"lower\" with \"upper\" in string 1: %s", ptr); + + xstrncase(str2, sizeof(str2), XSTR_UPPER, ptr); + xlog("Changed from lower to upper case 1: %s", str2); + free(ptr); + + int i, nAvail = xstrncatf(str1, XSTR_AVAIL(str1), "(2) and (1)"); + if (nAvail > 0) xstrncatf(str1, nAvail, str2); + + char colorized[128]; + xstrnclr(colorized, sizeof(colorized), XSTR_CLR_GREEN, str1); + xlog("Colorized output: %s", colorized); + + char sToken[32]; + int nNext = 0; + + while((nNext = xstrntok(sToken, sizeof(sToken), str1, nNext, " ")) >= 0) + { + xlog("Token: %s", sToken); + if (!nNext) break; + } + + xarray_t *pArr = xstrsplit("test.string.for.split", "."); + if (pArr != NULL) + { + for (i = 0; i < pArr->nUsed; i++) + { + char *pSplit = XArray_GetData(pArr, i); + xlog("xstrsplit: %s", pSplit); + } + + XArray_Destroy(pArr); + } + + // XString + xstring_t string; + XString_Init(&string, 1, 1); + + XString_Append(&string, "raise "); + XString_Append(&string, "your "); + XString_Append(&string, "arms"); + xlog(string.pData); + + XString_Remove(&string, 6, 5); + xlog(string.pData); + + XString_Insert(&string, 6, "your ", 5); + xlog(string.pData); + + XString_Append(&string, " to the big black sky"); + XString_Add(&string, "...", 3); + xlog(string.pData); + + XString_Delete(&string, 36, 3); + xlog(string.pData); + + XString_InsertFmt(&string, 36, " so whole universe will glow"); + xlog(string.pData); + + XString_Advance(&string, 40); + xlog(string.pData); + + if (strlen(string.pData) != string.nLength) + xloge("should not happen: %zu/%zu", strlen(string.pData), string.nLength); + + xstring_t string2; + XString_Copy(&string2, &string); + xlog(string2.pData); + + XString_ChangeCase(&string2, XSTR_UPPER); + xlog(string2.pData); + + XString_Case(&string2, XSTR_LOWER, 6, 8); + xlog(string2.pData); + + int nPosit = XString_Search(&string2, 0, "universe"); + if (nPosit >= 0) + { + XString_Case(&string2, XSTR_UPPER, nPosit, 8); + xlog(string2.pData); + + XString_Color(&string2, XSTR_CLR_BLUE, nPosit, 8); + xlog(string2.pData); + + XString_Delete(&string2, 6, strlen(XSTR_CLR_BLUE)); + XString_Delete(&string2, 14, strlen(XSTR_FMT_RESET)); + xlog(string2.pData); + } + + XString_ChangeColor(&string2, XSTR_CLR_BLUE); + xlog(string2.pData); + + XString_Replace(&string2, "UNIVERSE", "<>"); + xlog(string2.pData); + + xstring_t tok; + XString_Init(&tok, 32, 0); + nNext = 0; + + while((nNext = XString_Token(&string, &tok, nNext, " ")) >= 0) + { + xlog("Token: %s", tok.pData); + if (!nNext) break; + } + + XString_Clear(&tok); + + pArr = XString_SplitStr(&string, " "); + if (pArr != NULL) + { + for (i = 0; i < pArr->nUsed; i++) + { + xstring_t *pSplit = XArray_GetData(pArr, i); + xlog("Split: %s", pSplit->pData); + } + + XArray_Destroy(pArr); + } + + xstring_t sub; + XString_SubStr(&string, &sub, 6, 8); + xlog(sub.pData); + + XString_Clear(&sub); + XString_Clear(&string2); + + xstring_t *pNewStr = XString_FromFmt("new string"); + xlog(pNewStr->pData); + XString_Clear(pNewStr); + + pNewStr = XString_From("new string2", strlen("new string2")); + xlog(pNewStr->pData); + XString_Clear(pNewStr); + + pNewStr = XString_SubNew(&string, 6, 8); + xlog(pNewStr->pData); + XString_Clear(pNewStr); + + pNewStr = XString_CutNew(&string, "whole ", " will"); + if (pNewStr) xlog(pNewStr->pData); + XString_Clear(pNewStr); + + xstring_t substring; + XString_CutSub(&string, &substring, "whole ", " will"); + if (substring.nLength) xlog(substring.pData); + XString_Clear(&substring); + + pArr = XString_Split("test.string.for.split", "."); + if (pArr != NULL) + { + for (i = 0; i < pArr->nUsed; i++) + { + xstring_t *pSplit = XArray_GetData(pArr, i); + xlog("Split2: %s", pSplit->pData); + } + + XArray_Destroy(pArr); + } + + XString_Clear(&string); + return 0; +} diff --git a/examples/thread.c b/examples/thread.c new file mode 100644 index 0000000..5b15fd1 --- /dev/null +++ b/examples/thread.c @@ -0,0 +1,49 @@ +/* + * examples/thread.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * Example file of useing threads. + */ + + +#include +#include + +void* my_thread(void *arg) +{ + int *n = (int*)arg; + printf("Argument is: %d\n", *n); + return NULL; +} + +int myTask(void *pCtx) +{ + printf("My task function with interval\n"); + return 0; +} + +int main() +{ + xtask_t task; + XTask_Start(&task, myTask, NULL, 10000); + + xthread_t thread[2]; + int arg = 5; + int arg1 = 5; + + /* First way (undetached) */ + XThread_Init(&thread[0]); + thread[0].pArgument = &arg; + thread[0].functionCb = my_thread; + XThread_Run(&thread[0]); + XThread_Join(&thread[0]); + + /* Second way */ + XThread_Create(&thread[1], my_thread, &arg1, 1); + + /* Stop working */ + XTask_Stop(&task, 10000); + + return 0; +} diff --git a/examples/xcrypt.c b/examples/xcrypt.c new file mode 100644 index 0000000..7151ef8 --- /dev/null +++ b/examples/xcrypt.c @@ -0,0 +1,349 @@ +/*! + * @file libxutils/examples/xcrpt.c + * + * This source is part of "libxutils" project + * 2015-2022 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Crypt or Decrypt input file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XCRYPT_VER_MAX 0 +#define XCRYPT_VER_MIN 1 +#define XCRYPT_BUILD_NUM 18 + +#define XCRYPT_HEX_COLUMNS 16 +#define XAES_KEY_LENGTH 128 +extern char *optarg; + +typedef struct +{ + char sCiphers[XSTR_MIN]; + char sOutput[XPATH_MAX]; + char sInput[XPATH_MAX]; + char sText[XSTR_MID]; + + size_t nAESKeyLen; + xbool_t bDecrypt; + xbool_t bForce; + xbool_t bPrint; + xbool_t bHex; +} xcrypt_args_t; + +static xbool_t XCrypt_DecriptSupport(xcrypt_chipher_t eCipher) +{ + switch (eCipher) + { + case XC_AES: + case XC_HEX: + case XC_XOR: + case XC_BASE64: + case XC_CASEAR: + case XC_REVERSE: + return XTRUE; + case XC_MD5: + case XC_CRC32: + case XC_SHA256: + return XFALSE; + default: + break; + } + + return XFALSE; +} + +static char *XCrypt_WhiteSpace(const int nLength) +{ + static char sRetVal[XSTR_MIN]; + xstrnul(sRetVal); + int i = 0; + + int nLen = XSTD_MIN(nLength, sizeof(sRetVal) - 1); + for (i = 0; i < nLen; i++) sRetVal[i] = ' '; + + sRetVal[i] = '\0'; + return sRetVal; +} + +static void XCrypt_DisplayUsage(const char *pName) +{ + int nLength = strlen(pName) + 6; + + xlog("=========================================================="); + xlog(" Crypt/Decrypt file or text - v%d.%d build %d (%s)", + XCRYPT_VER_MAX, XCRYPT_VER_MIN, XCRYPT_BUILD_NUM, __DATE__); + xlog("=========================================================="); + + xlog("Usage: %s [-c ] [-i ] [-o ]", pName); + xlog(" %s [-t ] [-a] [-d] [-f] [-h] [-p] [-v]\n", XCrypt_WhiteSpace(nLength)); + + xlog("Options are:"); + xlog(" -c # Encrypt/Decrypt ciphers (%s*%s)", XSTR_CLR_RED, XSTR_FMT_RESET); + xlog(" -i # Input file path to encrtypt/decrypt"); + xlog(" -o # Output file path to write data"); + xlog(" -t # Text to encrtypt/decrypt"); + xlog(" -a # AES key length (default: 128)"); + xlog(" -d # Decryption mode"); + xlog(" -f # Force overwrite output"); + xlog(" -h # Display output as a HEX"); + xlog(" -p # Printf output to stdout"); + xlog(" -v # Version and usage\n"); + + xlog("Supported ciphers:"); + xlog(" aes"); + xlog(" hex"); + xlog(" md5"); + xlog(" xor"); + xlog(" crc32"); + xlog(" crc32b"); + xlog(" casear"); + xlog(" base64"); + xlog(" sha256"); + xlog(" reverse\n"); + + xlog("Examples:"); + xlog("%s -c aes -i rawFile.txt -o crypted.bin", pName); + xlog("%s -dc aes -i crypted.bin -o decrypted.txt", pName); + xlog("%s -dc hex:aes -i crypted.txt -o decrypted.bin\n", pName); +} + +static xbool_t XCrypt_GetKey(xcrypt_args_t *pArgs, xcrypt_key_t *pKey) +{ + const char *pCipher = XCrypt_GetCipherStr(pKey->eCipher); + printf("Enter keyword for the cipher '%s': ", pCipher); + + if (!XCLI_GetPass(NULL, pKey->sKey, sizeof(pKey->sKey))) + { + xloge("Failed to read master keyword: %d", errno); + return XFALSE; + } + + if (!pArgs->bDecrypt || pArgs->bForce) + { + char sKey[XSTR_MIN]; + printf("Re-enter keyword for the cipher '%s': ", pCipher); + + if (!XCLI_GetPass(NULL, sKey, sizeof(sKey))) + { + xloge("Failed to read keyword: %d", errno); + return XFALSE; + } + + if (strcmp(pKey->sKey, sKey)) + { + xloge("Keyword do not match"); + return XFALSE; + } + } + + if (pKey->eCipher == XC_AES) pKey->nLength = pArgs->nAESKeyLen; + else pKey->nLength = strlen(pKey->sKey); + + return XTRUE; +} + +static XSTATUS XCrypt_ValidateArgs(xcrypt_args_t *pArgs) +{ + if ((!pArgs->bPrint && + !xstrused(pArgs->sOutput)) || + (!xstrused(pArgs->sText) && + !xstrused(pArgs->sInput))) return XSTDNON; + + if (XPath_Exists(pArgs->sOutput) && pArgs->bForce == XFALSE) + { + xlogw("File already exists: %s", pArgs->sOutput); + xlogi("Use option -f to force overwrite output"); + return XSTDERR; + } + + if (!xstrused(pArgs->sCiphers)) + { + xlogw("No cipher is specified for ecnrypt/decrypt"); + return XSTDNON; + } + + xarray_t *pCiphers = xstrsplit(pArgs->sCiphers, ":"); + if (pCiphers != NULL) + { + size_t i, nUsed = XArray_Used(pCiphers); + for (i = 0; i < nUsed; i++) + { + const char *pCipher = XArray_GetData(pCiphers, i); + if (pCipher == NULL) continue; + + xcrypt_chipher_t eCipher = XCrypt_GetCipher(pCipher); + if (eCipher == XC_INVALID) + { + xloge("Invalid or unsupported cipher: %s", pCipher); + XArray_Destroy(pCiphers); + return XSTDERR; + } + else if (pArgs->bDecrypt && !XCrypt_DecriptSupport(eCipher)) + { + xloge("Decryption is not supported for cipher: %s", pCipher); + XArray_Destroy(pCiphers); + return XSTDERR; + } + } + + XArray_Destroy(pCiphers); + return XSTDOK; + } + + xcrypt_chipher_t eCipher = XCrypt_GetCipher(pArgs->sCiphers); + if (eCipher == XC_INVALID) + { + xloge("Invalid or unsupported cipher: %s", pArgs->sCiphers); + return XSTDERR; + } + else if (pArgs->bDecrypt && !XCrypt_DecriptSupport(eCipher)) + { + xloge("Decryption is not supported for cipher: %s", pArgs->sCiphers); + return XSTDERR; + } + + return XSTDOK; +} + +static xbool_t XCrypt_ParseArgs(xcrypt_args_t *pArgs, int argc, char *argv[]) +{ + memset(pArgs, 0, sizeof(xcrypt_args_t)); + pArgs->nAESKeyLen = XAES_KEY_LENGTH; + int nChar = 0; + + while ((nChar = getopt(argc, argv, "c:i:o:t:a:d1:f1:h1:p1:s1:v1")) != -1) + { + switch (nChar) + { + case 'c': + xstrncpy(pArgs->sCiphers, sizeof(pArgs->sCiphers), optarg); + break; + case 'i': + xstrncpy(pArgs->sInput, sizeof(pArgs->sInput), optarg); + break; + case 'o': + xstrncpy(pArgs->sOutput, sizeof(pArgs->sOutput), optarg); + break; + case 't': + xstrncpy(pArgs->sText, sizeof(pArgs->sText), optarg); + break; + case 'a': + pArgs->nAESKeyLen = atoi(optarg); + break; + case 'd': + pArgs->bDecrypt = XTRUE; + break; + case 'f': + pArgs->bForce = XTRUE; + break; + case 'h': + pArgs->bHex = XTRUE; + break; + case 'p': + pArgs->bPrint = XTRUE; + break; + case 'v': + default: + XCrypt_DisplayUsage(argv[0]); + return XFALSE; + } + } + + XSTATUS nStatus = XCrypt_ValidateArgs(pArgs); + if (!nStatus) XCrypt_DisplayUsage(argv[0]); + return nStatus == XSTDOK ? XTRUE : XFALSE; +} + +static void XCrypt_HEXDump(const uint8_t *pData, size_t nSize) +{ + uint8_t *pHex = XCrypt_HEX(pData, &nSize, XSTR_SPACE, 16, XFALSE); + if (pHex != NULL) + { + printf("\n%s\n", (char*)pHex); + free(pHex); + } +} + +static void XCrypt_Print(xcrypt_args_t *pArgs, uint8_t *pData, size_t nLength) +{ + if (!pArgs->bPrint) return; + pData[nLength] = XSTR_NUL; + + if (pArgs->bHex) XCrypt_HEXDump(pData, nLength); + else printf("%s\n", (char*)pData); +} + +xbool_t XCrypt_Callback(xcrypt_cb_type_t eType, void *pData, void *pCtx) +{ + if (eType == XCB_KEY) + { + xcrypt_args_t *pArgs = (xcrypt_args_t*)pCtx; + xcrypt_key_t *pKey = (xcrypt_key_t*)pData; + return XCrypt_GetKey(pArgs, pKey); + } + + xloge("%s (%d)", (const char*)pData, errno); + return XFALSE; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xlog_enable(XLOG_INFO); + + xcrypt_args_t args; + if (!XCrypt_ParseArgs(&args, argc, argv)) return XSTDERR; + + xbyte_buffer_t buffer; + XByteBuffer_Init(&buffer, 0, 0); + + if (xstrused(args.sInput) && !XPath_LoadBuffer(args.sInput, &buffer)) + { + xloge("Can not load file: %s (%d)", args.sInput, errno); + return XSTDERR; + } + else if (!xstrused(args.sInput) && XByteBuffer_AddFmt(&buffer, "%s", args.sText) <= 0) + { + xloge("Can not init crypt buffer: %d", errno); + return XSTDERR; + } + + xcrypt_ctx_t crypter; + XCrypt_Init(&crypter, args.bDecrypt, args.sCiphers, XCrypt_Callback, &args); + crypter.nColumns = XCRYPT_HEX_COLUMNS; + + size_t nLength = buffer.nUsed; + uint8_t *pData = XCrypt_Multy(&crypter, buffer.pData, &nLength); + if (pData == NULL) + { + xloge("Multy %s failed for ciphers: %s", + args.bDecrypt ? "decrypt" : "crypt", + args.sCiphers); + + XByteBuffer_Clear(&buffer); + return XSTDERR; + } + + if (xstrused(args.sOutput) && XPath_Write(args.sOutput, "cwt", pData, nLength) <= 0) + { + xloge("Failed to open output file: %s (%d)", args.sOutput, errno); + XByteBuffer_Clear(&buffer); + free(pData); + return XSTDERR; + } + + XCrypt_Print(&args, pData, nLength); + XByteBuffer_Clear(&buffer); + free(pData); + + return XSTDNON; +} \ No newline at end of file diff --git a/examples/xhttp.c b/examples/xhttp.c new file mode 100644 index 0000000..40ae543 --- /dev/null +++ b/examples/xhttp.c @@ -0,0 +1,501 @@ +/*! + * @file libxutils/examples/xhttp.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Example file for working with the HTTP request/responses. + * Send custom HTTP request, analyze headers, download content, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *optarg; + +#define XHTTP_VERSION_MAJ 0 +#define XHTTP_VERSION_MIN 5 + +#define XHTTP_INTERVAL_SEC 1 + +typedef struct xhttp_args_ { + xhttp_method_t eMethod; + xcli_bar_t progressBar; + xfile_t *pOutputFile; + + xtime_t lastTime; + xbool_t nAutoFollow; + xbool_t nForceWrite; + xbool_t nDownload; + xbool_t nVerbose; + xbool_t nSSL; + size_t nTimeout; + size_t nBytes; + size_t nDone; + + xbyte_buffer_t content; + char sAddress[XHTTP_URL_MAX]; + char sHeaders[XLINE_MAX]; + char sContent[XPATH_MAX]; + char sOutput[XPATH_MAX]; + char sSpeed[XSTR_MIN]; +} xhttp_args_t; + +static char *XHTTPApp_WhiteSpace(const int nLength) +{ + static char sRetVal[XHTTP_FIELD_MAX]; + xstrnul(sRetVal); + int i = 0; + + int nLen = XSTD_MIN(nLength, sizeof(sRetVal) - 1); + for (i = 0; i < nLen; i++) sRetVal[i] = ' '; + + sRetVal[i] = '\0'; + return sRetVal; +} + +void XHTTPApp_DisplayUsage(const char *pName) +{ + int nLength = strlen(pName) + 6; + + printf("==========================================================================\n"); + printf(" XHTTP tool v%d.%d - (c) 2022 Sandro Kalatozishvili (f4tb0y@protonmail.com)\n", + XHTTP_VERSION_MAJ, XHTTP_VERSION_MIN); + printf("==========================================================================\n"); + + printf("Usage: %s [-l
] [-c ] [-m ] [-d] [-f] [-s]\n", pName); + printf(" %s [-t ] [-o ] [-x ] [-v] [-w] [-h]\n", XHTTPApp_WhiteSpace(nLength)); + + printf("Options are:\n"); + printf(" -l
# HTTP/S address/link (%s*%s)\n", XSTR_CLR_RED, XSTR_FMT_RESET); + printf(" -c # Content file path\n"); + printf(" -m # HTTP request method\n"); + printf(" -o # Output file path\n"); + printf(" -t # Receive timeout (sec)\n"); + printf(" -x # Custom HTTP headers\n"); + printf(" -d # Download output as a file\n"); + printf(" -f # Follow redirected locations\n"); + printf(" -s # Force SSL connection\n"); + printf(" -v # Enable verbose logging\n"); + printf(" -w # Force overwrite output\n"); + printf(" -h # Print version and usage\n\n"); + printf("Examples:\n"); + printf("1) %s -l https://endpoint.com/ -c body.json -m POST\n", pName); + printf("2) %s -l endpoint.com/test -t 20 -wo output.txt -s -v\n", pName); + printf("2) %s -l endpoint.com -x 'X-Is-Custom: True; X-My-Header: Test'\n", pName); +} + +int XHTTPApp_ParseArgs(xhttp_args_t *pArgs, int argc, char *argv[]) +{ + XByteBuffer_Init(&pArgs->content, 0, 0); + XTime_Init(&pArgs->lastTime); + + pArgs->pOutputFile = NULL; + pArgs->eMethod = XHTTP_GET; + pArgs->nAutoFollow = XFALSE; + pArgs->nForceWrite = XFALSE; + pArgs->nDownload = XFALSE; + pArgs->nVerbose = XFALSE; + pArgs->nSSL = XFALSE; + pArgs->nTimeout = 0; + pArgs->nBytes = 0; + pArgs->nDone = 0; + + xstrncpy(pArgs->sSpeed, sizeof(pArgs->sSpeed), "N/A"); + xstrnul(pArgs->sAddress); + xstrnul(pArgs->sHeaders); + xstrnul(pArgs->sContent); + xstrnul(pArgs->sOutput); + int nChar = 0; + + while ((nChar = getopt(argc, argv, "l:c:m:o:t:x:d1:f1:s1:v1:w1:h1")) != -1) + { + switch (nChar) + { + case 'l': + xstrncpy(pArgs->sAddress, sizeof(pArgs->sAddress), optarg); + break; + case 'c': + xstrncpy(pArgs->sContent, sizeof(pArgs->sContent), optarg); + break; + case 'o': + xstrncpy(pArgs->sOutput, sizeof(pArgs->sOutput), optarg); + break; + case 'x': + xstrncpy(pArgs->sHeaders, sizeof(pArgs->sHeaders), optarg); + break; + case 'm': + pArgs->eMethod = XHTTP_GetMethodType(optarg); + break; + case 't': + pArgs->nTimeout = atoi(optarg); + break; + case 'd': + pArgs->nDownload = XTRUE; + break; + case 'f': + pArgs->nAutoFollow = XTRUE; + break; + case 's': + pArgs->nSSL = XTRUE; + break; + case 'v': + pArgs->nVerbose = XTRUE; + break; + case 'w': + pArgs->nForceWrite = XTRUE; + break; + case 'h': + default: + return 0; + } + } + + if (!xstrused(pArgs->sAddress)) return XFALSE; + xcli_bar_t *pBar = &pArgs->progressBar; + + if (xstrused(pArgs->sContent) && XPath_LoadBuffer(pArgs->sContent, &pArgs->content) <= 0) + { + xloge("Failed to load content from file: %s (%d)", pArgs->sContent, errno); + return XFALSE; + } + + if (xstrused(pArgs->sOutput) || pArgs->nDownload) + { + XProgBar_GetDefaults(pBar); + pArgs->nAutoFollow = XTRUE; + pArgs->nDownload = XTRUE; + pBar->bInPercent = XTRUE; + } + + if (pArgs->nVerbose) + { + xlog_timing(XLOG_TIME); + xlog_enable(XLOG_ALL); + } + + return XTRUE; +} + +int XHTTPApp_AppendArgHeaders(xhttp_t *pHandle, xhttp_args_t *pArgs) +{ + xarray_t *pArr = xstrsplit(pArgs->sHeaders, ";"); + if (pArr == NULL) return XSTDERR; + + pHandle->nAllowUpdate = XTRUE; + size_t i, nUsed = pArr->nUsed; + + for (i = 0; i < nUsed; i++) + { + char *pHeader = (char*)XArray_GetData(pArr, i); + if (pHeader == NULL) continue; + + char *pSavePtr = NULL; + char *pOption = xstrtok(pHeader, ":", &pSavePtr); + if (pOption == NULL) return XSTDERR; + + char *pField = xstrtok(NULL, ":", &pSavePtr); + if (pField == NULL) return XSTDERR; + + char *pOptionPtr = pOption; + char *pFieldPtr = pField; + + while (*pOptionPtr == XSTR_SPACE_CHAR) pOptionPtr++; + while (*pFieldPtr == XSTR_SPACE_CHAR) pFieldPtr++; + + XHTTP_AddHeader(pHandle, pOptionPtr, "%s", pFieldPtr); + xlogd("Adding header: %s: %s", pOptionPtr, pFieldPtr); + } + + XArray_Destroy(pArr); + return XSTDOK; +} + +int XHTTPApp_DisplayRequest(xhttp_t *pHandle) +{ + xhttp_args_t *pArgs = (xhttp_args_t*)pHandle->pUserCtx; + XTime_Get(&pArgs->lastTime); + if (!pArgs->nVerbose) return XSTDOK; + + xlogd("Sending %s request: %zu bytes", + XHTTP_GetMethodStr(pHandle->eMethod), + pHandle->dataRaw.nUsed); + + printf("%s", (char*)pHandle->dataRaw.pData); + if (XHTTP_GetBodySize(pHandle)) printf("\n"); + + return XSTDOK; +} + +void XHTTPApp_UpdateProgress(xhttp_t *pHandle) +{ + xhttp_args_t *pArgs = (xhttp_args_t*)pHandle->pUserCtx; + xcli_bar_t *pBar = &pArgs->progressBar; + char sReceivedSize[XHTTP_FIELD_MAX]; + + if (!pHandle->nContentLength) pBar->fPercent = -1.; + else pBar->fPercent = (double)100 / pHandle->nContentLength * pArgs->nDone; + + XBytesToUnit(sReceivedSize, sizeof(sReceivedSize), pArgs->nDone, XFALSE); + xstrncpyf(pBar->sPrefix, sizeof(pBar->sPrefix), "Downloading... %s ", pArgs->sSpeed); + xstrncpyf(pBar->sSuffix, sizeof(pBar->sSuffix), " %s", sReceivedSize); + XProgBar_Update(pBar); +} + +int XHTTPApp_DumpResponse(xhttp_t *pHandle, xhttp_ctx_t *pCbCtx) +{ + xhttp_args_t *pArgs = (xhttp_args_t*)pHandle->pUserCtx; + if (!pArgs->nDownload || !XHTTP_IsSuccessCode(pHandle)) return XSTDUSR; + + if (pArgs->pOutputFile == NULL) + { + char sTmpPath[XPATH_MAX]; + xstrncpyf(sTmpPath, sizeof(sTmpPath), "%s.part", pArgs->sOutput); + + pArgs->pOutputFile = XFile_New(sTmpPath, "cwt", NULL); + if (pArgs->pOutputFile == NULL) + { + xloge("Failed to open output file: %s (%d)", sTmpPath, errno); + return XSTDERR; + } + } + + xtime_t currTime; + XTime_Get(&currTime); + pArgs->nBytes += pCbCtx->nLength; + + int nDiff = XTime_Diff(&currTime, &pArgs->lastTime); + if (nDiff > XHTTP_INTERVAL_SEC) + { + char sPerSec[XSTR_MIN], sSpeed[XSTR_MIN]; + size_t nPerSec = pArgs->nBytes / nDiff; + XBytesToUnit(sPerSec, sizeof(sPerSec), nPerSec, XFALSE); + xstrncpyfl(sSpeed, sizeof(sSpeed), 12, XSTR_SPACE_CHAR, "%s/s", sPerSec); + xstrnclr(pArgs->sSpeed, sizeof(pArgs->sSpeed), XSTR_FMT_BOLD, "%s", sSpeed); + XTime_Copy(&pArgs->lastTime, &currTime); + pArgs->nBytes = 0; + } + + pArgs->nDone += pCbCtx->nLength; + XHTTPApp_UpdateProgress(pHandle); + + if (XFile_Write(pArgs->pOutputFile, pCbCtx->pData, pCbCtx->nLength) <= 0) + { + xloge("Failed to write data to output file: %s (%d)", pArgs->sOutput, errno); + return XSTDERR; + } + + return XSTDOK; +} + +int XHTTPApp_DisplayResponseHdr(xhttp_t *pHandle) +{ + xhttp_args_t *pArgs = (xhttp_args_t*)pHandle->pUserCtx; + if (!pArgs->nVerbose) return XSTDOK; + + const char *pStatus = XHTTP_GetCodeStr(pHandle->nStatusCode); + const char *pCntType = XHTTP_GetHeader(pHandle, "Content-Type"); + + char cSave = pHandle->dataRaw.pData[pHandle->nHeaderLength - 1]; + pHandle->dataRaw.pData[pHandle->nHeaderLength - 1] = XSTR_NUL; + xlogd("Received response header: %s", pStatus); + printf("%s\n", (char*)pHandle->dataRaw.pData); + pHandle->dataRaw.pData[pHandle->nHeaderLength - 1] = cSave; + + xbool_t bFollowing = (pHandle->nStatusCode >= 300 && pHandle->nStatusCode < 400 && + pArgs->nAutoFollow && XHTTP_GetHeader(pHandle, "Location") != NULL) ? XTRUE : XFALSE; + + if ((pHandle->nContentLength > 0 || xstrused(pCntType)) && !bFollowing) + { + char sBytes[XSTR_TINY]; + if (!pHandle->nContentLength) xstrncpy(sBytes, sizeof(sBytes), "N/A"); + else xstrncpyf(sBytes, sizeof(sBytes), "%zu", pHandle->nContentLength); + xlogd("Downloading body: %s bytes", sBytes); + } + + return XSTDOK; +} + +int XHTTPApp_Callback(xhttp_t *pHttp, xhttp_ctx_t *pCbCtx) +{ + switch (pCbCtx->eCbType) + { + case XHTTP_STATUS: + if (pCbCtx->eStatus == XHTTP_PARSED) + return XHTTPApp_DisplayResponseHdr(pHttp); + xlogd("%s", (const char*)pCbCtx->pData); + return XSTDOK; + case XHTTP_READ: + return XHTTPApp_DumpResponse(pHttp, pCbCtx); + case XHTTP_WRITE: + return XHTTPApp_DisplayRequest(pHttp); + case XHTTP_ERROR: + xloge("%s", (const char*)pCbCtx->pData); + return XSTDERR; + default: + break; + } + + return XSTDUSR; +} + +int XHTTPApp_Prepare(xhttp_args_t *pArgs, xlink_t *pLink) +{ + if (XLink_Parse(pLink, pArgs->sAddress) == XSTDERR) + { + xloge("Unsupported link: %s", pArgs->sAddress); + return XSTDERR; + } + + if (pArgs->nVerbose) + { + xlogd("Parsed link: %s", pArgs->sAddress); + printf("Protocol: %s\nHost: %s\nAddr: %s\nPort: %d\nUser: %s\nPass: %s\nFile: %s\nURL: %s\n\n", + pLink->sProtocol, pLink->sHost, pLink->sAddr, pLink->nPort, pLink->sUser, pLink->sPass, pLink->sFile, pLink->sUrl); + } + + if (pArgs->nDownload && !xstrused(pArgs->sOutput)) + { + const char *pFileName = xstrused(pLink->sFile) ? pLink->sFile : "xhttp.out"; + xstrncpy(pArgs->sOutput, sizeof(pArgs->sOutput), pFileName); + size_t nFileCount = 1; + + while (XPath_Exists(pArgs->sOutput)) + { + xstrncpyf(pArgs->sOutput, sizeof(pArgs->sOutput), "%s.%zu", pFileName, nFileCount); + nFileCount++; + } + } + + if (XPath_Exists(pArgs->sOutput) && pArgs->nForceWrite == XFALSE) + { + xlogw("File already exists: %s", pArgs->sOutput); + xlogi("Use option -w to force overwrite output"); + return XSTDERR; + } + + if (pArgs->nSSL && strncmp(pLink->sProtocol, "https", 5)) + { + xlogd("Upgrading to HTTPS: %s: %d -> %d", pLink->sAddr, pLink->nPort, XHTTP_SSL_PORT); + xstrncpyf(pLink->sHost, sizeof(pLink->sHost), "%s:%d", pLink->sAddr, XHTTP_SSL_PORT); + xstrncpy(pLink->sProtocol, sizeof(pLink->sProtocol), "https"); + pLink->nPort = XHTTP_SSL_PORT; + } + + return XSTDOK; +} + +int XHTTPApp_Perform(xhttp_args_t *pArgs, xlink_t *pLink) +{ + xhttp_t handle; + XHTTP_InitRequest(&handle, pArgs->eMethod, pLink->sUrl, NULL); + XHTTP_AddHeader(&handle, "Host", "%s", pLink->sHost); + XHTTP_AddHeader(&handle, "User-Agent", "xutils/%s", XUtils_VersionShort()); + handle.nTimeout = pArgs->nTimeout; + + uint16_t nCallbacks = XHTTP_ERROR | XHTTP_READ | XHTTP_WRITE | XHTTP_STATUS; + XHTTP_SetCallback(&handle, XHTTPApp_Callback, pArgs, nCallbacks); + + if (xstrused(pArgs->sHeaders) && XHTTPApp_AppendArgHeaders(&handle, pArgs) < 0) + { + xloge("Failed to appen custom headers: %s (%d)", pArgs->sHeaders, errno); + XHTTP_Clear(&handle); + return XSTDERR; + } + + xbool_t bHaveOutput = xstrused(pArgs->sOutput); + xhttp_status_t eStatus; + + eStatus = XHTTP_LinkPerform(&handle, pLink, pArgs->content.pData, pArgs->content.nUsed); + if (eStatus != XHTTP_COMPLETE) + { + if (eStatus == XHTTP_BIGCNT) xlogi("Too big content. Try to use output file (-o )"); + XFile_Clean(pArgs->pOutputFile); + XHTTP_Clear(&handle); + return XSTDERR; + } + + if (bHaveOutput) + { + char sTmpPath[XPATH_MAX]; + xstrncpyf(sTmpPath, sizeof(sTmpPath), "%s.part", pArgs->sOutput); + + if (XPath_Exists(sTmpPath) && rename(sTmpPath, pArgs->sOutput) < 0) + xloge("Failed to rename file: %s -> %s (%d)", sTmpPath, pArgs->sOutput, errno); + } + + if (pArgs->nDone && !handle.nContentLength) + XProgBar_Finish(&pArgs->progressBar); + + if (pArgs->nAutoFollow && + handle.nStatusCode < 400 && + handle.nStatusCode >= 300) + { + const char *pStatus = XHTTP_GetCodeStr(handle.nStatusCode); + xlogd("HTTP redirect: %d (%s)", handle.nStatusCode, pStatus); + + const char *pLocation = XHTTP_GetHeader(&handle, "Location"); + if (pLocation != NULL) + { + xstrncpy(pArgs->sAddress, sizeof(pArgs->sAddress), pLocation); + xlogd("Following location: %s", pArgs->sAddress); + + XFile_Clean(pArgs->pOutputFile); + XHTTP_Clear(&handle); + return XSTDOK; + } + } + + if (!XHTTP_IsSuccessCode(&handle)) + { + const char *pStatus = XHTTP_GetCodeStr(handle.nStatusCode); + xlogw("HTTP response: %d (%s)", handle.nStatusCode, pStatus); + } + + const char *pBody = (const char *)XHTTP_GetBody(&handle); + if (pBody != NULL && !bHaveOutput) printf("%s\n", pBody); + + XFile_Clean(pArgs->pOutputFile); + XHTTP_Clear(&handle); + return XSTDNON; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xlog_cfg_t xcfg; + + xlog_get(&xcfg); + xcfg.eColorFormat = XLOG_COLORING_FULL; + xcfg.nUseHeap = XTRUE; + xcfg.nIdent = XTRUE; + xcfg.nFlags |= XLOG_INFO; + xlog_set(&xcfg); + + xhttp_args_t args; + if (!XHTTPApp_ParseArgs(&args, argc, argv)) + { + XHTTPApp_DisplayUsage(argv[0]); + return XSTDERR; + } + + int nStatus = XSTDOK; + while (nStatus == XSTDOK) + { + xlink_t link; + nStatus = XHTTPApp_Prepare(&args, &link); + if (nStatus < 0) break; + nStatus = XHTTPApp_Perform(&args, &link); + } + + XByteBuffer_Clear(&args.content); + XSock_DeinitSSL(); + return nStatus; +} \ No newline at end of file diff --git a/examples/xjson.c b/examples/xjson.c new file mode 100644 index 0000000..eb35bd0 --- /dev/null +++ b/examples/xjson.c @@ -0,0 +1,114 @@ +/*! + * @file libxutils/examples/xjson.c + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Parse, lint and minify json using xjson library. + */ + +#include +#include +#include +#include +#include +#include + +extern char *optarg; + +#define XJSON_LINT_VER_MAX 0 +#define XJSON_LINT_VER_MIN 2 + +typedef struct xjson_args_ { + char sFile[XPATH_MAX]; + uint16_t nTabSize; + uint8_t nMinify; +} xjson_args_t; + +void XJSON_DisplayUsage(const char *pName) +{ + xlog("================================================"); + xlog(" Lint and Minify JSON file - v%d.%d (%s)", + XJSON_LINT_VER_MAX, XJSON_LINT_VER_MIN, __DATE__); + xlog("================================================"); + + xlog("Usage: %s [-f ] [-l ] [-m] [-h]\n", pName); + xlog("Options are:"); + xlog(" -f # JSON file path (%s*%s)", XSTR_CLR_RED, XSTR_FMT_RESET); + xlog(" -l # Linter tab size"); + xlog(" -m # Minify json file"); + xlog(" -h # Version and usage\n"); + xlog("Example: %s -f example.json -l 4\n", pName); +} + +int XJSON_ParseArgs(xjson_args_t *pArgs, int argc, char *argv[]) +{ + xstrnul(pArgs->sFile); + pArgs->nTabSize = 4; + pArgs->nMinify = 0; + int nChar = 0; + + while ((nChar = getopt(argc, argv, "f:l:m1:h1")) != -1) + { + switch (nChar) + { + case 'f': + xstrncpy(pArgs->sFile, sizeof(pArgs->sFile), optarg); + break; + case 'l': + pArgs->nTabSize = atoi(optarg); + break; + case 'm': + pArgs->nMinify = 1; + break; + case 'h': + default: + return XFALSE; + } + } + + return (xstrused(pArgs->sFile)) ? XTRUE : XFALSE; +} + +int main(int argc, char *argv[]) +{ + xlog_defaults(); + xbyte_buffer_t buffer; + xjson_args_t args; + xjson_t json; + + if (!XJSON_ParseArgs(&args, argc, argv)) + { + XJSON_DisplayUsage(argv[0]); + return 1; + } + + if (XPath_LoadBuffer(args.sFile, &buffer) <= 0) + { + xloge("Failed to load file: %s (%s)", args.sFile, strerror(errno)); + return 1; + } + + if (!XJSON_Parse(&json, (const char*)buffer.pData, buffer.nUsed)) + { + char sError[256]; + XJSON_GetErrorStr(&json, sError, sizeof(sError)); + xloge("Failed to parse JSON: %s", sError); + + XByteBuffer_Clear(&buffer); + XJSON_Destroy(&json); + return 1; + } + + xjson_writer_t writer; + XJSON_InitWriter(&writer, NULL, buffer.nUsed); + if (!args.nMinify) writer.nTabSize = args.nTabSize; + + if (XJSON_WriteObject(json.pRootObj, &writer)) printf("%s\n", writer.pData); + else xloge("Failed to serialize json: errno(%d) %s", errno, writer.pData ? writer.pData : ""); + + XJSON_DestroyWriter(&writer); + XByteBuffer_Clear(&buffer); + XJSON_Destroy(&json); + return 0; +} \ No newline at end of file diff --git a/examples/xlog.c b/examples/xlog.c new file mode 100644 index 0000000..cae0ee1 --- /dev/null +++ b/examples/xlog.c @@ -0,0 +1,90 @@ +/* + * examples/xlog.c + * + * Copyleft (C) 2015 Sun Dro (a.k.a. kala13x) + * + * Example file of useing XLog library. + */ + + +#include +#include +#include + +int logCallback(const char *pLog, size_t nLength, xlog_flag_t eFlag, void *pCtx) +{ + printf("%s", pLog); + return 0; +} + +void greet() +{ + /* Get and print XLog version */ + char sVersion[128]; + XLog_Version(sVersion, sizeof(sVersion), 0); + + printf("=========================================\n"); + printf("XLog Version: %s\n", sVersion); + printf("=========================================\n"); +} + +int main() +{ + /* Used variables */ + char char_arg[32]; + strcpy(char_arg, "test string"); + int int_arg = 69; + + /* Greet users */ + greet(); + + /* Initialize XLog with default parameters */ + XLog_Init("example", XLOG_ALL, 0); + xlog_separator("[xutils]"); + xlog_ident(1); + + xlog_cfg_t slgCfg; + xlog_get(&slgCfg); + slgCfg.eTimeFormat = XLOG_DATE; + slgCfg.logCallback = logCallback; + slgCfg.nUseHeap = 1; + xlog_set(&slgCfg); + + /* Log and print something with level 0 */ + XLog_Note("Test message with level 0"); + + /* Log and print something with level 1 */ + XLog_Warn("Warn message with level 1"); + + /* Log and print something with level 2 */ + XLog_Info("Info message with level 2"); + + /* Log and print something with level 3 */ + XLog_Note("Test message with level 3"); + + /* Log and print something with char argument */ + XLog_Debug("Debug message with char argument: %s", char_arg); + + /* Log and print something with int argument */ + XLog_Error("Error message with int argument: %d", int_arg); + XLog("Message without tag"); + + slgCfg.eColorFormat = XLOG_COLORING_FULL; + slgCfg.nToFile = 1; + xlog_set(&slgCfg); + + /* Print message and save log in the file */ + XLog_Debug("Debug message in the file with int argument: %d", int_arg); + + /* On the fly change parameters (enable file logger) */ + slgCfg.nToFile = 1; + xlog_set(&slgCfg); + + /* Log with user specified tag */ + XLog("Message without tag and with int argument: %d", int_arg); + + xlogd("just another debug message"); + xlogt("just another trace message"); + + return 0; +} diff --git a/examples/xpass.c b/examples/xpass.c new file mode 100644 index 0000000..c557105 --- /dev/null +++ b/examples/xpass.c @@ -0,0 +1,1054 @@ +/*! + * @file libxutils/examples/xpass.c + * + * This source is part of "libxutils" project + * 2015-2022 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Simple and safe password manager for CLI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XPASS_VER_MAX 0 +#define XPASS_VER_MIN 2 +#define XPASS_BUILD_NUM 4 + +#define XPASS_AES_LEN 128 +#define XPASS_NAME_LEN 6 +#define XPASS_HEX_COLUMNS 16 +#define XPASS_CIPHERS "base64:aes:xor:hex" +#define XPASS_DATABASE "/var/lib/xpass/data.xb" +#define XPASS_CONFIG ".config/xpass/config.json" +#define XPASS_DIR_CHMOD "rwxrwxr-x" + +#define XPASS_FRAME_FMT XSTR_FMT_DIM +#define XPASS_ENTRY_FMT XSTR_CLR_NONE +#define XPASS_NAME_FMT XSTR_CLR_GREEN + +#define XPASS_ARG_FMT \ + XSTR_CLR_CYAN, \ + XSTR_FMT_RESET, \ + XSTR_FMT_DIM, \ + XSTR_FMT_RESET + +#define XPASS_CORNER "+" +#define XPASS_LINE "-" +#define XPASS_EDGE "|" + +extern char *optarg; + +typedef struct +{ + const char *pName; + const char *pAddr; + const char *pUser; + const char *pPass; + const char *pDesc; +} xpass_entry_t; + +typedef struct +{ + char sUser[XSTR_TINY]; + char sName[XSTR_TINY]; + char sPass[XSTR_TINY]; + char sAddr[XSTR_MIN]; + char sDesc[XSTR_MIN]; + + char sCiphers[XPATH_MAX]; + char sFile[XPATH_MAX]; + char sConf[XPATH_MAX]; + char sKey[XSTR_TINY]; + + size_t nAESKeyLength; + size_t nFrameLength; + size_t nNameLength; + xjson_t dataBase; + + xbool_t bInitData; + xbool_t bInitConf; + xbool_t bRead; + xbool_t bWrite; + xbool_t bDelete; + xbool_t bUpdate; + + char cCorner; + char cLine; + char cEdge; +} xpass_ctx_t; + +void XPass_InitCtx(xpass_ctx_t *pCtx) +{ + memset(pCtx, 0, sizeof(xpass_ctx_t)); + pCtx->dataBase.pRootObj = NULL; + const char *pHomeDir; + + if ((pHomeDir = getenv("HOME")) == NULL) pHomeDir = getpwuid(getuid())->pw_dir; + xstrncpyf(pCtx->sConf, sizeof(pCtx->sConf), "%s/%s", pHomeDir, XPASS_CONFIG); + + pCtx->cCorner = XPASS_CORNER[0]; + pCtx->cLine = XPASS_LINE[0]; + pCtx->cEdge = XPASS_EDGE[0]; +} + +static char *XPass_WhiteSpace(const int nLength) +{ + static char sRetVal[XSTR_MIN]; + xstrnul(sRetVal); + int i = 0; + + int nLen = XSTD_MIN(nLength, sizeof(sRetVal) - 1); + for (i = 0; i < nLen; i++) sRetVal[i] = ' '; + + sRetVal[i] = '\0'; + return sRetVal; +} + +static void XPass_DisplayUsage(const char *pName) +{ + int nLength = strlen(pName) + 6; + + xlog("=============================================================="); + xlog(" Secure Password Manager for CLI - v%d.%d build %d (%s)", + XPASS_VER_MAX, XPASS_VER_MIN, XPASS_BUILD_NUM, __DATE__); + xlog("=============================================================="); + + xlog("Usage: %s [-c ] [-i ] [-k ] [-a ]", pName); + xlog(" %s [-d ] [-n ] [-u ] [-p ]", XPass_WhiteSpace(nLength)); + xlog(" %s [-C ] [-I] [-J] [-D] [-R] [-W] [-U] [-h]\n", XPass_WhiteSpace(nLength)); + + xlog("These arguments are optional:"); + xlog(" %s-a%s %s# Address value of the entry%s", XPASS_ARG_FMT); + xlog(" %s-d%s %s# Description value of the entry%s", XPASS_ARG_FMT); + xlog(" %s-n%s %s# Unique name value of the entry%s", XPASS_ARG_FMT); + xlog(" %s-u%s %s# Username value of the the entry%s", XPASS_ARG_FMT); + xlog(" %s-p%s %s# Password value of the the entry%s\n", XPASS_ARG_FMT); + + xlog(" %s-C%s %s# Cipher or ciphers by encryption order%s", XPASS_ARG_FMT); + xlog(" %s-c%s %s# Configuration file path%s", XPASS_ARG_FMT); + xlog(" %s-i%s %s# Input/Database file path%s", XPASS_ARG_FMT); + xlog(" %s-k%s %s# AES encryt/decrypt key size%s", XPASS_ARG_FMT); + xlog(" %s-h%s %s# Version and usage%s\n", XPASS_ARG_FMT); + + xlog("Required one operation from this list%s*%s:", XSTR_CLR_RED, XSTR_FMT_RESET); + xlog(" %s-I%s %s# Initialize database file%s", XPASS_ARG_FMT); + xlog(" %s-J%s %s# Initialize JSON config file%s", XPASS_ARG_FMT); + xlog(" %s-D%s %s# Delete entry from the database%s", XPASS_ARG_FMT); + xlog(" %s-R%s %s# Search or read entries from the database%s", XPASS_ARG_FMT); + xlog(" %s-W%s %s# Write new entry to the database%s", XPASS_ARG_FMT); + xlog(" %s-U%s %s# Update existing entry in the database%s\n", XPASS_ARG_FMT); + + + xlog("Supported ciphers:"); + xlog(" aes"); + xlog(" hex"); + xlog(" xor"); + xlog(" base64"); + xlog(" reverse\n"); + + xlog("%sNotes:%s", XSTR_CLR_YELLOW, XSTR_FMT_RESET); + xlog("%s1%s) If you do not specify an argument password (-p ),", XSTR_FMT_BOLD, XSTR_FMT_RESET); + xlog("the program will prompt you to enter the password securely.\n"); + + xlog("%s2%s) The delimiter \":\" can be used to specify more than one ciphers.", XSTR_FMT_BOLD, XSTR_FMT_RESET); + xlog("The program will use the ciphers to encrypt/decrypt database by order.\n"); + + xlog("%s3%s) Unique entry name (-n ) option must be used while reading the", XSTR_FMT_BOLD, XSTR_FMT_RESET); + xlog("database to read password value of the entry, otherwise all the values"); + xlog("will be displayed from the found entry except the username and password.\n"); + + xlog("%sExamples:%s", XSTR_CLR_YELLOW, XSTR_FMT_RESET); + xlog("Initialize database and config files with default values."); + xlog("%s[xutils@examples]$ %s -IJ -i /my/data.xb -c ~/.config/xpass/config.json%s\n", XSTR_FMT_DIM, pName, XSTR_FMT_RESET); + + xlog("Read github.com entry from the database for username \"kala13x\"."); + xlog("%s[xutils@examples]$ %s -R -a github.com -u kala13x%s\n", XSTR_FMT_DIM, pName, XSTR_FMT_RESET); + + xlog("Update description in the database for the entry with name \"FB231\"."); + xlog("%s[xutils@examples]$ %s -U -n FB231 -d \"Personal account\"%s\n", XSTR_FMT_DIM, pName, XSTR_FMT_RESET); + + xlog("Read all entries for user \"kala\" and use custom cipher order to decryt database."); + xlog("%s[xutils@examples]$ %s -R -u kala -c \"hex:aes:xor:base64\"%s\n", XSTR_FMT_DIM, pName, XSTR_FMT_RESET); +} + +static xbool_t XPass_GetPass(xpass_ctx_t *pCtx) +{ + if (XCLI_GetPass("Enter password for entry: ", pCtx->sPass, sizeof(pCtx->sPass)) < 0) + { + xloge("Failed to read password: %d", errno); + return XFALSE; + } + + char sPwd[XSTR_MIN]; + if (XCLI_GetPass("Re-enter password for entry: ", sPwd, sizeof(sPwd)) < 0) + { + xloge("Failed to read password: %d", errno); + return XFALSE; + } + + if (xstrused(pCtx->sPass) && xstrused(sPwd) && strcmp(pCtx->sPass, sPwd)) + { + xloge("Password do not match."); + return XFALSE; + } + + return XTRUE; +} + +static xbool_t XPass_GetKey(xpass_ctx_t *pCtx) +{ + char sPwd[XSTR_MIN]; + + if (XCLI_GetPass("Enter master password: ", sPwd, sizeof(sPwd)) <= 0) + { + xloge("Failed to read master password: %d", errno); + return XFALSE; + } + + if (!pCtx->bRead) + { + char sPwd2[XSTR_MIN]; + + if (XCLI_GetPass("Re-enter master password: ", sPwd2, sizeof(sPwd2)) <= 0) + { + xloge("Failed to read password: %d", errno); + return XFALSE; + } + + if (strcmp(sPwd, sPwd2)) + { + xloge("Password do not match."); + return XFALSE; + } + } + + char *pCrypted = XCrypt_MD5((uint8_t*)sPwd, strlen(sPwd)); + if (pCrypted == NULL) + { + xloge("Failed to crypt master password: %d", errno); + return XFALSE; + } + + xstrncpy(pCtx->sKey, sizeof(pCtx->sKey), pCrypted); + free(pCrypted); + + return XTRUE; +} + +static xbool_t XPass_ParseArgs(xpass_ctx_t *pCtx, int argc, char *argv[]) +{ + int nChar = 0; + while ((nChar = getopt(argc, argv, "a:c:C:d:n:i:u:p:k:D1:I1:J1:R1:W1:U1:h1")) != -1) + { + switch (nChar) + { + case 'a': + xstrncpy(pCtx->sAddr, sizeof(pCtx->sAddr), optarg); + break; + case 'c': + xstrncpy(pCtx->sConf, sizeof(pCtx->sConf), optarg); + break; + case 'C': + xstrncpy(pCtx->sCiphers, sizeof(pCtx->sCiphers), optarg); + break; + case 'd': + xstrncpy(pCtx->sDesc, sizeof(pCtx->sDesc), optarg); + break; + case 'n': + xstrncpy(pCtx->sName, sizeof(pCtx->sName), optarg); + break; + case 'i': + xstrncpy(pCtx->sFile, sizeof(pCtx->sFile), optarg); + break; + case 'u': + xstrncpy(pCtx->sUser, sizeof(pCtx->sUser), optarg); + break; + case 'p': + xstrncpy(pCtx->sPass, sizeof(pCtx->sPass), optarg); + break; + case 'k': + pCtx->nAESKeyLength = (size_t)atoi(optarg); + break; + case 'D': + pCtx->bDelete = XTRUE; + break; + case 'I': + pCtx->bInitData = XTRUE; + break; + case 'J': + pCtx->bInitConf = XTRUE; + break; + case 'R': + pCtx->bRead = XTRUE; + break; + case 'W': + pCtx->bWrite = XTRUE; + break; + case 'U': + pCtx->bUpdate = XTRUE; + break; + case 'h': + default: + XPass_DisplayUsage(argv[0]); + return XFALSE; + } + } + + if (!pCtx->bInitData && + !pCtx->bInitConf && + !pCtx->bRead && + !pCtx->bWrite && + !pCtx->bDelete && + !pCtx->bUpdate) + { + xloge("Please specify the operation."); + XPass_DisplayUsage(argv[0]); + return XFALSE; + } + + if (!xstrused(pCtx->sConf)) + { + xloge("Invalid or missing config file argument."); + XPass_DisplayUsage(argv[0]); + return XFALSE; + } + + return XTRUE; +} + +static xbool_t XPass_FindEntry(xpass_ctx_t *pCtx, const char *pName) +{ + if (!xstrused(pName)) return XFALSE; + xjson_t *pJson = &pCtx->dataBase; + + xjson_obj_t *pArrObj = XJSON_GetObject(pJson->pRootObj, "entries"); + if (pArrObj == NULL) + { + xloge("Database file does not contain entries."); + return XFALSE; + } + + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pItem = XJSON_GetArrayItem(pArrObj, i); + if (pItem != NULL) + { + const char *pEntryName = XJSON_GetString(XJSON_GetObject(pItem, "name")); + if (xstrused(pEntryName) && !strcmp(pName, pEntryName)) return XTRUE; + } + } + + return XFALSE; +} + +static xbool_t XPass_GetName(xpass_ctx_t *pCtx) +{ + printf("Enter unique name of the entry: "); + fgets(pCtx->sName, sizeof(pCtx->sName), stdin); + + size_t nLength = strlen(pCtx->sName); + pCtx->sName[nLength - 1] = XSTR_NUL; + return nLength ? XTRUE : XFALSE; +} + +static xbool_t XPass_GenerateName(xpass_ctx_t *pCtx) +{ + if (!xstrrand(pCtx->sName, sizeof(pCtx->sName), pCtx->nNameLength, XFALSE, XTRUE)) return XFALSE; + if (XPass_FindEntry(pCtx, pCtx->sName)) return XPass_GenerateName(pCtx); + return XTRUE; +} + +static xbool_t XPass_ParseConfig(xpass_ctx_t *pCtx) +{ + xbyte_buffer_t buffer; + if (!XPath_LoadBuffer(pCtx->sConf, &buffer)) + { + xloge("Can not parse config file: %s (%d)", pCtx->sConf, errno); + XByteBuffer_Clear(&buffer); + return XFALSE; + } + + xjson_t json; + if (!XJSON_Parse(&json, (const char*)buffer.pData, buffer.nUsed)) + { + char sError[XMSG_MID]; + XJSON_GetErrorStr(&json, sError, sizeof(sError)); + xloge("Failed to parse database file: %s", sError); + + XByteBuffer_Clear(&buffer); + return XFALSE; + } + + XByteBuffer_Clear(&buffer); + xjson_obj_t *pRoot = json.pRootObj; + + xjson_obj_t *pCfgObj = XJSON_GetObject(pRoot, "config"); + if (pCfgObj == NULL) + { + xloge("Invalid configuration file: %s", pCtx->sConf); + XJSON_Destroy(&json); + return XFALSE; + } + + if (!pCtx->nAESKeyLength) + { + xjson_obj_t *pAesLenObj = XJSON_GetObject(pCfgObj, "aesKeyLength"); + if (pAesLenObj == NULL) + { + xloge("Missing \"aesKeyLength\" entry in the config file: %s", pCtx->sConf); + XJSON_Destroy(&json); + return XFALSE; + } + + pCtx->nAESKeyLength = XJSON_GetU32(pAesLenObj); + } + + if (!xstrused(pCtx->sFile)) + { + const char* pFilePath = XJSON_GetString(XJSON_GetObject(pCfgObj, "databasePath")); + if (!xstrused(pFilePath)) + { + xloge("Missing \"databasePath\" entry in the config file: %s", pCtx->sConf); + XJSON_Destroy(&json); + return XFALSE; + } + + xstrncpy(pCtx->sFile, sizeof(pCtx->sFile), pFilePath); + } + + if (!xstrused(pCtx->sCiphers)) + { + const char *pCiphers = XJSON_GetString(XJSON_GetObject(pCfgObj, "ciphers")); + if (!xstrused(pCiphers)) + { + xloge("Missing \"ciphers\" entry in the config file: %s", pCtx->sConf); + XJSON_Destroy(&json); + return XFALSE; + } + + xstrncpy(pCtx->sCiphers, sizeof(pCtx->sCiphers), pCiphers); + } + + xjson_obj_t *pNameLenObj = XJSON_GetObject(pCfgObj, "nameLength"); + if (pNameLenObj == NULL) + { + xloge("Missing \"nameLength\" entry in the config file: %s", pCtx->sConf); + XJSON_Destroy(&json); + return XFALSE; + } + + pCtx->nNameLength = XJSON_GetU32(pNameLenObj); + + xjson_obj_t *pLayoytObj = XJSON_GetObject(pRoot, "layoyt"); + if (pLayoytObj != NULL) + { + const char *pCorner = XJSON_GetString(XJSON_GetObject(pCfgObj, "cornerChar")); + if (xstrused(pCorner)) pCtx->cCorner = pCorner[0]; + + const char *pLine = XJSON_GetString(XJSON_GetObject(pCfgObj, "lineChar")); + if (xstrused(pLine)) pCtx->cLine = pLine[0]; + + const char *pEdge = XJSON_GetString(XJSON_GetObject(pCfgObj, "edgeChar")); + if (xstrused(pEdge)) pCtx->cEdge = pEdge[0]; + } + + xcli_size_t cliSize; + XCLI_GetWindowSize(&cliSize); + pCtx->nFrameLength = cliSize.nWinColumns - 1; + + XJSON_Destroy(&json); + return XTRUE; +} + +static void XPass_PrintEntry(xpass_ctx_t *pCtx, const char *pName, const char *pVal, size_t nFillSize) +{ + char sBuffer[XPATH_MAX], sFill[XPATH_MAX]; + + size_t nBytes = xstrncpyf(sBuffer, sizeof(sBuffer), "%s%c%s %s%s%s: %s%s%s", + XPASS_FRAME_FMT, pCtx->cEdge, XSTR_FMT_RESET, + XPASS_NAME_FMT, pName, XSTR_FMT_RESET, + XPASS_ENTRY_FMT, pVal, XSTR_FMT_RESET); + + int nFreeSpace = (int)sizeof(sBuffer) - (int)nBytes; + nBytes -= xstrextra(sBuffer, nBytes, 0, NULL, NULL); + + if (nFillSize > nBytes) + { + xstrfill(sFill, sizeof(sFill), nFillSize - nBytes, ' '); + nFreeSpace -= xstrncat(sBuffer, nFreeSpace, "%s", sFill); + } + + if (nFreeSpace > 0) + { + xstrncat(sBuffer, (size_t)nFreeSpace, "%s%c%s", + XPASS_FRAME_FMT, pCtx->cEdge, XSTR_FMT_RESET); + } + + printf("%s\n", sBuffer); +} + +static void XPass_DisplayEntry(xpass_ctx_t *pCtx, xpass_entry_t *pEntry, xbool_t bFirst) +{ + char sLineBuff[XPATH_MAX], tmpBuff[XPATH_MAX]; + size_t nFillSize = pCtx->nFrameLength; + + xstrncpyfl(tmpBuff, sizeof(tmpBuff), nFillSize, pCtx->cLine, "%c", pCtx->cCorner); + xstrncat(tmpBuff, sizeof(tmpBuff) - nFillSize, "%c", pCtx->cCorner); + xstrnclr(sLineBuff, sizeof(sLineBuff), XPASS_FRAME_FMT, "%s", tmpBuff); + if (bFirst) printf("%s\n", sLineBuff); + + xbool_t bPrintSecrets = (xstrused(pCtx->sName)) ? XTRUE : XFALSE; + if (xstrused(pEntry->pName)) XPass_PrintEntry(pCtx, "Name", pEntry->pName, nFillSize); + if (xstrused(pEntry->pAddr)) XPass_PrintEntry(pCtx, "Addr", pEntry->pAddr, nFillSize); + if (xstrused(pEntry->pUser) && bPrintSecrets) XPass_PrintEntry(pCtx, "User", pEntry->pUser, nFillSize); + if (xstrused(pEntry->pPass) && bPrintSecrets) XPass_PrintEntry(pCtx, "Pass", pEntry->pPass, nFillSize); + if (xstrused(pEntry->pDesc)) XPass_PrintEntry(pCtx, "Desc", pEntry->pDesc, nFillSize); + printf("%s\n", sLineBuff); +} + +static xbool_t XPass_WriteObj(xpass_ctx_t *pCtx, xjson_obj_t *pObj, xbool_t bUpdate) +{ + if (!bUpdate) + { + if (!xstrused(pCtx->sName) && !XPass_GenerateName(pCtx)) return XFALSE; + if (!xstrused(pCtx->sPass) && !XPass_GetPass(pCtx)) return XFALSE; + } + + if (!xstrused(pCtx->sName)) return XFALSE; + XSTATUS nStatus = XJSON_ERR_NONE; + + nStatus = XJSON_AddString(pObj, "name", pCtx->sName); + if (!nStatus && xstrused(pCtx->sAddr)) nStatus = XJSON_AddString(pObj, "addr", pCtx->sAddr); + if (!nStatus && xstrused(pCtx->sUser)) nStatus = XJSON_AddString(pObj, "user", pCtx->sUser); + if (!nStatus && xstrused(pCtx->sPass)) nStatus = XJSON_AddString(pObj, "pass", pCtx->sPass); + if (!nStatus && xstrused(pCtx->sDesc)) nStatus = XJSON_AddString(pObj, "desc", pCtx->sDesc); + return nStatus == XJSON_ERR_NONE ? XTRUE : XFALSE; +} + +xbool_t XPass_Callback(xcrypt_cb_type_t eType, void *pData, void *pCtx) +{ + if (eType == XCB_KEY) + { + xcrypt_key_t *pKey = (xcrypt_key_t*)pData; + xpass_ctx_t *pArgs = (xpass_ctx_t*)pCtx; + + xstrncpyf(pKey->sKey, sizeof(pKey->sKey), pArgs->sKey); + if (pKey->eCipher == XC_AES) pKey->nLength = pArgs->nAESKeyLength; + else pKey->nLength = strlen(pArgs->sKey); + + return XTRUE; + } + + xloge("%s", (const char*)pData); + return XFALSE; +} + +static xbool_t XPass_ReverseCiphers(char *pDst, size_t nSize, const char *pSrc) +{ + xarray_t *pCiphers = xstrsplit(pSrc, ":"); + if (pCiphers == NULL) + { + xstrncpy(pDst, nSize, pSrc); + return XFALSE; + } + + int i, nUsed = (int)XArray_Used(pCiphers); + size_t nAvail = nSize; + + for (i = nUsed; i >= 0; i--) + { + char *pCipher = XArray_GetData(pCiphers, i); + if (pCipher == NULL) continue; + + nAvail -= xstrncat(pDst, nAvail, "%s", pCipher); + if (i - 1 >= 0) nAvail -= xstrncat(pDst, nAvail, ":"); + } + + XArray_Destroy(pCiphers); + return XTRUE; +} + +static xbool_t XPass_LoadDatabase(xpass_ctx_t *pCtx) +{ + xbyte_buffer_t buffer; + if (!XPath_LoadBuffer(pCtx->sFile, &buffer)) + { + xloge("Can not load database file: %s (%d)", pCtx->sFile, errno); + return XFALSE; + } + + char sCiphers[XSTR_MIN]; + sCiphers[0] = XSTR_NUL; + + XPass_ReverseCiphers(sCiphers, sizeof(sCiphers), pCtx->sCiphers); + const uint8_t *pInput = (const uint8_t*)buffer.pData; + size_t nLength = buffer.nUsed; + + xcrypt_ctx_t crypter; + XCrypt_Init(&crypter, XTRUE, sCiphers, XPass_Callback, pCtx); + crypter.nColumns = XPASS_HEX_COLUMNS; + + uint8_t *pData = XCrypt_Multy(&crypter, pInput, &nLength); + if (pData == NULL) + { + xloge("Failed to decrypt database: %d", errno); + XByteBuffer_Clear(&buffer); + return XFALSE; + } + + XByteBuffer_Clear(&buffer); + xjson_t *pJson = &pCtx->dataBase; + + if (!XJSON_Parse(pJson, (const char*)pData, nLength)) + { + char sError[XMSG_MID]; + XJSON_GetErrorStr(pJson, sError, sizeof(sError)); + xloge("Failed to parse database file: %s", sError); + + free(pData); + return XFALSE; + } + + free(pData); + xjson_obj_t *pRoot = pJson->pRootObj; + + xjson_obj_t *pCrcObj = XJSON_GetObject(pRoot, "crc32"); + if (pCrcObj == NULL) + { + xloge("Invalid database file."); + XJSON_Destroy(pJson); + return XFALSE; + } + + const uint8_t *pKey = (const uint8_t*)pCtx->sKey; + size_t nKeyLength = strlen(pCtx->sKey); + + uint32_t nDecryptedCRC = XJSON_GetU32(pCrcObj); + uint32_t nCurrentCRC = XCrypt_CRC32(pKey, nKeyLength); + + if (nDecryptedCRC != nCurrentCRC) + { + xloge("CRC32 missmatch in database file."); + XJSON_Destroy(pJson); + return XFALSE; + } + + return XTRUE; +} + +static xbool_t XPass_WriteDatabase(xpass_ctx_t *pCtx) +{ + xjson_t *pJson = &pCtx->dataBase; + if (pJson->pRootObj == NULL) return XFALSE; + + xjson_writer_t writer; + XJSON_InitWriter(&writer, NULL, XSTR_MIN); + + if (!XJSON_WriteObject(pJson->pRootObj, &writer)) + { + xloge("Failed to serialize entries in JSON format."); + XJSON_DestroyWriter(&writer); + return XFALSE; + } + + xcrypt_ctx_t crypter; + XCrypt_Init(&crypter, XFALSE, pCtx->sCiphers, XPass_Callback, pCtx); + crypter.nColumns = XPASS_HEX_COLUMNS; + + size_t nLength = writer.nLength; + const uint8_t *pInput = (const uint8_t*)writer.pData; + + uint8_t *pData = XCrypt_Multy(&crypter, pInput, &nLength); + if (pData == NULL) + { + xloge("Failed to encrypt database entries: %d", errno); + XJSON_DestroyWriter(&writer); + return XFALSE; + } + + if (XPath_Write(pCtx->sFile, "cwt", pData, nLength) <= 0) + { + xloge("Failed to wite data: %s (%d)", pCtx->sFile, errno); + XJSON_DestroyWriter(&writer); + free(pData); + return XFALSE; + } + + XJSON_DestroyWriter(&writer); + free(pData); + return XTRUE; +} + +static xbool_t XPass_InitDatabase(xpass_ctx_t *pCtx) +{ + if (!xstrused(pCtx->sFile)) return XTRUE; + xjson_t *pJson = &pCtx->dataBase; + + xlogw("This operation will erase and reinitialize following file:"); + xlog("%sDatabase:%s %s", XSTR_FMT_BOLD, XSTR_FMT_RESET, pCtx->sFile); + printf("Do you want to continue? (y/n): "); + + char sAnswer[XSTR_TINY]; + sAnswer[0] = XSTR_NUL; + fgets(sAnswer, sizeof(sAnswer), stdin); + + if (sAnswer[0] != 'Y' && + sAnswer[0] != 'y') + return XFALSE; + + xpath_t path; + XPath_Parse(&path, pCtx->sFile); + + xmode_t nMode = 0; + XPath_PermToMode(XPASS_DIR_CHMOD, &nMode); + + if (xstrused(path.sPath) && XDir_Create(path.sPath, nMode) <= 0) + { + xloge("Failed create directory: %s (%d)", path.sPath, errno); + return XFALSE; + } + + pJson->pRootObj = XJSON_NewObject(NULL, XTRUE); + if (pJson->pRootObj == NULL) + { + xloge("Failed to allocate memory for JSON object: %d", errno); + return XFALSE; + } + + xjson_obj_t *pArrObj = XJSON_NewArray("entries", XTRUE); + if (pArrObj == NULL) + { + xloge("Failed to allocate memory for JSON array: %d", errno); + return XFALSE; + } + + if (XJSON_AddObject(pJson->pRootObj, pArrObj) != XJSON_ERR_NONE) + { + xloge("Failed to initialize database entries: %d", errno); + return XFALSE; + } + + const uint8_t* pKey = (const uint8_t*)pCtx->sKey; + const char *pXUtils = XUtils_VersionShort(); + + size_t nKeyLength = strlen(pCtx->sKey); + uint32_t nCRC32 = XCrypt_CRC32(pKey, nKeyLength); + + char sVersion[XSTR_TINY]; + xstrncpyf(sVersion, sizeof(sVersion), "%d.%d.%d", + XPASS_VER_MAX, XPASS_VER_MIN, XPASS_BUILD_NUM); + + if (XJSON_AddString(pJson->pRootObj, "version", sVersion) != XJSON_ERR_NONE || + XJSON_AddString(pJson->pRootObj, "xutils", pXUtils) != XJSON_ERR_NONE || + XJSON_AddU32(pJson->pRootObj, "crc32", nCRC32) != XJSON_ERR_NONE) + { + xloge("Failed to initialize json database: %d", errno); + return XFALSE; + } + + return XTRUE; +} + +static xbool_t XPass_InitConfigFile(xpass_ctx_t *pCtx) +{ + xlogw("This operation will erase and reinitialize following file:"); + xlog("%sConfig:%s %s", XSTR_FMT_BOLD, XSTR_FMT_RESET, pCtx->sConf); + printf("Do you want to continue? (y/n): "); + + char sAnswer[XSTR_TINY]; + sAnswer[0] = XSTR_NUL; + fgets(sAnswer, sizeof(sAnswer), stdin); + + if (sAnswer[0] != 'Y' && + sAnswer[0] != 'y') + return XFALSE; + + xpath_t path; + XPath_Parse(&path, pCtx->sConf); + + xmode_t nMode = 0; + XPath_PermToMode(XPASS_DIR_CHMOD, &nMode); + + if (xstrused(path.sPath) && XDir_Create(path.sPath, nMode) <= 0) + { + xloge("Failed create directory: %s (%d)", path.sPath, errno); + return XFALSE; + } + + xjson_obj_t *pRootObj = XJSON_NewObject(NULL, XFALSE); + if (pRootObj == NULL) + { + xloge("Failed to allocate memory for JSON object: %d", errno); + return XFALSE; + } + + xjson_obj_t *pCfgObj = XJSON_NewObject("config", XFALSE); + if (pCfgObj == NULL) + { + xloge("Failed to allocate memory for config JSON object: %s", pCtx->sConf); + XJSON_FreeObject(pRootObj); + return XFALSE; + } + + if (!xstrused(pCtx->sCiphers)) xstrncpy(pCtx->sCiphers, sizeof(pCtx->sCiphers), XPASS_CIPHERS); + if (!xstrused(pCtx->sFile)) xstrncpy(pCtx->sFile, sizeof(pCtx->sFile), XPASS_DATABASE); + pCtx->nAESKeyLength = pCtx->nAESKeyLength ? pCtx->nAESKeyLength : XPASS_AES_LEN; + pCtx->nNameLength = pCtx->nNameLength ? pCtx->nNameLength : XPASS_NAME_LEN; + + if (XJSON_AddU32(pCfgObj, "aesKeyLength", pCtx->nAESKeyLength) != XJSON_ERR_NONE || + XJSON_AddU32(pCfgObj, "nameLength", pCtx->nNameLength) != XJSON_ERR_NONE || + XJSON_AddString(pCfgObj, "databasePath", pCtx->sFile) != XJSON_ERR_NONE || + XJSON_AddString(pCfgObj, "ciphers", pCtx->sCiphers) != XJSON_ERR_NONE || + XJSON_AddObject(pRootObj, pCfgObj) != XJSON_ERR_NONE) + { + xloge("Failed to initialize JSON config object: %s", pCtx->sConf); + XJSON_FreeObject(pRootObj); + XJSON_FreeObject(pCfgObj); + return XFALSE; + } + + xjson_obj_t *pLayoutObj = XJSON_NewObject("layout", XFALSE); + if (pLayoutObj == NULL) + { + xloge("Failed to allocate memory for layout JSON object: %s", pCtx->sConf); + XJSON_FreeObject(pRootObj); + return XFALSE; + } + + if (XJSON_AddString(pLayoutObj, "cornerChar", XPASS_CORNER) != XJSON_ERR_NONE || + XJSON_AddString(pLayoutObj, "lineChar", XPASS_LINE) != XJSON_ERR_NONE || + XJSON_AddString(pLayoutObj, "edgeChar", XPASS_EDGE) != XJSON_ERR_NONE || + XJSON_AddObject(pRootObj, pLayoutObj) != XJSON_ERR_NONE) + { + xloge("Failed to initialize layout JSON object: %s", pCtx->sConf); + XJSON_FreeObject(pRootObj); + XJSON_FreeObject(pLayoutObj); + return XFALSE; + } + + xjson_writer_t writer; + XJSON_InitWriter(&writer, NULL, XSTR_MIN); + writer.nTabSize = 4; // Enable linter + + if (!XJSON_WriteObject(pRootObj, &writer)) + { + xloge("Failed to serialize config entries in JSON format: %d", errno); + XJSON_DestroyWriter(&writer); + XJSON_FreeObject(pRootObj); + return XFALSE; + } + + const uint8_t *pData = (const uint8_t*)writer.pData; + size_t nLength = writer.nLength; + + if (XPath_Write(pCtx->sConf, "cwt", pData, nLength) <= 0) + { + xloge("Failed to wite config data: %s (%d)", pCtx->sConf, errno); + XJSON_FreeObject(pRootObj); + XJSON_DestroyWriter(&writer); + return XFALSE; + } + + XJSON_FreeObject(pRootObj); + XJSON_DestroyWriter(&writer); + return XTRUE; +} + +static xbool_t XPass_ReadEntry(xpass_ctx_t *pCtx) +{ + xjson_t *pJson = &pCtx->dataBase; + + xjson_obj_t *pArrObj = XJSON_GetObject(pJson->pRootObj, "entries"); + if (pArrObj == NULL) + { + xloge("Database file does not contain entries."); + return XFALSE; + } + + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + xbool_t bFirst = XTRUE; + + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pArrObj, i); + if (pArrItemObj != NULL) + { + xpass_entry_t entry; + entry.pName = XJSON_GetString(XJSON_GetObject(pArrItemObj, "name")); + entry.pUser = XJSON_GetString(XJSON_GetObject(pArrItemObj, "user")); + entry.pAddr = XJSON_GetString(XJSON_GetObject(pArrItemObj, "addr")); + entry.pDesc = XJSON_GetString(XJSON_GetObject(pArrItemObj, "desc")); + + if (xstrused(pCtx->sName) && (!xstrused(entry.pName) || xstrsrc(entry.pName, pCtx->sName) < 0)) continue; + if (xstrused(pCtx->sUser) && (!xstrused(entry.pUser) || xstrsrc(entry.pUser, pCtx->sUser) < 0)) continue; + if (xstrused(pCtx->sAddr) && (!xstrused(entry.pAddr) || xstrsrc(entry.pAddr, pCtx->sAddr) < 0)) continue; + if (xstrused(pCtx->sDesc) && (!xstrused(entry.pDesc) || xstrsrc(entry.pDesc, pCtx->sDesc) < 0)) continue; + + entry.pPass = XJSON_GetString(XJSON_GetObject(pArrItemObj, "pass")); + XPass_DisplayEntry(pCtx, &entry, bFirst); + bFirst = XFALSE; + } + } + + return XTRUE; +} + +static xbool_t XPass_UpdateEntry(xpass_ctx_t *pCtx) +{ + xjson_t *pJson = &pCtx->dataBase; + + xjson_obj_t *pArrObj = XJSON_GetObject(pJson->pRootObj, "entries"); + if (pArrObj == NULL) + { + xloge("Database file does not contain entries."); + return XFALSE; + } + + if (!xstrused(pCtx->sName) && !XPass_GetName(pCtx)) return XFALSE; + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + xbool_t bUpdated = XFALSE, bFound = XFALSE; + + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pArrObj, i); + if (pArrItemObj != NULL) + { + const char *pName = XJSON_GetString(XJSON_GetObject(pArrItemObj, "name")); + if (!xstrused(pName) || strcmp(pName, pCtx->sName)) continue; + + pArrItemObj->nAllowUpdate = XTRUE; + bUpdated = XPass_WriteObj(pCtx, pArrItemObj, XTRUE); + bFound = XTRUE; + break; + } + } + + if (!bFound) xloge("Entry not found: %s", pCtx->sName); + else if (!bUpdated) xloge("Failed to update entry: %s", pCtx->sName); + + return bUpdated; +} + +static xbool_t XPass_DeleteEntry(xpass_ctx_t *pCtx) +{ + xjson_t *pJson = &pCtx->dataBase; + + xjson_obj_t *pArrObj = XJSON_GetObject(pJson->pRootObj, "entries"); + if (pArrObj == NULL) + { + xloge("Database file does not contain entries."); + return XFALSE; + } + + if (!xstrused(pCtx->sName) && !XPass_GetName(pCtx)) return XFALSE; + size_t i, nLength = XJSON_GetArrayLength(pArrObj); + xbool_t bDeleted = XFALSE; + + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pArrObj, i); + if (pArrItemObj != NULL) + { + const char *pName = XJSON_GetString(XJSON_GetObject(pArrItemObj, "name")); + if (!xstrused(pName) || strcmp(pName, pCtx->sName)) continue; + + XJSON_RemoveArrayItem(pArrObj, i); + bDeleted = XTRUE; + break; + } + } + + if (!bDeleted) xloge("Entry not found: %s", pCtx->sName); + return bDeleted; +} + +static xbool_t XPass_AppendEntry(xpass_ctx_t *pCtx) +{ + xjson_t *pJson = &pCtx->dataBase; + + xjson_obj_t *pArrObj = XJSON_GetObject(pJson->pRootObj, "entries"); + if (pArrObj == NULL) + { + xloge("Database file does not contain entries."); + return XFALSE; + } + + xjson_obj_t *pNewObj = XJSON_NewObject(NULL, XTRUE); + if (pNewObj == NULL) + { + xloge("Failed to allocate memory for JSON object: %d", errno); + return XFALSE; + } + + if (!XPass_WriteObj(pCtx, pNewObj, XFALSE)) + { + xloge("Failed to write JSON object: %d", errno); + XJSON_FreeObject(pNewObj); + return XFALSE; + } + + if (XJSON_AddObject(pArrObj, pNewObj) != XJSON_ERR_NONE) + { + xloge("Failed to store new database object: %d", errno); + XJSON_FreeObject(pNewObj); + return XFALSE; + } + + return XTRUE; +} + +static xbool_t XPass_ProcessDatabase(xpass_ctx_t *pCtx) +{ + if (!XPass_GetKey(pCtx)) return XFALSE; + + if ((pCtx->bRead || pCtx->bWrite || + pCtx->bUpdate || pCtx->bDelete) && + !XPass_LoadDatabase(pCtx)) return XFALSE; + + if (pCtx->bWrite && XPass_FindEntry(pCtx, pCtx->sName)) + { + xloge("Entry with name '%s' already exists.", pCtx->sName); + xlogi("Press enter for auto unique name generation."); + if (!XPass_GetName(pCtx) && !XPass_GenerateName(pCtx)) return XFALSE; + } + + if (pCtx->bInitData) return XPass_InitDatabase(pCtx); + else if (pCtx->bRead) return XPass_ReadEntry(pCtx); + else if (pCtx->bWrite) return XPass_AppendEntry(pCtx); + else if (pCtx->bUpdate) return XPass_UpdateEntry(pCtx); + else if (pCtx->bDelete) return XPass_DeleteEntry(pCtx); + return XTRUE; +} + +int main(int argc, char* argv[]) +{ + srand(time(0)); + xlog_defaults(); + xlog_enable(XLOG_INFO); + + xpass_ctx_t ctx; + XPass_InitCtx(&ctx); + + if (!XPass_ParseArgs(&ctx, argc, argv)) return XSTDERR; + else if (ctx.bInitConf && !XPass_InitConfigFile(&ctx)) return XSTDERR; + else if (!ctx.bInitConf && !XPass_ParseConfig(&ctx)) return XSTDERR; + + if (!XPass_ProcessDatabase(&ctx)) + { + XJSON_Destroy(&ctx.dataBase); + return XSTDERR; + } + + if (!ctx.bRead) XPass_WriteDatabase(&ctx); + XJSON_Destroy(&ctx.dataBase); + return XSTDNON; +} \ No newline at end of file diff --git a/examples/xsrc.c b/examples/xsrc.c new file mode 100644 index 0000000..0af1e79 --- /dev/null +++ b/examples/xsrc.c @@ -0,0 +1,368 @@ +/*! + * @file libxutils/examples/xsrc.c + * + * This source is part of "libxutils" project + * 2015-2022 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of advanced file search based on the xUtils. + */ + +#include +#include +#include +#include + +#define XSEARCH_VERSION_MAX 1 +#define XSEARCH_VERSION_MIN 0 +#define XSEARCH_BUILD_NUMBER 9 + +#define XSEARCH_INFO_LEN 128 +#define XSEARCH_TIME_LEN 12 +#define XSEARCH_SIZE_LEN 32 + +#define XSEARCH_ARG_COLORING \ + XSTR_CLR_CYAN, \ + XSTR_FMT_RESET, \ + XSTR_FMT_DIM, \ + XSTR_FMT_RESET + +static xatomic_t g_interrupted = 0; +extern char *optarg; + +typedef struct { + char sDirectory[XPATH_MAX]; + char sFileName[XNAME_MAX]; + char sText[XSTR_MID]; + xbool_t bInsensitive; + xbool_t bRecursive; + xbool_t bVerbose; + int nPermissions; + int nLinkCount; + int nFileTypes; + int nFileSize; +} xsearch_args_t; + +void signal_callback(int sig) +{ + printf("\nInterrupted with signal: %d\n", sig); + XSYNC_ATOMIC_SET(&g_interrupted, 1); +} + +static int XSearch_GetFileTypes(const char *pTypes) +{ + size_t i, nLen = strlen(pTypes); + int nTypes = 0; + + for (i = 0; i < nLen; i++) + { + switch (pTypes[i]) + { + case 'b': nTypes |= XF_BLOCK_DEVICE; break; + case 'c': nTypes |= XF_CHAR_DEVICE; break; + case 'd': nTypes |= XF_DIRECTORY; break; + case 'f': nTypes |= XF_REGULAR; break; + case 'l': nTypes |= XF_SYMLINK; break; + case 'p': nTypes |= XF_PIPE; break; + case 's': nTypes |= XF_SOCKET; break; + default: + { + xloge("Invalid file type"); + return XSTDERR; + } + } + } + + return nTypes; +} + +static int XSearch_GetPermissins(const char *pPerm) +{ + xmode_t nMode = 0; + if (XPath_PermToMode(pPerm, &nMode) < 0) + { + xloge("Invalid permissions"); + return XSTDERR; + } + + char sChmod[32]; + XPath_ModeToChmod(sChmod, sizeof(sChmod), nMode); + + return atoi(sChmod); +} + +void XSearch_Usage(const char *pName) +{ + printf("==========================================================\n"); + printf("Advanced File Search - Version: %d.%d build %d (%s)\n", + XSEARCH_VERSION_MAX, XSEARCH_VERSION_MIN, XSEARCH_BUILD_NUMBER, __DATE__); + printf("==========================================================\n"); + + int i, nLength = strlen(pName) + 6; + char sWhiteSpace[nLength + 1]; + + for (i = 0; i < nLength; i++) sWhiteSpace[i] = ' '; + sWhiteSpace[nLength] = 0; + + printf("Usage: %s [-f ] [-n ] [-g ] [-i]\n", pName); + printf(" %s [-d ] [-l ] [-r]\n", sWhiteSpace); + printf(" %s [-p ] [-t ] [-h] [-v]\n\n", sWhiteSpace); + + printf("Options are:\n"); + printf(" %s-d%s %s# Target directory path%s\n", XSEARCH_ARG_COLORING); + printf(" %s-f%s %s# Target file name%s\n", XSEARCH_ARG_COLORING); + printf(" %s-g%s %s# Search file containing the text%s\n", XSEARCH_ARG_COLORING); + printf(" %s-n%s %s# Target file size in bytes%s\n", XSEARCH_ARG_COLORING); + printf(" %s-l%s %s# Target file link count%s\n", XSEARCH_ARG_COLORING); + printf(" %s-p%s %s# Target file permissions (e.g. 'rwxr-xr--')%s\n", XSEARCH_ARG_COLORING); + printf(" %s-t%s %s# Target file types (*)%s\n", XSEARCH_ARG_COLORING); + printf(" %s-i%s %s# Case insensitive search%s\n", XSEARCH_ARG_COLORING); + printf(" %s-r%s %s# Recursive search target directory%s\n", XSEARCH_ARG_COLORING); + printf(" %s-v%s %s# Display additional information (verbose)%s\n", XSEARCH_ARG_COLORING); + printf(" %s-h%s %s# Display version and usage information%s\n\n", XSEARCH_ARG_COLORING); + + printf("File types (*):\n"); + printf(" %sb%s: block device\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sc%s: character device\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sd%s: directory\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sf%s: regular file\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sl%s: symbolic link\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sp%s: pipe\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %ss%s: socket\n\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + + printf("Notes:\n"); + printf(" 1) option is supporting wildcard character: '%s*%s'\n", XSTR_FMT_BOLD, XSTR_FMT_RESET); + printf(" 2) option is supporting one and more file types: %s-t ldb%s\n", XSTR_FMT_BOLD, XSTR_FMT_RESET); + printf(" 3) One or more argument can be specified by using delimiter: '%s;%s'\n\n", XSTR_FMT_BOLD, XSTR_FMT_RESET); + + printf("Examples:\n"); + printf("%sRecursive search of every symlink or a regular file in the root file%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); + printf("%ssystem that has permissions 777 and contains \".log\" in the file name:%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); + printf("%s[xutils@examples]$ %s -rvd / -t lf -f \"*.log\" -p rwxrwxrwx%s\n\n", XSTR_FMT_BOLD, pName, XSTR_FMT_RESET); + + printf("%sRecursive search of every .cpp and .java file in the \"/opt\" directory%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); + printf("%sthat contains the case insensitive text \"socket\" and verbose output:%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); + printf("%s[xutils@examples]$ %s -rvd /opt -f \"*.cpp;*.java\" -ig test%s\n\n", XSTR_FMT_BOLD, pName, XSTR_FMT_RESET); +} + + +static int XSearch_ParseArgs(xsearch_args_t *pArgs, int argc, char *argv[]) +{ + memset(pArgs, 0, sizeof(xsearch_args_t)); + pArgs->sDirectory[0] = '.'; + pArgs->sDirectory[1] = '/'; + pArgs->sDirectory[2] = '\0'; + pArgs->nLinkCount = -1; + pArgs->nFileSize = -1; + int opt = 0; + + while ((opt = getopt(argc, argv, "d:f:g:p:t:l:n:i1:r1:v1:h1")) != -1) + { + switch (opt) + { + case 'd': + xstrncpy(pArgs->sDirectory, sizeof(pArgs->sDirectory), optarg); + break; + case 'f': + xstrncpy(pArgs->sFileName, sizeof(pArgs->sFileName), optarg); + break; + case 'g': + xstrncpy(pArgs->sText, sizeof(pArgs->sText), optarg); + break; + case 'p': + pArgs->nPermissions = XSearch_GetPermissins(optarg); + break; + case 't': + pArgs->nFileTypes = XSearch_GetFileTypes(optarg); + break; + case 'l': + pArgs->nLinkCount = atol(optarg); + break; + case 'n': + pArgs->nFileSize = atol(optarg); + break; + case 'i': + pArgs->bInsensitive = XTRUE; + break; + case 'r': + pArgs->bRecursive = XTRUE; + break; + case 'v': + pArgs->bVerbose = 1; + break; + case 'h': + default: + return 0; + } + } + + if (pArgs->bInsensitive && xstrused(pArgs->sFileName)) + { + xstrcase(pArgs->sFileName, XSTR_LOWER); + xstrcase(pArgs->sText, XSTR_LOWER); + } + + /* Validate opts */ + if (pArgs->nPermissions < 0 || + pArgs->nFileTypes < 0) + return 0; + + return 1; +} + +static void XSearch_ColorizeEntry(char *pOutput, size_t nSize, xfile_entry_t *pEntry) +{ + xbool_t bIsExec = pEntry->sPerm[XPERM_LEN-1] == 'x' ? XTRUE : XFALSE; + char *pColor = XSTR_EMPTY; + char *pBack = XSTR_EMPTY; + char *pFmt = XSTR_EMPTY; + + if (pEntry->eType == XF_SYMLINK) + { + if (pEntry->pRealPath == NULL) + { + pColor = XSTR_CLR_RED; + pBack = XSTR_BACK_BLACK; + pFmt = XSTR_FMT_BOLD; + } + else + { + pColor = XSTR_CLR_CYAN; + pFmt = XSTR_FMT_BOLD; + } + } + else if (pEntry->eType == XF_DIRECTORY) + { + pColor = XSTR_CLR_BLUE; + pFmt = XSTR_FMT_BOLD; + } + else if (pEntry->eType == XF_SOCKET) + { + pColor = XSTR_CLR_MAGENTA; + pFmt = XSTR_FMT_BOLD; + } + else if (pEntry->eType == XF_PIPE) + { + pColor = XSTR_CLR_YELLOW; + pBack = XSTR_BACK_BLACK; + } + else if (pEntry->eType == XF_REGULAR && bIsExec) + { + pColor = XSTR_CLR_GREEN; + pFmt = XSTR_FMT_BOLD; + } + else if (pEntry->eType == XF_CHAR_DEVICE || + pEntry->eType == XF_BLOCK_DEVICE) + { + pColor = XSTR_CLR_YELLOW; + pBack = XSTR_BACK_BLACK; + pFmt = XSTR_FMT_BOLD; + } + + size_t nOffset = 0; + while (!strncmp(&pEntry->sPath[nOffset], "./", 2)) nOffset += 2; + + xstrncpyf(pOutput, nSize, "%s%s%s%s%s%s", pColor, pFmt, pBack, + &pEntry->sPath[nOffset], pEntry->sName, XSTR_FMT_RESET); +} + +static void XSearch_ColorizeSymlink(char *pSimlink, size_t nSize, xfile_entry_t *pEntry) +{ + if (pEntry->eType == XF_SYMLINK) + { + if (pEntry->pRealPath != NULL) + { + struct stat statbuf; + xfile_entry_t linkEntry; + xstat(pEntry->pRealPath, &statbuf); + + XFile_CreateEntry(&linkEntry, NULL, pEntry->sLink, &statbuf); + XSearch_ColorizeEntry(pSimlink, nSize, &linkEntry); + } + else + { + xstrncpyf(pSimlink, nSize, "%s%s%s%s", + XSTR_FMT_BOLD, XSTR_BACK_RED, + pEntry->sLink, XSTR_FMT_RESET); + } + } +} + +static void XSearch_DisplayEntry(xfile_search_t *pSearch, xfile_entry_t *pEntry) +{ + char sEntry[XPATH_MAX], sLinkPath[XPATH_MAX], sTime[XSEARCH_TIME_LEN + 1]; + const char *pArrow = pEntry->eType == XF_SYMLINK ? " -> " : XSTR_EMPTY; + + sLinkPath[0] = sEntry[0] = sTime[0] = XSTR_NUL; + XSearch_ColorizeEntry(sEntry, sizeof(sEntry), pEntry); + XSearch_ColorizeSymlink(sLinkPath, sizeof(sLinkPath), pEntry); + + /* Do not display additional info if verbose is not set */ + if (!((xsearch_args_t*)pSearch->pUserCtx)->bVerbose) + { + xlog("%s%s%s", sEntry, pArrow, sLinkPath); + return; + } + + /* Get username and group from uid and gid */ + struct passwd *pws = getpwuid(pEntry->nUID); + struct group *grp = getgrgid(pEntry->nGID); + const char *pUname = pws ? pws->pw_name : XSTR_EMPTY; + const char *pGname = grp ? grp->gr_name : XSTR_EMPTY; + + /* Get last access time */ + const char *pTime = ctime(&pEntry->nTime); + if (pTime != NULL) xstrncpy(sTime, sizeof(sTime), &pTime[4]); + + /* Create size string with indentation */ + char sSize[XSEARCH_SIZE_LEN], sRound[XSEARCH_SIZE_LEN + 8]; + XBytesToUnit(sSize, sizeof(sSize), pEntry->nSize, XTRUE); + xstrnlcpyf(sRound, sizeof(sRound), 7, XSTR_SPACE_CHAR, "%s", sSize); + + xlog("%c%s %lu %s %s %s [%s] %s%s%s", + XFile_GetTypeChar(pEntry->eType), + pEntry->sPerm, pEntry->nLinkCount, + pUname, pGname, sRound, sTime, + sEntry, pArrow, sLinkPath); +} + +int XSearch_Callback(xfile_search_t *pSearch, xfile_entry_t *pEntry, const char *pMsg) +{ + if (pEntry != NULL) XSearch_DisplayEntry(pSearch, pEntry); + if (pMsg != NULL) xloge("%s (%s)", pMsg, strerror(errno)); + return XSTDNON; +} + +int main(int argc, char* argv[]) +{ + xlog_defaults(); + xsearch_args_t args; + xfile_search_t srcCtx; + + if (!XSearch_ParseArgs(&args, argc, argv)) + { + XSearch_Usage(argv[0]); + return XSTDERR; + } + + const char *pDirectory = xstrused(args.sDirectory) ? args.sDirectory : NULL; + const char *pFileName = xstrused(args.sFileName) ? args.sFileName : NULL; + + XFile_SearchInit(&srcCtx, pFileName); + xstrncpy(srcCtx.sText, sizeof(srcCtx.sText), args.sText); + srcCtx.nPermissions = args.nPermissions; + srcCtx.bInsensitive = args.bInsensitive; + srcCtx.bRecursive = args.bRecursive; + srcCtx.nFileTypes = args.nFileTypes; + srcCtx.nLinkCount = args.nLinkCount; + srcCtx.nFileSize = args.nFileSize; + srcCtx.callback = XSearch_Callback; + srcCtx.pUserCtx = &args; + + srcCtx.pInterrupted = &g_interrupted; + signal(SIGINT, signal_callback); + + XFile_Search(&srcCtx, pDirectory); + XFile_SearchDestroy(&srcCtx); + + return XSTDNON; +} diff --git a/examples/xtop.c b/examples/xtop.c new file mode 100644 index 0000000..e7ac4d9 --- /dev/null +++ b/examples/xtop.c @@ -0,0 +1,1587 @@ +/*! + * @file libxutils/examples/xtop.c + * + * This source is part of "libxutils" project + * 2015-2022 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of advanced system monitor based on the xUtils. + * Collect and monitor network, memory and CPU statistics in one window. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XTOP_VERSION_MAJ 0 +#define XTOP_VERSION_MIN 7 + +#define XTOP_SORT_DISABLE 0 +#define XTOP_SORT_BUSY 1 +#define XTOP_SORT_FREE 2 +#define XTOP_SORT_NAME 3 +#define XTOP_SORT_LEN 4 + +#define XTOP_CPU_HEADER " "\ + "CPU IDL "\ + "US KS "\ + "NI SI "\ + "HI IO "\ + "ST GT GN" + +#define XTOP_IFACE_HEADER \ + "IFACE "\ + "RX "\ + "TX "\ + "SUM "\ + "MAC IP" + +static int g_nInterrupted = 0; +extern char *optarg; + +typedef enum { + XTOP_INVALID = (uint8_t)0, + XTOP_NOTALLOWED, + XTOP_NOTFOUND, + XTOP_NETWORK, + XTOP_MEMORY, + XTOP_CPU, + XTOP_ALL +} xtop_request_t; + +typedef struct xtop_args_ { + xbool_t bExcludeCPU; + xbool_t bDaemon; + xbool_t bServer; + + char sLink[XLINK_MAX]; + char sAddr[XLINK_MAX]; + char sName[XNAME_MAX]; + char sLogs[XNAME_MAX]; + + size_t nIntervalU; + uint16_t nPort; + uint8_t nSort; + xpid_t nPID; +} xtop_args_t; + +void XTOPApp_SignalCallback(int sig) +{ + if (sig == 2) printf("\n"); + g_nInterrupted = 1; +} + +static char *XTOPApp_WhiteSpace(const int nLength) +{ + static char sRetVal[XSTR_TINY]; + xstrnul(sRetVal); + int i = 0; + + int nLen = XSTD_MIN(nLength, sizeof(sRetVal) - 1); + for (i = 0; i < nLen; i++) sRetVal[i] = ' '; + + sRetVal[i] = '\0'; + return sRetVal; +} + +void XTOPApp_DisplayUsage(const char *pName) +{ + int nLength = strlen(pName) + 6; + + printf("==================================================================\n"); + printf("XTOP v%d.%d - (c) 2022 Sandro Kalatozishvili (f4tb0y@protonmail.com)\n", + XTOP_VERSION_MAJ, XTOP_VERSION_MIN); + printf("==================================================================\n\n"); + + printf("CPU usage bar: %s[%s%slow-priority/%s%snormal/%s%skernel/%s%svirtualized%s %sused%%%s%s]%s\n", + XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_BLUE, XSTR_FMT_RESET, XSTR_CLR_GREEN, XSTR_FMT_RESET, XSTR_CLR_RED, + XSTR_FMT_RESET, XSTR_CLR_CYAN, XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); + + printf("Memory bar: %s[%s%sused/%s%sbuffers/%s%sshared/%s%scache%s %sused/total%s%s]%s\n", + XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_GREEN, XSTR_FMT_RESET, XSTR_CLR_BLUE, XSTR_FMT_RESET, XSTR_CLR_MAGENTA, + XSTR_FMT_RESET, XSTR_CLR_YELLOW, XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); + + printf("Swap bar: %s[%s%sused/%s%scache%s %sused/total%s%s]%s\n\n", + XSTR_FMT_BOLD, XSTR_FMT_RESET, XSTR_CLR_RED, XSTR_FMT_RESET, XSTR_CLR_YELLOW, + XSTR_FMT_RESET, XSTR_FMT_DIM, XSTR_FMT_RESET, XSTR_FMT_BOLD, XSTR_FMT_RESET); + + printf("Usage: %s [-i ] [-m ] [-s ] [-t ] [-c]\n", pName); + printf(" %s [-a ] [-p ] [-l ] [-r ] [-d] [-h]\n\n", XTOPApp_WhiteSpace(nLength)); + + printf("Options are:\n"); + printf(" %s-a%s # Address of the listener server\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-p%s # Port of the listener server\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-i%s # Interface name to display on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-m%s # Monitoring interval seconds\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-l%s # Output directory path for logs\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-r%s # Remote endpoint link to monitor\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-s%s # Sort result by selected type\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-t%s # Track process CPU and memory usage\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-c%s # Exclude additional CPU info \n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-d%s # Run as server daemon \n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %s-h%s # Print version and usage\n\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + + printf("Sort types:\n"); + printf(" %sb%s: Busy on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sf%s: Free on top\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + printf(" %sn%s: Sort by name\n\n", XSTR_CLR_CYAN, XSTR_FMT_RESET); + + printf("Examples:\n"); + printf("1) %s -m 2 -s b -p 2274\n", pName); + printf("2) %s -s b -p 2274 -i enp4s0\n", pName); + printf("3) %s -r http://remote.srv/endpint/\n\n", pName); +} + +uint8_t XTOPApp_GetSortType(const char *pArg) +{ + if (pArg == NULL) return XTOP_SORT_DISABLE; + else if (*pArg == 'b') return XTOP_SORT_BUSY; + else if (*pArg == 'f') return XTOP_SORT_FREE; + else if (*pArg == 'n') return XTOP_SORT_NAME; + return XTOP_SORT_DISABLE; +} + +int XTOPApp_ParseArgs(xtop_args_t *pArgs, int argc, char *argv[]) +{ + pArgs->bExcludeCPU = XFALSE; + pArgs->bDaemon = XFALSE; + pArgs->bServer = XFALSE; + pArgs->nSort = XTOP_SORT_LEN; + + xstrnul(pArgs->sAddr); + xstrnul(pArgs->sLogs); + xstrnul(pArgs->sLink); + xstrnul(pArgs->sName); + + pArgs->nIntervalU = 0; + pArgs->nPort = 0; + pArgs->nPID = 0; + int nChar = 0; + + while ((nChar = getopt(argc, argv, "a:i:l:m:p:r:s:t:c1:d1:h1")) != -1) + { + switch (nChar) + { + case 'a': + xstrncpy(pArgs->sAddr, sizeof(pArgs->sAddr), optarg); + break; + case 'i': + xstrncpy(pArgs->sName, sizeof(pArgs->sName), optarg); + break; + case 'l': + xstrncpy(pArgs->sLogs, sizeof(pArgs->sLogs), optarg); + break; + case 'r': + xstrncpy(pArgs->sLink, sizeof(pArgs->sLink), optarg); + break; + case 's': + pArgs->nSort = XTOPApp_GetSortType(optarg); + break; + case 'm': + pArgs->nIntervalU = atoi(optarg); + break; + case 'p': + pArgs->nPort = atoi(optarg); + break; + case 't': + pArgs->nPID = atoi(optarg); + break; + case 'c': + pArgs->bExcludeCPU = XTRUE; + break; + case 'd': + pArgs->bDaemon = XTRUE; + break; + case 'h': + default: + return 0; + } + } + + pArgs->bServer = xstrused(pArgs->sAddr) && pArgs->nPort; + if (pArgs->bDaemon && !pArgs->bServer) + { + xloge("Missing addr/port arguments for daemon"); + return XFALSE; + } + + if (!pArgs->nIntervalU) pArgs->nIntervalU = XTOP_INTERVAL_USEC; + else pArgs->nIntervalU *= XTOP_INTERVAL_USEC; + + if (xstrused(pArgs->sLogs)) + { + xlog_path(pArgs->sLogs); + xlog_file(XTRUE); + } + + if (xstrused(pArgs->sName)) + { + char sIfcPath[XPATH_MAX]; + xstrncpyf(sIfcPath, sizeof(sIfcPath), "%s/%s", XSYS_CLASS_NET, pArgs->sName); + + if (!XPath_Exists(sIfcPath)) + { + xloge("Interface not found: %s", pArgs->sName); + return XFALSE; + } + } + + return XTRUE; +} + +int XTOPApp_CompareCPUs(const void *pData1, const void *pData2, void *pContext) +{ + xtop_args_t *pArgs = (xtop_args_t*)pContext; + xcpu_info_t *pInfo1 = (xcpu_info_t*)((xarray_data_t*)pData1)->pData; + xcpu_info_t *pInfo2 = (xcpu_info_t*)((xarray_data_t*)pData2)->pData; + + return (pArgs->nSort == XTOP_SORT_BUSY) ? + (int)pInfo1->nIdleTime - (int)pInfo2->nIdleTime: + (int)pInfo2->nIdleTime - (int)pInfo1->nIdleTime; +} + +int XTOPApp_CompareIFaces(const void *pData1, const void *pData2, void *pContext) +{ + xtop_args_t *pArgs = (xtop_args_t*)pContext; + xnet_iface_t *pIface1 = (xnet_iface_t*)((xarray_data_t*)pData1)->pData; + xnet_iface_t *pIface2 = (xnet_iface_t*)((xarray_data_t*)pData2)->pData; + + if (pArgs->nSort == XTOP_SORT_LEN) return (int)strlen(pIface1->sName) - (int)strlen(pIface2->sName); + else if (pArgs->nSort == XTOP_SORT_NAME) return strcmp(pIface1->sName, pIface2->sName); + + int nData1 = (int)pIface1->nBytesReceivedPerSec + (int)pIface1->nBytesSentPerSec; + int nData2 = (int)pIface2->nBytesReceivedPerSec + (int)pIface2->nBytesSentPerSec; + return (pArgs->nSort == XTOP_SORT_BUSY) ? nData2 - nData1 : nData1 - nData2; +} + +int XTOPApp_FillCPUBar(xcli_bar_t *pBar, xcpu_info_t *pCore, char *pDst, size_t nSize) +{ + if (pDst == NULL || !nSize) return XSTDNON; + char sNormal[XLINE_MAX], sKernel[XLINE_MAX]; + char sVirt[XLINE_MAX], sLow[XLINE_MAX]; + + /* Unpack raw percentage information */ + float fLow = XU32ToFloat(pCore->nUserSpaceNiced); + float fVirt = XU32ToFloat(pCore->nStealTime); + float fNormal = XU32ToFloat(pCore->nUserSpace); + float fKernel = XU32ToFloat(pCore->nKernelSpace); + fKernel += XU32ToFloat(pCore->nSoftInterrupts); + fKernel += XU32ToFloat(pCore->nHardInterrupts); + fKernel += XU32ToFloat(pCore->nIOWait); + + /* Calculate percentage */ + size_t nMaxSize = pBar->nBarLength; + size_t nNormal = nMaxSize * (size_t)floor(fNormal) / 100; + size_t nKernel = nMaxSize * (size_t)floor(fKernel) / 100; + size_t nVirt = nMaxSize * (size_t)floor(fVirt) / 100; + size_t nLow = nMaxSize * (size_t)floor(fLow) / 100; + size_t nSum = nLow + nVirt + nNormal + nKernel; + + /* Round the calculated results to improve bar fill accurracy */ + if (fNormal > 0. && !nNormal && nSum < nMaxSize) { nNormal++; nSum++; } + if (fKernel > 0. && !nKernel && nSum < nMaxSize) { nKernel++; nSum++; } + if (fVirt > 0. && !nVirt && nSum < nMaxSize) { nVirt++; nSum++; } + if (fLow > 0. && !nLow && nSum < nMaxSize) nLow++; + + /* Fill partial results with the bar used character */ + xstrfill(sNormal, sizeof(sNormal), nNormal, pBar->cLoader); + xstrfill(sKernel, sizeof(sKernel), nKernel, pBar->cLoader); + xstrfill(sVirt, sizeof(sVirt), nVirt, pBar->cLoader); + xstrfill(sLow, sizeof(sLow), nLow, pBar->cLoader); + + /* Create colorized line for CPU usage bar */ + return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s%s%s%s%s%s%s", + XSTR_CLR_BLUE, sLow, XSTR_FMT_RESET, + XSTR_CLR_GREEN, sNormal, XSTR_FMT_RESET, + XSTR_CLR_RED, sKernel, XSTR_FMT_RESET, + XSTR_CLR_CYAN, sVirt, XSTR_FMT_RESET); +} + +XSTATUS XTOPApp_AddCPULoadBar(xcli_wind_t *pWin, xcli_bar_t *pBar, xcpu_stats_t *pCPU) +{ + char sFirst[XLINE_MAX], sSecond[XLINE_MAX], sUsed[XLINE_MAX]; + uint16_t i, nNext = pCPU->nCoreCount; + uint16_t nEdge = 0, nUsedCount = 0; + + xstrnul(pBar->sSuffix); + xstrnul(sSecond); + xstrnul(sFirst); + + XProgBar_UpdateWindowSize(pBar); + pBar->frameSize.nWinColumns /= 2; + + for (i = 0; i < pCPU->nCoreCount; i++) + { + xcpu_info_t *pCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, i); + if (pCore != NULL) + { + if (nUsedCount >= pCPU->nCoreCount) break; + else if (nEdge && i == nEdge) continue; + + nNext = i + pCPU->nCoreCount / 2; + if (!nEdge) nEdge = nNext; + nUsedCount++; + + char sCore[XSTR_TINY]; + xstrnlcpyf(sCore, sizeof(sCore), 5, XSTR_SPACE_CHAR, "%d", pCore->nID); + xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", sCore); + + pBar->fPercent = XU32ToFloat(pCore->nUserSpace) + XU32ToFloat(pCore->nUserSpaceNiced); + pBar->fPercent += XU32ToFloat(pCore->nKernelSpace) + XU32ToFloat(pCore->nSoftInterrupts); + pBar->fPercent += XU32ToFloat(pCore->nHardInterrupts) + XU32ToFloat(pCore->nIOWait); + pBar->fPercent += XU32ToFloat(pCore->nStealTime); + + xstrnul(sUsed); + xbool_t bHidePct = XProgBar_CalculateBounds(pBar); + XTOPApp_FillCPUBar(pBar, pCore, sUsed, sizeof(sUsed)); + XProgBar_GetOutputAdv(pBar, sFirst, sizeof(sFirst), sUsed, bHidePct); + + if (i == nNext || nNext >= pCPU->nCoreCount) + { + xstrfill(sSecond, sizeof(sSecond), pBar->frameSize.nWinColumns, XSTR_SPACE_CHAR); + return XWindow_AddLineFmt(pWin, "%s%s", sFirst, sSecond); + } + + xcpu_info_t *pSecondCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, nNext); + if (pSecondCore != NULL) + { + xstrnlcpyf(sCore, sizeof(sCore), 5, XSTR_SPACE_CHAR, "%d", pSecondCore->nID); + xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", sCore); + + pBar->fPercent = XU32ToFloat(pSecondCore->nUserSpace) + XU32ToFloat(pSecondCore->nUserSpaceNiced); + pBar->fPercent += XU32ToFloat(pSecondCore->nKernelSpace) + XU32ToFloat(pSecondCore->nSoftInterrupts); + pBar->fPercent += XU32ToFloat(pSecondCore->nHardInterrupts) + XU32ToFloat(pSecondCore->nIOWait); + pBar->fPercent += XU32ToFloat(pSecondCore->nStealTime); + + xstrnul(sUsed); + xbool_t bHidePct = XProgBar_CalculateBounds(pBar); + XTOPApp_FillCPUBar(pBar, pSecondCore, sUsed, sizeof(sUsed)); + XProgBar_GetOutputAdv(pBar, sSecond, sizeof(sSecond), sUsed, bHidePct); + + XWindow_AddLineFmt(pWin, "%s%s", sFirst, sSecond); + nUsedCount++; + } + } + } + + return XSTDOK; +} + +int XTOPApp_FillMemoryBar(xcli_bar_t *pBar, xmem_info_t *pMemInfo, char *pDst, size_t nSize) +{ + if (pDst == NULL || !nSize) return XSTDNON; + char sBuffers[XLINE_MAX], sUsed[XLINE_MAX]; + char sShared[XLINE_MAX], sCached[XLINE_MAX]; + + size_t nMaxSize = pBar->nBarLength; + size_t nMaxUsed = pBar->nBarUsed; + + size_t nTotalUsed = pMemInfo->nMemoryTotal - pMemInfo->nMemoryFree; + size_t nCached = pMemInfo->nMemoryCached - pMemInfo->nMemoryShared; + size_t nUsed = nTotalUsed - (pMemInfo->nBuffers + pMemInfo->nMemoryCached); + + double fBuffers = nTotalUsed ? (double)100 / nTotalUsed * pMemInfo->nBuffers : 0.; + double fShared = nTotalUsed ? (double)100 / nTotalUsed * pMemInfo->nMemoryShared : 0.; + double fCached = nTotalUsed ? (double)100 / nTotalUsed * nCached : 0.; + double fUsed = nTotalUsed ? (double)100 / nTotalUsed * nUsed : 0.; + + size_t nBuffersPct = nMaxUsed * (size_t)floor(fBuffers) / 100; + size_t nSharedPct = nMaxUsed * (size_t)floor(fShared) / 100; + size_t nCachedPct = nMaxUsed * (size_t)floor(fCached) / 100; + size_t nUsedPct = nMaxUsed * (size_t)floor(fUsed) / 100; + size_t nSum = nUsedPct + nSharedPct + nBuffersPct + nCachedPct; + + /* Round the calculated results to improve bar fill accurracy */ + if (fBuffers > 0. && !nBuffersPct && nSum < nMaxSize) { nBuffersPct++; nSum++; } + if (fShared > 0. && !nSharedPct && nSum < nMaxSize) { nSharedPct++; nSum++; } + if (fCached > 0. && !nCachedPct && nSum < nMaxSize) { nCachedPct++; nSum++; } + if (fUsed > 0. && !nUsedPct && nSum < nMaxSize) nUsedPct++; + + xstrfill(sBuffers, sizeof(sBuffers), nBuffersPct, pBar->cLoader); + xstrfill(sShared, sizeof(sShared), nSharedPct, pBar->cLoader); + xstrfill(sCached, sizeof(sCached), nCachedPct, pBar->cLoader); + xstrfill(sUsed, sizeof(sUsed), nUsedPct, pBar->cLoader); + + return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s%s%s%s%s%s%s", + XSTR_CLR_GREEN, sUsed, XSTR_FMT_RESET, + XSTR_CLR_BLUE, sBuffers, XSTR_FMT_RESET, + XSTR_CLR_MAGENTA, sShared, XSTR_FMT_RESET, + XSTR_CLR_YELLOW, sCached, XSTR_FMT_RESET); +} + +int XTOPApp_FillSwapBar(xcli_bar_t *pBar, xmem_info_t *pMemInfo, char *pDst, size_t nSize) +{ + if (pDst == NULL || !nSize) return XSTDNON; + char sUsed[XLINE_MAX], sCached[XLINE_MAX]; + + size_t nMaxSize = pBar->nBarLength; + size_t nMaxUsed = pBar->nBarUsed; + + /* Calculate swap and cache usage percents */ + size_t nSwapUsed = pMemInfo->nSwapTotal - pMemInfo->nSwapFree - pMemInfo->nSwapCached; + double fCached = nSwapUsed ? (double)100 / nSwapUsed * pMemInfo->nSwapCached : 0.; + double fUsed = nSwapUsed ? (double)100 / pMemInfo->nSwapTotal * nSwapUsed : 0.; + + /* Calculate swap and cached percents in usage bar */ + size_t nCachedPct = nMaxUsed * (size_t)floor(fCached) / 100; + size_t nUsedPct = nMaxUsed * (size_t)floor(fUsed) / 100; + size_t nSum = nUsedPct + nCachedPct; + + /* Round the calculated results to improve bar fill accurracy */ + if (fCached > 0. && !nCachedPct && nSum < nMaxSize) { nCachedPct++; nSum++; } + if (fUsed > 0. && !nUsedPct && nSum < nMaxSize) nUsedPct++; + + /* Fill partial results with the bar used character */ + xstrfill(sCached, sizeof(sCached), nCachedPct, pBar->cLoader); + xstrfill(sUsed, sizeof(sUsed), nUsedPct, pBar->cLoader); + + return xstrncpyf(pDst, nSize, "%s%s%s%s%s%s", + XSTR_CLR_RED, sUsed, XSTR_FMT_RESET, + XSTR_CLR_YELLOW, sCached, XSTR_FMT_RESET); +} + +XSTATUS XTOPApp_AddOverallBar(xcli_wind_t *pWin, xcli_bar_t *pBar, xmem_info_t *pMemInfo, xcpu_stats_t *pCPU) +{ + if (pMemInfo->nMemoryTotal < pMemInfo->nMemoryAvail) return XSTDNON; + char sLine[XLINE_MAX], sBuff[XSTR_TINY]; + char sUsed[XSTR_TINY], sCache[XSTR_TINY]; + + /* Calculate memory usage percentage */ + size_t nTotalUsed = pMemInfo->nMemoryTotal - pMemInfo->nMemoryFree; + size_t nUsed = nTotalUsed - (pMemInfo->nBuffers + pMemInfo->nMemoryCached); + pBar->fPercent = nTotalUsed ? (double)100 / pMemInfo->nMemoryTotal * nTotalUsed : 0.; + + /* Create memory usage bar */ + XKBToUnit(sUsed, sizeof(sUsed), nUsed, XTRUE); + XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nMemoryTotal, XTRUE); + xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", " Mem"); + xstrncpyf(pBar->sSuffix, sizeof(pBar->sSuffix), + "%s%s/%s%s", XSTR_FMT_DIM, sUsed, sBuff, XSTR_FMT_RESET); + + xstrnul(sUsed); + xbool_t bHidePct = XProgBar_CalculateBounds(pBar); + XTOPApp_FillMemoryBar(pBar, pMemInfo, sUsed, sizeof(sUsed)); + XProgBar_GetOutputAdv(pBar, sLine, sizeof(sLine), sUsed, bHidePct); + + /* Create and append memory usage info next to memory bar */ + XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nBuffers, XTRUE); + XKBToUnit(sUsed, sizeof(sUsed), pMemInfo->nMemoryShared, XTRUE); + XKBToUnit(sCache, sizeof(sCache), pMemInfo->nMemoryCached, XTRUE); + XWindow_AddLineFmt(pWin, "%s " + "%sBuff:%s %s, " + "%sShared:%s %s, " + "%sCached:%s %s", sLine, + XSTR_CLR_CYAN, XSTR_FMT_RESET, sBuff, + XSTR_CLR_CYAN, XSTR_FMT_RESET, sUsed, + XSTR_CLR_CYAN, XSTR_FMT_RESET, sCache); + + /* Calculate swap usage percentage */ + if (pMemInfo->nSwapTotal < pMemInfo->nSwapFree) return XSTDNON; + size_t nSwapUsed = pMemInfo->nSwapTotal - pMemInfo->nSwapFree - pMemInfo->nSwapCached; + pBar->fPercent = nSwapUsed ? (double)100 / pMemInfo->nSwapTotal * nSwapUsed : 0.; + + /* Create swap usage bar */ + XKBToUnit(sUsed, sizeof(sUsed), nSwapUsed, XTRUE); + XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nSwapTotal, XTRUE); + xstrnclr(pBar->sPrefix, sizeof(pBar->sPrefix), XSTR_CLR_CYAN, "%s", " Swp"); + xstrncpyf(pBar->sSuffix, sizeof(pBar->sSuffix), + "%s%s/%s%s", XSTR_FMT_DIM, sUsed, sBuff, XSTR_FMT_RESET); + + xstrnul(sUsed); + bHidePct = XProgBar_CalculateBounds(pBar); + XTOPApp_FillSwapBar(pBar, pMemInfo, sUsed, sizeof(sUsed)); + XProgBar_GetOutputAdv(pBar, sLine, sizeof(sLine), sUsed, bHidePct); + + XKBToUnit(sCache, sizeof(sCache), pMemInfo->nSwapCached, XTRUE); + XWindow_AddLineFmt(pWin, "%s " + "%sSwp Cached:%s %s, " + "%sLoad avg:%s %s%.2f%s %s%.2f%s %s%.2f%s", + sLine, XSTR_CLR_CYAN, XSTR_FMT_RESET, + sCache, XSTR_CLR_CYAN, XSTR_FMT_RESET, + XSTR_FMT_BOLD, XU32ToFloat(pCPU->nLoadAvg[0]), XSTR_FMT_RESET, + XSTR_CLR_LIGHT_CYAN, XU32ToFloat(pCPU->nLoadAvg[1]), XSTR_FMT_RESET, + XSTR_CLR_LIGHT_BLUE, XU32ToFloat(pCPU->nLoadAvg[2]), XSTR_FMT_RESET); + + /* Create half-empry line for pretty print */ + XProgBar_UpdateWindowSize(pBar); pBar->frameSize.nWinColumns /= 2; + xstrfill(sLine, sizeof(sLine), pBar->frameSize.nWinColumns, XSTR_SPACE_CHAR); + + /* Create and append process track info next to swap bar */ + XKBToUnit(sUsed, sizeof(sUsed), pMemInfo->nResidentMemory, XTRUE); + XKBToUnit(sBuff, sizeof(sBuff), pMemInfo->nVirtualMemory, XTRUE); + return XWindow_AddLineFmt(pWin, "%s%sRes:%s %s, %sVirt:%s %s, %sUS:%s %.2f, %sKS:%s %.2f", sLine, + XSTR_CLR_CYAN, XSTR_FMT_RESET, sUsed, XSTR_CLR_CYAN, XSTR_FMT_RESET, sBuff, + XSTR_CLR_CYAN, XSTR_FMT_RESET, XU32ToFloat(pCPU->usage.nUserSpaceUsage), + XSTR_CLR_CYAN, XSTR_FMT_RESET, XU32ToFloat(pCPU->usage.nKernelSpace)); +} + +void XTOPApp_AddCPUInfoUnit(char *pLine, size_t nSize, double fPct, xbool_t bIdle) +{ + char sBuff[XSTR_TINY]; + const char *pColor; + + if (bIdle) + { + if (fPct > 50.) pColor = XSTR_CLR_GREEN; + else if (fPct <= 20.) pColor = XLOG_COLOR_RED; + else pColor = XLOG_COLOR_YELLOW; + } + else + { + if (fPct < 50.) pColor = XSTR_CLR_NONE; + else if (fPct >= 80.) pColor = XLOG_COLOR_RED; + else pColor = XLOG_COLOR_YELLOW; + } + + size_t nIdleLen = xstrnclr(sBuff, sizeof(sBuff), pColor, "%.2f", fPct); + nIdleLen -= xstrextra(sBuff, nIdleLen, 0, NULL, NULL); + + if (nIdleLen < 8) + { + char sEmpty[XSTR_TINY]; + xstrfill(sEmpty, sizeof(sEmpty), 8 - nIdleLen, XSTR_SPACE_CHAR); + xstrncat(pLine, nSize, "%s%s", sEmpty, sBuff); + } +} + +XSTATUS XTOPApp_AddCPUInfo(xcli_wind_t *pWin, xcpu_info_t *pCore) +{ + char sLine[XLINE_MAX]; + char sCore[XSTR_TINY]; + + if (pCore->nID >= 0) + { + xstrnlcpyf(sCore, sizeof(sCore), 4, XSTR_SPACE_CHAR, "%d", pCore->nID); + xstrncpyf(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sCore, XSTR_FMT_RESET); + } + else + { + xstrnlcpyf(sCore, sizeof(sCore), 4, XSTR_SPACE_CHAR, "%s", "s"); + xstrncpyf(sLine, sizeof(sLine), "%s%s%s%s", XSTR_FMT_BOLD, XSTR_FMT_ITALIC, sCore, XSTR_FMT_RESET); + } + + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nIdleTime), XTRUE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nUserSpace), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nKernelSpace), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nUserSpaceNiced), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nSoftInterrupts), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nHardInterrupts), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nIOWait), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nStealTime), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nGuestTime), XFALSE); + XTOPApp_AddCPUInfoUnit(sLine, sizeof(sLine), XU32ToFloat(pCore->nGuestNiced), XFALSE); + return XWindow_AddLineFmt(pWin, "%s", sLine); +} + +XSTATUS XTOPApp_AddCPUExtra(xcli_wind_t *pWin, xtop_args_t *pArgs, xcli_bar_t *pBar, xmem_info_t *pMemInfo, xcpu_stats_t *pCPU) +{ + XWindow_AddAligned(pWin, XTOP_CPU_HEADER, XSTR_BACK_BLUE, XCLI_LEFT); + XSTATUS nStatus = XTOPApp_AddCPUInfo(pWin, &pCPU->sum); + if (nStatus <= 0) return nStatus; + + if (pArgs->nSort && pCPU->nCoreCount && + pArgs->nSort != XTOP_SORT_NAME && + pArgs->nSort != XTOP_SORT_LEN) + XArray_Sort(&pCPU->cores, XTOPApp_CompareCPUs, pArgs); + + uint16_t i; + for (i = 0; i < pCPU->nCoreCount; i++) + { + xcpu_info_t *pCore = (xcpu_info_t*)XArray_GetData(&pCPU->cores, i); + if (pCore != NULL) nStatus = XTOPApp_AddCPUInfo(pWin, pCore); + } + + return nStatus; +} + +XSTATUS XTOPApp_AddInterface(xcli_wind_t *pWin, xtop_args_t *pArgs, xnet_iface_t *pIface, size_t nLength) +{ + char sLine[XLINE_MAX], sName[XSTR_TINY], sRound[XSTR_TINY], sData[XSTR_TINY]; + xstrnlcpyf(sName, sizeof(sName), nLength + 1, XSTR_SPACE_CHAR, "%s", pIface->sName); + xstrncpy(sLine, sizeof(sLine), sName); + + XBytesToUnit(sData, sizeof(sData), pIface->nBytesReceivedPerSec, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + + XBytesToUnit(sData, sizeof(sData), pIface->nBytesSentPerSec, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + + uint64_t nSum = pIface->nBytesReceivedPerSec + pIface->nBytesSentPerSec; + XBytesToUnit(sData, sizeof(sData), nSum, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + + xstrnlcpyf(sRound, sizeof(sRound), strlen(pIface->sHWAddr) + 8, XSTR_SPACE_CHAR, "%s", pIface->sHWAddr); + if (strncmp(pIface->sHWAddr, XNET_HWADDR_DEFAULT, 17)) xstrncat(sLine, sizeof(sLine), "%s", sRound); + else xstrncat(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sRound, XSTR_FMT_RESET); + + xstrnlcpyf(sRound, sizeof(sRound), strlen(pIface->sIPAddr) + 8, XSTR_SPACE_CHAR, "%s", pIface->sIPAddr); + if (strncmp(pIface->sIPAddr, XNET_IPADDR_DEFAULT, 7)) xstrncat(sLine, sizeof(sLine), "%s", sRound); + else xstrncat(sLine, sizeof(sLine), "%s%s%s", XSTR_FMT_DIM, sRound, XSTR_FMT_RESET); + + return XWindow_AddLineFmt(pWin, "%s", sLine); +} + +XSTATUS XTOPApp_AddNetworkInfo(xcli_wind_t *pWin, xtop_args_t *pArgs, xarray_t *pIfaces) +{ + if (pArgs->nSort) XArray_Sort(pIfaces, XTOPApp_CompareIFaces, pArgs); + size_t nTrackLen = strlen(pArgs->sName); + size_t i, nLength = 0; + int nTrackID = -1; + + size_t nSumRX, nSumTX; + nSumRX = nSumTX = 0; + + for (i = 0; i < pIfaces->nUsed; i++) + { + xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, i); + if (pIface == NULL) continue; + + nSumRX += pIface->nBytesReceivedPerSec; + nSumTX += pIface->nBytesSentPerSec; + + if (xstrused(pIface->sName) == XTRUE && nTrackLen > 0 && nTrackID < 0 && + !strncmp(pArgs->sName, pIface->sName, nTrackLen)) nTrackID = (int)i; + + size_t nNextLength = strlen(pIface->sName); + if (nNextLength > nLength) nLength = nNextLength; + } + + char sLine[XLINE_MAX], sRound[XSTR_TINY], sData[XSTR_TINY]; + size_t nPreHdr = nLength > 4 ? nLength - 4 : nLength; + xstrfill(sLine, sizeof(sLine), nPreHdr, XSTR_SPACE_CHAR); + xstrncat(sLine, sizeof(sLine), "%s", XTOP_IFACE_HEADER); + XWindow_AddAligned(pWin, sLine, XSTR_BACK_BLUE, XCLI_LEFT); + + if (nTrackID >= 0) + { + xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, nTrackID); + if (pIface != NULL) XTOPApp_AddInterface(pWin, pArgs, pIface, nLength); + } + + for (i = 0; i < pIfaces->nUsed; i++) + { + if (nTrackID >= 0 && i == nTrackID) continue; + xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, i); + if (pIface != NULL) XTOPApp_AddInterface(pWin, pArgs, pIface, nLength); + } + + xstrnlcpyf(sLine, sizeof(sLine), nLength + 1, XSTR_SPACE_CHAR, "%s", "total"); + XBytesToUnit(sData, sizeof(sData), nSumRX, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + + XBytesToUnit(sData, sizeof(sData), nSumTX, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + + XBytesToUnit(sData, sizeof(sData), nSumRX + nSumTX, XFALSE); + xstrnlcpyf(sRound, sizeof(sRound), 18, XSTR_SPACE_CHAR, "%s", sData); + xstrncat(sLine, sizeof(sLine), "%s/s", sRound); + return XWindow_AddAligned(pWin, sLine, XSTR_CLR_LIGHT_CYAN, XCLI_LEFT); +} + +int XTOPApp_GetJSONStats(xtop_stats_t *pStats, xjson_t *pJson) +{ + xcpu_stats_t *pCpuStats = &pStats->cpuStats; + xmem_info_t *pMemInfo = &pStats->memInfo; + + XArray_Destroy(&pStats->netIfaces); + XArray_Destroy(&pCpuStats->cores); + + xjson_obj_t *pCPUObj = XJSON_GetObject(pJson->pRootObj, "cpu"); + if (pCPUObj == NULL) + { + xloge("Response does not contain CPU object in JSON"); + return XSTDERR; + } + + xjson_obj_t *pLoadAvgObj = XJSON_GetObject(pCPUObj, "loadAverage"); + if (pCPUObj == NULL) + { + xloge("Response does not contain CPU loadAverage object in JSON"); + return XSTDERR; + } + + size_t i, nLength = XJSON_GetArrayLength(pLoadAvgObj); + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pLoadAvgObj, i); + if (pArrItemObj != NULL) + { + float fValue = XJSON_GetFloat(XJSON_GetObject(pArrItemObj, "value")); + const char* pInter = XJSON_GetString(XJSON_GetObject(pArrItemObj, "interval")); + if (pInter == NULL) continue; + + if (!strncmp(pInter, "1m", 2)) pCpuStats->nLoadAvg[0] = XFloatToU32(fValue); + if (!strncmp(pInter, "5m", 2)) pCpuStats->nLoadAvg[1] = XFloatToU32(fValue); + if (!strncmp(pInter, "15m", 3)) pCpuStats->nLoadAvg[2] = XFloatToU32(fValue); + } + } + + xjson_obj_t *pUsageObj = XJSON_GetObject(pCPUObj, "usage"); + if (pUsageObj == NULL) + { + xloge("Response does not contain CPU usage object in JSON"); + return XSTDERR; + } + + xjson_obj_t *pProcObj = XJSON_GetObject(pUsageObj, "process"); + if (pProcObj == NULL) + { + xloge("Response does not contain CPU process object in JSON"); + return XSTDERR; + } + + xjson_obj_t *pCoresObj = XJSON_GetObject(pUsageObj, "cores"); + if (pCoresObj == NULL) + { + xloge("Response does not contain CPU core object in JSON"); + return XSTDERR; + } + + float fKernelSpace = XJSON_GetFloat(XJSON_GetObject(pProcObj, "kernelSpace")); + float fUserSpace = XJSON_GetFloat(XJSON_GetObject(pProcObj, "userSpace")); + pCpuStats->usage.nKernelSpaceUsage = XFloatToU32(fKernelSpace); + pCpuStats->usage.nUserSpaceUsage = XFloatToU32(fUserSpace); + + pCpuStats->sum.nSoftInterrupts = XJSON_GetU32(XJSON_GetObject(pUsageObj, "softInterrupts")); + pCpuStats->sum.nHardInterrupts = XJSON_GetU32(XJSON_GetObject(pUsageObj, "hardInterrupts")); + pCpuStats->sum.nUserSpaceNiced = XJSON_GetU32(XJSON_GetObject(pUsageObj, "userSpaceNiced")); + pCpuStats->sum.nKernelSpace = XJSON_GetU32(XJSON_GetObject(pUsageObj, "kernelSpace")); + pCpuStats->sum.nUserSpace = XJSON_GetU32(XJSON_GetObject(pUsageObj, "userSpace")); + pCpuStats->sum.nIdleTime = XJSON_GetU32(XJSON_GetObject(pUsageObj, "idleTime")); + pCpuStats->sum.nIOWait = XJSON_GetU32(XJSON_GetObject(pUsageObj, "ioWait")); + pCpuStats->sum.nStealTime = XJSON_GetU32(XJSON_GetObject(pUsageObj, "stealTime")); + pCpuStats->sum.nGuestTime = XJSON_GetU32(XJSON_GetObject(pUsageObj, "guestTime")); + pCpuStats->sum.nGuestNiced = XJSON_GetU32(XJSON_GetObject(pUsageObj, "guestNiced")); + + nLength = XJSON_GetArrayLength(pCoresObj); + XSYNC_ATOMIC_SET(&pStats->cpuStats.nCoreCount, nLength); + + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pCoresObj, i); + if (pArrItemObj != NULL) + { + xcpu_info_t *pInfo = (xcpu_info_t*)malloc(sizeof(xcpu_info_t)); + if (pInfo == NULL) + { + xloge("Failed to allocate memory for CPU core object: %d", errno); + return XSTDERR; + } + + pInfo->nSoftInterrupts = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "softInterrupts")); + pInfo->nHardInterrupts = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "hardInterrupts")); + pInfo->nUserSpaceNiced = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "userSpaceNiced")); + pInfo->nKernelSpace = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "kernelSpace")); + pInfo->nUserSpace = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "userSpace")); + pInfo->nIdleTime = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "idleTime")); + pInfo->nIOWait = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "ioWait")); + pInfo->nStealTime = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "stealTime")); + pInfo->nGuestTime = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "guestTime")); + pInfo->nGuestNiced = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "guestNiced")); + pInfo->nID = XJSON_GetInt(XJSON_GetObject(pArrItemObj, "id")); + + if (XArray_AddData(&pCpuStats->cores, pInfo, 0) < 0) + { + xloge("Failed to store CPU core object: %d", errno); + return XSTDERR; + } + } + } + + xjson_obj_t *pMemoryObj = XJSON_GetObject(pJson->pRootObj, "memory"); + if (pMemoryObj == NULL) + { + xloge("Response does not contain memory object in JSON"); + return XSTDERR; + } + + pMemInfo->nBuffers = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memBuffered")); + pMemInfo->nReclaimable = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memReclaimable")); + pMemInfo->nResidentMemory = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memResident")); + pMemInfo->nVirtualMemory = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memVirtual")); + pMemInfo->nMemoryCached = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memCached")); + pMemInfo->nMemoryShared = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memShared")); + pMemInfo->nMemoryAvail = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memAvail")); + pMemInfo->nMemoryTotal = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memTotal")); + pMemInfo->nMemoryFree = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "memFree")); + pMemInfo->nSwapCached = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "swapCached")); + pMemInfo->nSwapTotal = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "swapTotal")); + pMemInfo->nSwapFree = XJSON_GetU64(XJSON_GetObject(pMemoryObj, "swapFree")); + + xjson_obj_t *pNetObj = XJSON_GetObject(pJson->pRootObj, "network"); + if (pNetObj == NULL) + { + xloge("Response does not contain network object in JSON"); + return XSTDERR; + } + + nLength = XJSON_GetArrayLength(pNetObj); + for (i = 0; i < nLength; i++) + { + xjson_obj_t *pArrItemObj = XJSON_GetArrayItem(pNetObj, i); + if (pArrItemObj != NULL) + { + xnet_iface_t *pIfcObj = (xnet_iface_t*)malloc(sizeof(xnet_iface_t)); + if (pIfcObj == NULL) + { + xloge("Failed to allocate memory for network iface object: %d", errno); + return XSTDERR; + } + + memset(pIfcObj, 0, sizeof(xnet_iface_t)); + pIfcObj->nPacketsReceivedPerSec = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "packetsReceivedPerSec")); + pIfcObj->nBytesReceivedPerSec = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "bytesReceivedPerSec")); + pIfcObj->nPacketsSentPerSec = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "packetsSentPerSec")); + pIfcObj->nBytesSentPerSec = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "bytesSentPerSec")); + pIfcObj->nPacketsReceived = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "packetsReceived")); + pIfcObj->nBytesReceived = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "bytesReceived")); + pIfcObj->nPacketsSent = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "packetsSent")); + pIfcObj->nBytesSent = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "bytesSent")); + pIfcObj->nBandwidth = XJSON_GetU64(XJSON_GetObject(pArrItemObj, "bandwidth")); + pIfcObj->nType = XJSON_GetU32(XJSON_GetObject(pArrItemObj, "type")); + + const char *pName = XJSON_GetString(XJSON_GetObject(pArrItemObj, "name")); + if (pName != NULL) xstrncpy(pIfcObj->sName, sizeof(pIfcObj->sName), pName); + + const char *pHwAddr = XJSON_GetString(XJSON_GetObject(pArrItemObj, "hwAddr")); + if (pHwAddr != NULL) xstrncpy(pIfcObj->sHWAddr, sizeof(pIfcObj->sHWAddr), pHwAddr); + + const char *pIpAddr = XJSON_GetString(XJSON_GetObject(pArrItemObj, "ipAddr")); + if (pIpAddr != NULL) xstrncpy(pIfcObj->sIPAddr, sizeof(pIfcObj->sIPAddr), pIpAddr); + + if (XArray_AddData(&pStats->netIfaces, pIfcObj, 0) < 0) + { + xloge("Failed to store network iface object: %d", errno); + return XSTDERR; + } + } + } + + return XSTDOK; +} + +int XTOPApp_GetRemoteStats(xtop_args_t *pArgs, xtop_stats_t *pStats) +{ + xhttp_t response; + xhttp_status_t eStatus = XHTTP_SoloPerform(&response, XHTTP_GET, pArgs->sLink, NULL, 0); + + if (eStatus != XHTTP_COMPLETE) + { + xloge("%s", XHTTP_GetStatusStr(eStatus)); + XHTTP_Clear(&response); + return XSTDERR; + } + + if (response.nStatusCode != 200) + { + xlogw("HTTP response: %d %s", response.nStatusCode, + XHTTP_GetCodeStr(response.nStatusCode)); + + XHTTP_Clear(&response); + return XSTDERR; + } + + const char *pBody = (const char *)XHTTP_GetBody(&response); + if (pBody == NULL) + { + xloge("HTTP response does not contain data"); + XHTTP_Clear(&response); + return XSTDERR; + } + + xjson_t json; + if (!XJSON_Parse(&json, pBody, response.nContentLength)) + { + char sError[256]; + XJSON_GetErrorStr(&json, sError, sizeof(sError)); + xloge("Failed to parse JSON: %s", sError); + + XHTTP_Clear(&response); + return XSTDERR; + } + + int nStatus = XTOPApp_GetJSONStats(pStats, &json); + + XHTTP_Clear(&response); + XJSON_Destroy(&json); + return nStatus; +} + +void XTOPApp_PrintStatus(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + const char *pStr = XAPI_GetStatusStr(pCtx); + int nFD = pData ? (int)pData->nFD : XSTDERR; + + if (pCtx->eCbType == XAPI_CB_STATUS) + xlogn("%s: fd(%d)", pStr, nFD); + else if (pCtx->eCbType == XAPI_CB_ERROR) + xloge("%s: fd(%d), errno(%d)", pStr, nFD, errno); +} + +int XTOPApp_HandleRequest(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + xtop_request_t *pRequest = (xtop_request_t*)pData->pSessionData; + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + *pRequest = XTOP_NOTFOUND; + + xlogn("Received request: fd(%d), url: %s", + (int)pData->nFD, pHandle->sUrl); + + if (pHandle->eMethod != XHTTP_GET) + { + xlogw("Unsupported HTTP method: %s", + XHTTP_GetMethodStr(pHandle->eMethod)); + + *pRequest = XTOP_NOTALLOWED; + return XAPI_SetEvents(pData, XPOLLOUT); + } + + xarray_t *pArr = xstrsplit(pHandle->sUrl, "/"); + if (pArr == NULL) + { + xlogw("Invalid request URL: %s", pHandle->sUrl); + *pRequest = XTOP_INVALID; + return XAPI_SetEvents(pData, XPOLLOUT); + } + + char *pDirect = (char*)XArray_GetData(pArr, 0); + char *pEntry = (char*)XArray_GetData(pArr, 1); + + if (pDirect != NULL && pEntry != NULL && !strncmp(pDirect, "api", 3)) + { + if (!strncmp(pEntry, "all", 3)) *pRequest = XTOP_ALL; + else if (!strncmp(pEntry, "cpu", 3)) *pRequest = XTOP_CPU; + else if (!strncmp(pEntry, "memory", 6)) *pRequest = XTOP_MEMORY; + else if (!strncmp(pEntry, "network", 7)) *pRequest = XTOP_NETWORK; + } + + if (*pRequest == XTOP_NOTFOUND) + xlogw("Requested data is not found: %s", pHandle->sUrl); + + XArray_Destroy(pArr); + return XAPI_SetEvents(pData, XPOLLOUT); +} + +xjson_obj_t* XTOPApp_CreateMemoryObj(xtop_stats_t *pStats) +{ + xmem_info_t memInfo; + XTop_GetMemoryInfo(pStats, &memInfo); + + xjson_obj_t *pMemObj = XJSON_NewObject("memory", XFALSE); + if (pMemObj == NULL) + { + xloge("Failed to allocate memory for JSON memory object: %d", errno); + return NULL; + } + + if (XJSON_AddU64(pMemObj, "memReclaimable", memInfo.nReclaimable) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memBuffered", memInfo.nBuffers) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memResident", memInfo.nResidentMemory) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memVirtual", memInfo.nVirtualMemory) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memCached", memInfo.nMemoryCached) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memShared", memInfo.nMemoryShared) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memAvail", memInfo.nMemoryAvail) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memTotal", memInfo.nMemoryTotal) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "memFree", memInfo.nMemoryFree) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "swapCached", memInfo.nSwapCached) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "swapTotal", memInfo.nSwapTotal) != XJSON_ERR_NONE || + XJSON_AddU64(pMemObj, "swapFree", memInfo.nSwapFree) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the JSON memory object: %d", errno); + XJSON_FreeObject(pMemObj); + return XFALSE; + } + + return pMemObj; +} + +xjson_obj_t* XTOPApp_CreateNetworkObj(xtop_stats_t *pStats) +{ + xarray_t netIfaces; + if (XTop_GetNetworkStats(pStats, &netIfaces) > 0) + { + xjson_obj_t *pNetObj = XJSON_NewArray("network", XFALSE); + if (pNetObj == NULL) + { + xloge("Failed to allocate memory for JSON array: %d", errno); + XJSON_FreeObject(pNetObj); + return NULL; + } + + size_t i, nUsed = XArray_Used(&netIfaces); + for (i = 0; i < nUsed; i++) + { + xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(&netIfaces, i); + if (pIface == NULL) continue; + + xjson_obj_t *pItemObj = XJSON_NewObject(NULL, XFALSE); + if (pItemObj == NULL) + { + xloge("Failed to allocate memory for JSON network item: %d", errno); + XJSON_FreeObject(pNetObj); + return NULL; + } + + if (XJSON_AddObject(pNetObj, pItemObj) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON network item object: %d", errno); + XJSON_FreeObject(pItemObj); + XJSON_FreeObject(pNetObj); + return NULL; + } + + if (XJSON_AddU64(pItemObj, "packetsReceivedPerSec", pIface->nPacketsReceivedPerSec) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "bytesReceivedPerSec", pIface->nBytesReceivedPerSec) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "packetsSentPerSec", pIface->nPacketsSentPerSec) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "bytesSentPerSec", pIface->nBytesSentPerSec) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "packetsReceived", pIface->nPacketsReceived) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "bytesReceived", pIface->nBytesReceived) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "packetsSent", pIface->nPacketsSent) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "bytesSent", pIface->nBytesSent) != XJSON_ERR_NONE || + XJSON_AddU64(pItemObj, "bandwidth", pIface->nBandwidth) != XJSON_ERR_NONE || + XJSON_AddU32(pItemObj, "type", pIface->nType) != XJSON_ERR_NONE || + XJSON_AddString(pItemObj, "name", pIface->sName) != XJSON_ERR_NONE || + XJSON_AddString(pItemObj, "hwAddr", pIface->sHWAddr) != XJSON_ERR_NONE || + XJSON_AddString(pItemObj, "ipAddr", pIface->sIPAddr) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the JSON network object: %d", errno); + XJSON_FreeObject(pNetObj); + return NULL; + } + } + + XArray_Destroy(&netIfaces); + return pNetObj; + } + + return NULL; +} + +xjson_obj_t* XTOPApp_CreateCPUInfoObj(xcpu_info_t *pCpu) +{ + xjson_obj_t *pCpuObj = XJSON_NewObject(NULL, XFALSE); + if (pCpuObj == NULL) + { + xloge("Failed to allocate memory for CPU core JSON object: %d", errno); + return NULL; + } + + if (XJSON_AddU32(pCpuObj, "softInterrupts", pCpu->nSoftInterrupts) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "hardInterrupts", pCpu->nHardInterrupts) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "userSpaceNiced", pCpu->nUserSpaceNiced) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "kernelSpace", pCpu->nKernelSpace) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "userSpace", pCpu->nUserSpace) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "idleTime", pCpu->nIdleTime) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "ioWait", pCpu->nIOWait) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "stealTime", pCpu->nStealTime) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "guestTime", pCpu->nGuestTime) != XJSON_ERR_NONE || + XJSON_AddU32(pCpuObj, "guestNiced", pCpu->nGuestNiced) != XJSON_ERR_NONE || + XJSON_AddInt(pCpuObj, "id", pCpu->nID) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the CPU cores JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + return pCpuObj; +} + +xjson_obj_t* XTOPApp_CreateCPUObj(xtop_stats_t *pStats) +{ + xcpu_stats_t cpuStats; + if (XTop_GetCPUStats(pStats, &cpuStats) > 0) + { + xjson_obj_t *pCpuObj = XJSON_NewObject("cpu", XFALSE); + if (pCpuObj == NULL) + { + xloge("Failed to allocate memory for JSON CPU object: %d", errno); + return NULL; + } + + xjson_obj_t *pLoadArr = XJSON_NewArray("loadAverage", XFALSE); + if (pLoadArr == NULL) + { + xloge("Failed to allocate memory for JSON loadAverage object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pCpuObj, pLoadArr) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON loadAverage object: %d", errno); + XJSON_FreeObject(pLoadArr); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + int i; + for (i = 0; i < 3; i++) + { + xjson_obj_t *pItemObj = XJSON_NewObject(NULL, XFALSE); + if (pItemObj == NULL) + { + xloge("Failed to allocate memory for loadAverage item object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pLoadArr, pItemObj) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON loadAverage item object: %d", errno); + XJSON_FreeObject(pItemObj); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + char sInterval[XSTR_TINY]; + sInterval[0] = XSTR_NUL; + + if (i == 0) xstrncpy(sInterval, sizeof(sInterval), "1m"); + else if (i == 1) xstrncpy(sInterval, sizeof(sInterval), "5m"); + else if (i == 2) xstrncpy(sInterval, sizeof(sInterval), "15m"); + + if (XJSON_AddString(pItemObj, "interval", sInterval) != XJSON_ERR_NONE || + XJSON_AddFloat(pItemObj, "value", XU32ToFloat(cpuStats.nLoadAvg[i])) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the JSON network object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + } + + xjson_obj_t *pUsageObj = XJSON_NewObject("usage", XFALSE); + if (pUsageObj == NULL) + { + xloge("Failed to allocate memory for process JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pCpuObj, pUsageObj) != XJSON_ERR_NONE) + { + xloge("Failed to append process JSON object: %d", errno); + XJSON_FreeObject(pUsageObj); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + xjson_obj_t *pProcObj = XJSON_NewObject("process", XFALSE); + if (pProcObj == NULL) + { + xloge("Failed to allocate memory for process JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pUsageObj, pProcObj) != XJSON_ERR_NONE) + { + xloge("Failed to append process JSON object: %d", errno); + XJSON_FreeObject(pProcObj); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddFloat(pProcObj, "userSpace", XU32ToFloat(cpuStats.usage.nUserSpaceUsage)) != XJSON_ERR_NONE || + XJSON_AddFloat(pProcObj, "kernelSpace", XU32ToFloat(cpuStats.usage.nKernelSpaceUsage)) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the network JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddU32(pUsageObj, "softInterrupts", cpuStats.sum.nSoftInterrupts) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "hardInterrupts", cpuStats.sum.nHardInterrupts) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "userSpaceNiced", cpuStats.sum.nUserSpaceNiced) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "kernelSpace", cpuStats.sum.nKernelSpace) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "userSpace", cpuStats.sum.nUserSpace) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "idleTime", cpuStats.sum.nIdleTime) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "ioWait", cpuStats.sum.nIOWait) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "stealTime", cpuStats.sum.nStealTime) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "guestTime", cpuStats.sum.nGuestTime) != XJSON_ERR_NONE || + XJSON_AddU32(pUsageObj, "guestNiced", cpuStats.sum.nGuestNiced) != XJSON_ERR_NONE) + { + xloge("Failed to append entries to the CPU sum JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + xjson_obj_t *pCoresArr = XJSON_NewArray("cores", XFALSE); + if (pCoresArr == NULL) + { + xloge("Failed to allocate memory for cores JSON object: %d", errno); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pUsageObj, pCoresArr) != XJSON_ERR_NONE) + { + xloge("Failed to append cores JSON object: %d", errno); + XJSON_FreeObject(pCoresArr); + XJSON_FreeObject(pCpuObj); + return NULL; + } + + for (i = 0; i < (int)cpuStats.cores.nUsed; i++) + { + xcpu_info_t *pCore = (xcpu_info_t*)XArray_GetData(&cpuStats.cores, i); + if (pCore == NULL) continue; + + xjson_obj_t *pCoreObj = XTOPApp_CreateCPUInfoObj(pCore); + if (pCoreObj == NULL) + { + XJSON_FreeObject(pCpuObj); + return NULL; + } + + if (XJSON_AddObject(pCoresArr, pCoreObj) != XJSON_ERR_NONE) + { + xloge("Failed to append CPU core JSON object: %d", errno); + XJSON_FreeObject(pCoreObj); + XJSON_FreeObject(pCpuObj); + return NULL; + } + } + + XArray_Destroy(&cpuStats.cores); + return pCpuObj; + } + + return NULL; +} + +char* XTOPApp_AssembleBody(xapi_data_t *pData, size_t *pLength) +{ + xtop_stats_t *pStats = (xtop_stats_t*)pData->pApi->pUserCtx; + xtop_request_t eRequest = *(xtop_request_t*)pData->pSessionData; + + xjson_obj_t *pRootObj = XJSON_NewObject(NULL, XFALSE); + if (pRootObj == NULL) + { + xloge("Failed to allocate memory for JSON root object: %d", errno); + return NULL; + } + + if (eRequest == XTOP_ALL || eRequest == XTOP_CPU) + { + xjson_obj_t *pCPUObj = XTOPApp_CreateCPUObj(pStats); + if (pCPUObj == NULL) + { + XJSON_FreeObject(pRootObj); + return NULL; + } + + if (XJSON_AddObject(pRootObj, pCPUObj) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON CPU object: %d", errno); + XJSON_FreeObject(pRootObj); + XJSON_FreeObject(pCPUObj); + return NULL; + } + } + + if (eRequest == XTOP_ALL || eRequest == XTOP_MEMORY) + { + xjson_obj_t *pMemObj = XTOPApp_CreateMemoryObj(pStats); + if (pMemObj == NULL) + { + XJSON_FreeObject(pRootObj); + return NULL; + } + + if (XJSON_AddObject(pRootObj, pMemObj) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON memory object: %d", errno); + XJSON_FreeObject(pRootObj); + XJSON_FreeObject(pMemObj); + return NULL; + } + } + + if (eRequest == XTOP_ALL || eRequest == XTOP_NETWORK) + { + xjson_obj_t *pNetObj = XTOPApp_CreateNetworkObj(pStats); + if (pNetObj == NULL) + { + XJSON_FreeObject(pRootObj); + return NULL; + } + + if (XJSON_AddObject(pRootObj, pNetObj) != XJSON_ERR_NONE) + { + xloge("Failed to append JSON network object: %d", errno); + XJSON_FreeObject(pRootObj); + XJSON_FreeObject(pNetObj); + return NULL; + } + } + + xjson_writer_t writer; + XJSON_InitWriter(&writer, NULL, XSTR_MID); + writer.nTabSize = 4; // Enable linter + + if (!XJSON_WriteObject(pRootObj, &writer)) + { + xloge("Failed to serialize data in JSON format: %d", errno); + XJSON_DestroyWriter(&writer); + XJSON_FreeObject(pRootObj); + return NULL; + } + + XJSON_FreeObject(pRootObj); + *pLength = writer.nLength; + return (char*)writer.pData; +} + +int XTOPApp_SendResponse(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + xtop_request_t *pRequest = (xtop_request_t*)pData->pSessionData; + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + + if (*pRequest == XTOP_INVALID) pHandle->nStatusCode = 400; + else if (*pRequest == XTOP_NOTFOUND) pHandle->nStatusCode = 404; + else if (*pRequest == XTOP_NOTALLOWED) pHandle->nStatusCode = 405; + else pHandle->nStatusCode = 200; + + if (XHTTP_AddHeader(pHandle, "Server", "xutils/%s", XUtils_VersionShort()) < 0) + { + xloge("Failed to initialize HTTP response: %s", strerror(errno)); + return XSTDERR; + } + + const char *pContentType = NULL; + const char *pBody = NULL; + char *pContent = NULL; + size_t nLength = 0; + + if (pHandle->nStatusCode == 200) + { + pContent = XTOPApp_AssembleBody(pData, &nLength); + if (pContent == NULL) return XSTDERR; + pContentType = "application/json"; + pBody = pContent; + } + else + { + pBody = XHTTP_GetCodeStr(pHandle->nStatusCode); + nLength = strlen(pBody); + pContentType = "text/plain"; + } + + + if (XHTTP_AddHeader(pHandle, "Content-Type", pContentType) < 0 || + XHTTP_Assemble(pHandle, (const uint8_t*)pBody, nLength) == NULL) + { + xloge("Failed to assemble HTTP response: %s", strerror(errno)); + free(pContent); + return XSTDERR; + } + + xlogn("Sending response: fd(%d), buff(%zu)", + (int)pData->nFD, pHandle->dataRaw.nUsed); + + free(pContent); + return XSTDOK; +} + +int XTOPApp_InitSessionData(xapi_data_t *pData) +{ + xtop_request_t *pRequest = (xtop_request_t *)malloc(sizeof(xtop_request_t)); + if (pRequest == NULL) + { + xloge("Failed to allocate memory for session data: %d", errno); + return XSTDERR; + } + + *pRequest = XTOP_INVALID; + pData->pSessionData = pRequest; + + xlogn("Accepted connection: fd(%d)", (int)pData->nFD); + return XAPI_SetEvents(pData, XPOLLIN); +} + +int XTOPApp_ClearSessionData(xapi_data_t *pData) +{ + xlogn("Connection closed: fd(%d)", (int)pData->nFD); + free(pData->pSessionData); + pData->pSessionData = NULL; + return XSTDERR; +} + +int XTOPApp_ServiceCb(xapi_ctx_t *pCtx, xapi_data_t *pData) +{ + switch (pCtx->eCbType) + { + case XAPI_CB_ERROR: + case XAPI_CB_STATUS: + XTOPApp_PrintStatus(pCtx, pData); + break; + case XAPI_CB_REQUEST: + return XTOPApp_HandleRequest(pCtx, pData); + case XAPI_CB_WRITE: + return XTOPApp_SendResponse(pCtx, pData); + case XAPI_CB_ACCEPTED: + return XTOPApp_InitSessionData(pData); + case XAPI_CB_CLOSED: + return XTOPApp_ClearSessionData(pData); + case XAPI_CB_COMPLETE: + xlogn("Write data is complete: fd(%d)", (int)pData->nFD); + return XSTDERR; + case XAPI_CB_INTERRUPT: + if (g_nInterrupted) return XSTDERR; + break; + default: + break; + } + + return XSTDOK; +} + +int XTOPApp_ServerMode(xtop_args_t *pArgs, xtop_stats_t *pStats) +{ + xapi_t api; + api.callback = XTOPApp_ServiceCb; + api.pUserCtx = pStats; + + if (XAPI_StartListener(&api, pArgs->sAddr, pArgs->nPort) < 0) return XSTDERR; + xlogn("Socket started listen to port: %d", pArgs->nPort); + + xevent_status_t status; + do status = XAPI_Service(&api, 100); + while (status == XEVENT_STATUS_SUCCESS); + + XAPI_Destroy(&api); + return XSTDNON; +} + +int main(int argc, char *argv[]) +{ + xlog_init("xtop", XLOG_DEFAULT, XFALSE); + xtop_stats_t stats; + xtop_args_t args; + + if (!XTOPApp_ParseArgs(&args, argc, argv)) + { + XTOPApp_DisplayUsage(argv[0]); + return XSTDERR; + } + + if (args.bDaemon && daemon(XTRUE, XTRUE) < 0) + { + xlogn("Failed to run server as daemon: %d", errno); + return XSTDERR; + } + + if (XTop_InitStats(&stats) < 0) + { + xloge("Failed to initialize stats: %d", errno); + return XSTDERR; + } + + xlog_screen(!args.bDaemon); + xlog_timing(XLOG_TIME); + + int nSignals[2]; + nSignals[0] = SIGTERM; + nSignals[1] = SIGINT; + XSig_Register(nSignals, 2, XTOPApp_SignalCallback); + + xbool_t bRemote = xstrused(args.sLink); + xbool_t bMonStarted = XFALSE; + + if (args.bServer || !bRemote) + { + int nStatus = XTop_StartMonitoring(&stats, args.nIntervalU, args.nPID); + if (nStatus < 0) + { + xloge("PID not found: %d", args.nPID); + XTop_DestroyStats(&stats); + return XSTDERR; + } + else if (!nStatus) + { + xloge("Failed to start monitoring thread: %d", errno); + XTop_DestroyStats(&stats); + return XSTDERR; + } + + XTop_WaitLoad(&stats, 1000); + bMonStarted = XTRUE; + } + + if (args.bServer) + { + int nStatus = XTOPApp_ServerMode(&args, &stats); + XTop_StopMonitoring(&stats, 1000); + XTop_DestroyStats(&stats); + + usleep(10000); // Make valgrind happy + return nStatus; + } + + xcli_wind_t win; + XWindow_Init(&win); + + xcli_bar_t bar; + XProgBar_GetDefaults(&bar); + bar.bInPercent = XTRUE; + bar.bInSuffix = XTRUE; + bar.cLoader = '|'; + + while (!g_nInterrupted) + { + if (bRemote && XTOPApp_GetRemoteStats(&args, &stats) < 0) break; + + XWindow_AddAligned(&win, "[XTOP]", XSTR_BACK_BLUE, XCLI_CENTER); + XWindow_AddEmptyLine(&win); + + xcpu_stats_t cpuStats; + if (XTop_GetCPUStats(&stats, &cpuStats) > 0) + { + xmem_info_t memInfo; + XTop_GetMemoryInfo(&stats, &memInfo); + + XTOPApp_AddCPULoadBar(&win, &bar, &cpuStats); + XTOPApp_AddOverallBar(&win, &bar, &memInfo, &cpuStats); + + if (!args.bExcludeCPU) + { + XWindow_AddEmptyLine(&win); + XTOPApp_AddCPUExtra(&win, &args, &bar, &memInfo, &cpuStats); + } + + XWindow_AddEmptyLine(&win); + XArray_Destroy(&cpuStats.cores); + } + + xarray_t netIfaces; + if (XTop_GetNetworkStats(&stats, &netIfaces) > 0) + { + XTOPApp_AddNetworkInfo(&win, &args, &netIfaces); + XArray_Destroy(&netIfaces); + } + + XWindow_Flush(&win); + xusleep(args.nIntervalU); + } + + if (bMonStarted) + XTop_StopMonitoring(&stats, 1000); + + XTop_DestroyStats(&stats); + XWindow_Destroy(&win); + + usleep(10000); // Make valgrind happy + return 0; +} \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..bc226d6 --- /dev/null +++ b/install.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +./build.sh smake --install +./clean.sh diff --git a/media/mdtp.c b/media/mdtp.c new file mode 100644 index 0000000..d3b9bb6 --- /dev/null +++ b/media/mdtp.c @@ -0,0 +1,325 @@ +/*! + * @file libxutils/media/mdtp.c + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of Modern Data Transmit Protocol + * (MDTP) packet parser and assembler functionality + */ + +#include "xstd.h" +#include "xstr.h" +#include "mdtp.h" + +const char *XPacket_GetTypeStr(xpacket_type_t eType) +{ + switch(eType) + { + case XPACKET_TYPE_DUMMY: return "dummy"; + case XPACKET_TYPE_MULTY: return "multy"; + case XPACKET_TYPE_ERROR: return "error"; + case XPACKET_TYPE_LITE: return "lite"; + case XPACKET_TYPE_DATA: return "data"; + case XPACKET_TYPE_PING: return "ping"; + case XPACKET_TYPE_PONG: return "pong"; + case XPACKET_TYPE_INFO: return "info"; + case XPACKET_TYPE_CMD: return "cmd"; + case XPACKET_TYPE_EOS: return "eos"; + case XPACKET_TYPE_KA: return "ka"; + default: break; + } + + return "invalid"; +} + +xpacket_type_t XPacket_GetType(const char *pType) +{ + if (!strncmp(pType, "dummy", 5)) return XPACKET_TYPE_DUMMY; + else if (!strncmp(pType, "multy", 5)) return XPACKET_TYPE_MULTY; + else if (!strncmp(pType, "error", 5)) return XPACKET_TYPE_ERROR; + else if (!strncmp(pType, "lite", 4)) return XPACKET_TYPE_LITE; + else if (!strncmp(pType, "data", 4)) return XPACKET_TYPE_DATA; + else if (!strncmp(pType, "ping", 4)) return XPACKET_TYPE_PING; + else if (!strncmp(pType, "pong", 4)) return XPACKET_TYPE_PONG; + else if (!strncmp(pType, "info", 4)) return XPACKET_TYPE_INFO; + else if (!strncmp(pType, "cmd", 3)) return XPACKET_TYPE_CMD; + else if (!strncmp(pType, "eos", 3)) return XPACKET_TYPE_EOS; + else if (!strncmp(pType, "ka", 2)) return XPACKET_TYPE_KA; + return XPACKET_TYPE_INVALID; +} + +void XPacket_Clear(xpacket_t *pPacket) +{ + if (pPacket != NULL) + { + if (pPacket->callback != NULL) + { + pPacket->callback(pPacket, XPACKET_CB_CLEAR); + pPacket->callback = NULL; + } + + if (pPacket->pHeaderObj != NULL) + { + XJSON_FreeObject(pPacket->pHeaderObj); + pPacket->pHeaderObj = NULL; + } + + pPacket->pUserData = NULL; + XByteBuffer_Clear(&pPacket->rawData); + if (pPacket->nAllocated) free(pPacket); + } +} + +void XPacket_ParseHeader(xpacket_header_t *pHeader, xjson_obj_t *pHeaderObj) +{ + const char *pVersion = XJSON_GetString(XJSON_GetObject(pHeaderObj, "version")); + if (pVersion != NULL && xstrused(pVersion)) xstrncpy(pHeader->sVersion, sizeof(pHeader->sVersion), pVersion); + + const char *pPacketType = XJSON_GetString(XJSON_GetObject(pHeaderObj, "packetType")); + if (xstrused(pPacketType)) pHeader->eType = XPacket_GetType(pPacketType); + + pHeader->nTimeStamp = XJSON_GetU32(XJSON_GetObject(pHeaderObj, "timeStamp")); + pHeader->nSessionID = XJSON_GetU32(XJSON_GetObject(pHeaderObj, "sessionId")); + pHeader->nPacketID = XJSON_GetU32(XJSON_GetObject(pHeaderObj, "packetId")); + + xjson_obj_t *pPayloadObj = XJSON_GetObject(pHeaderObj, "payload"); + if (pPayloadObj != NULL) + { + const char *pPayloadType = XJSON_GetString(XJSON_GetObject(pPayloadObj, "payloadType")); + if (xstrused(pPayloadType)) xstrncpy(pHeader->sPayloadType, sizeof(pHeader->sPayloadType), pPayloadType); + + pHeader->nPayloadSize = XJSON_GetU32(XJSON_GetObject(pPayloadObj, "payloadSize")); + pHeader->nCrypted = XJSON_GetBool(XJSON_GetObject(pPayloadObj, "crypted")); + pHeader->nSSRCHash = XJSON_GetU32(XJSON_GetObject(pPayloadObj, "ssrcHash")); + } + + xjson_obj_t *pExtraObj = XJSON_GetObject(pHeaderObj, "extension"); + if (pExtraObj != NULL) + { + const char *pTime = XJSON_GetString(XJSON_GetObject(pExtraObj, "time")); + if (xstrused(pTime)) xstrncpy(pHeader->sTime, sizeof(pHeader->sTime), pTime); + + const char *pTZ = XJSON_GetString(XJSON_GetObject(pExtraObj, "timeZone")); + if (xstrused(pTZ)) xstrncpy(pHeader->sTZ, sizeof(pHeader->sTZ), pTZ); + } +} + +int XPacket_UpdateHeader(xpacket_t *pPacket) +{ + if (pPacket == NULL) return XSTDERR; + xpacket_header_t *pHeader = &pPacket->header; + + if (pPacket->pHeaderObj == NULL) + { + pPacket->pHeaderObj = XJSON_NewObject(NULL, 1); + if (pPacket->pHeaderObj == NULL) return XSTDERR; + } + + xjson_obj_t *pHeaderObj = pPacket->pHeaderObj; + pHeaderObj->nAllowUpdate = 1; + + if (pPacket->callback != NULL) pPacket->callback(pPacket, XPACKET_CB_UPDATE); + if (pHeader->eType == XPACKET_TYPE_ERROR || pHeader->eType == XPACKET_TYPE_INVALID) return XSTDERR; + const char *pPacketType = pHeader->eType == XPACKET_TYPE_LITE ? NULL : XPacket_GetTypeStr(pHeader->eType); + + if ((xstrused(pHeader->sVersion) && XJSON_AddString(pHeaderObj, "version", pHeader->sVersion) != XJSON_ERR_NONE) || + (xstrused(pPacketType) && XJSON_AddString(pHeaderObj, "packetType", pPacketType) != XJSON_ERR_NONE) || + (pHeader->nSessionID && XJSON_AddU32(pHeaderObj, "sessionId", pHeader->nSessionID) != XJSON_ERR_NONE) || + (pHeader->nTimeStamp && XJSON_AddU32(pHeaderObj, "timeStamp", pHeader->nTimeStamp) != XJSON_ERR_NONE) || + (pHeader->nPacketID && XJSON_AddU32(pHeaderObj, "packetId", pHeader->nPacketID) != XJSON_ERR_NONE)) + { + pHeader->eType = XPACKET_TYPE_ERROR; + return XSTDERR; + } + + uint8_t nHaveDataType = xstrused(pHeader->sPayloadType); + uint8_t nHaveTime = xstrused(pHeader->sTime); + uint8_t nHaveTZ = xstrused(pHeader->sTZ); + + if (nHaveTime || nHaveTZ) + { + xjson_obj_t *pExtension = XJSON_GetOrCreateObject(pHeaderObj, "extension", 1); + if (pExtension == NULL) + { + pHeader->eType = XPACKET_TYPE_ERROR; + return XSTDERR; + } + + if ((nHaveTime && XJSON_AddString(pExtension, "time", pHeader->sTime) != XJSON_ERR_NONE) || + (nHaveTZ && XJSON_AddString(pExtension, "timeZone", pHeader->sTZ) != XJSON_ERR_NONE)) + { + pHeader->eType = XPACKET_TYPE_ERROR; + return XSTDERR; + } + } + + if (pHeader->nPayloadSize) + { + xjson_obj_t *pPayloadObj = XJSON_GetOrCreateObject(pHeaderObj, "payload", 1); + if (pPayloadObj == NULL) + { + pHeader->eType = XPACKET_TYPE_ERROR; + return XSTDERR; + } + + if ((nHaveDataType && XJSON_AddString(pPayloadObj, "payloadType", pHeader->sPayloadType) != XJSON_ERR_NONE) || + (pHeader->nCrypted && XJSON_AddBool(pPayloadObj, "crypted", pHeader->nCrypted) != XJSON_ERR_NONE) || + XJSON_AddU32(pPayloadObj, "payloadSize", pHeader->nPayloadSize) != XJSON_ERR_NONE) + { + pHeader->eType = XPACKET_TYPE_ERROR; + return XSTDERR; + } + } + + return XSTDOK; +} + +int XPacket_Init(xpacket_t *pPacket, uint8_t *pData, uint32_t nSize) +{ + xpacket_header_t *pHeader = &pPacket->header; + memset(pHeader, 0, sizeof(xpacket_header_t)); + + pHeader->eType = XPACKET_TYPE_LITE; + pHeader->nPayloadSize = nSize; + + XByteBuffer_Init(&pPacket->rawData, 0, 0); + pPacket->nHeaderLength = 0; + pPacket->nPacketSize = 0; + pPacket->nAllocated = 0; + pPacket->pPayload = pData; + pPacket->pUserData = NULL; + pPacket->callback = NULL; + + pPacket->pHeaderObj = XJSON_NewObject(NULL, 1); + return (pPacket->pHeaderObj == NULL) ? XSTDERR : XSTDOK; +} + +xpacket_t *XPacket_New(uint8_t *pData, uint32_t nSize) +{ + xpacket_t *pPacket = (xpacket_t*)malloc(sizeof(xpacket_t)); + if (pPacket == NULL) return NULL; + + if (XPacket_Init(pPacket, pData, nSize) == XSTDERR) + { + free(pPacket); + return NULL; + } + + pPacket->nAllocated = 1; + return pPacket; +} + +int XPacket_Create(xbyte_buffer_t *pBuffer, const char *pHeader, size_t nHdrLen, uint8_t *pData, size_t nSize) +{ + if ((pBuffer == NULL || pHeader == NULL || !nHdrLen) || + (!XByteBuffer_Add(pBuffer, (uint8_t*)&nHdrLen, sizeof(nHdrLen))) || + (!XByteBuffer_Add(pBuffer, (uint8_t*)pHeader, nHdrLen)) || + (pData != NULL && nSize && !XByteBuffer_Add(pBuffer, pData, nSize))) + { + XByteBuffer_Clear(pBuffer); + return XSTDERR; + } + + return XSTDOK; +} + +xbyte_buffer_t *XPacket_Assemble(xpacket_t *pPacket) +{ + if (XPacket_UpdateHeader(pPacket) == XSTDERR) return NULL; + xpacket_header_t *pHeader = &pPacket->header; + xjson_writer_t jsonWriter; + + XJSON_InitWriter(&jsonWriter, NULL, XPACKET_HDR_INITIAL); + XByteBuffer_Reset(&pPacket->rawData); + + if (XJSON_WriteObject(pPacket->pHeaderObj, &jsonWriter)) + { + XPacket_Create( + &pPacket->rawData, + jsonWriter.pData, + jsonWriter.nLength, + pPacket->pPayload, + pHeader->nPayloadSize + ); + + pPacket->nHeaderLength = (uint32_t)jsonWriter.nLength; + XPacket_ParseHeader(&pPacket->header, pPacket->pHeaderObj); + } + + XJSON_DestroyWriter(&jsonWriter); + return &pPacket->rawData; +} + +uint8_t *XPacket_Parse(xpacket_t *pPacket, const uint8_t *pData, size_t nSize) +{ + xpacket_header_t *pHdr = &pPacket->header; + memset(pHdr, 0, sizeof(xpacket_header_t)); + pHdr->eType = XPACKET_TYPE_INVALID; + + if (pData == NULL || nSize <= 0) return NULL; + XByteBuffer_Init(&pPacket->rawData, 0, 0); + + pPacket->nHeaderLength = (*(uint32_t*)pData); + pPacket->pHeaderObj = NULL; + pPacket->pPayload = NULL; + pPacket->pUserData = NULL; + pPacket->callback = NULL; + pPacket->nPacketSize = 0; + pPacket->nAllocated = 0; + + if ((int)(nSize - XPACKET_INFO_BYTES) < pPacket->nHeaderLength) + { + pHdr->eType = XPACKET_TYPE_INCOMPLETE; + return NULL; + } + + xstrncpy(pHdr->sVersion, sizeof(pHdr->sVersion), XPACKET_VERSION_STR); + const char *pHeader = &((char*)pData)[XPACKET_INFO_BYTES]; + + if (pPacket->nHeaderLength > 0) + { + xjson_t json; + if (!XJSON_Parse(&json, pHeader, pPacket->nHeaderLength)) + { + XJSON_Destroy(&json); + return NULL; + } + + pPacket->pHeaderObj = json.pRootObj; + XPacket_ParseHeader(pHdr, pPacket->pHeaderObj); + + size_t nPayloadOffset = XPACKET_INFO_BYTES + pPacket->nHeaderLength; + pPacket->nPacketSize = (uint32_t)nPayloadOffset + pHdr->nPayloadSize; + + if (nSize < pPacket->nPacketSize) + { + pHdr->eType = XPACKET_TYPE_INCOMPLETE; + XJSON_FreeObject(pPacket->pHeaderObj); + pPacket->pHeaderObj = NULL; + return NULL; + } + + if (pHdr->nPayloadSize) pPacket->pPayload = (uint8_t*)&pData[nPayloadOffset]; + if (pPacket->callback != NULL) pPacket->callback(pPacket, XPACKET_CB_PARSED); + } + + return pPacket->pPayload; +} + +const uint8_t *XPacket_GetHeader(xpacket_t *pPacket) +{ + if (pPacket == NULL || !pPacket->nHeaderLength) return NULL; + size_t nRequired = XPACKET_INFO_BYTES + pPacket->nHeaderLength; + if (pPacket->rawData.nUsed < nRequired) return NULL; + return &pPacket->rawData.pData[XPACKET_INFO_BYTES]; +} + +const uint8_t *XPacket_GetPayload(xpacket_t *pPacket) +{ + if (pPacket == NULL || !pPacket->header.nPayloadSize) return NULL; + size_t nPayloadOffset = XPACKET_INFO_BYTES + pPacket->nHeaderLength; + size_t nActualSize = nPayloadOffset + pPacket->header.nPayloadSize; + if (pPacket->rawData.nUsed < nActualSize) return NULL; + return &pPacket->rawData.pData[nPayloadOffset]; +} \ No newline at end of file diff --git a/media/mdtp.h b/media/mdtp.h new file mode 100644 index 0000000..2fd7952 --- /dev/null +++ b/media/mdtp.h @@ -0,0 +1,104 @@ +/*! + * @file libxutils/media/mdtp.h + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of Modern Data Transmit Protocol + * (MDTP) packet parser and assembler functionality + */ + +#ifndef __XUTILS_XPACKET_H__ +#define __XUTILS_XPACKET_H__ + +#include +#include +#include "xjson.h" +#include "xbuf.h" + +#define XPACKET_VERSION_STR "1.0" +#define XPACKET_INFO_BYTES 4 + +#define XPACKET_HDR_INITIAL 256 +#define XPACKET_PROTO_MAX 32 +#define XPACKET_TYPE_MAX 128 +#define XPACKET_TIME_MAX 64 +#define XPACKET_VER_MAX 8 +#define XPACKET_TZ_MAX 8 + +#define XPACKET_CB_PARSED 0 +#define XPACKET_CB_UPDATE 1 +#define XPACKET_CB_CLEAR 2 + +typedef struct XPacket xpacket_t; +typedef void(*xpacket_cb_t)(xpacket_t *pPacket, uint8_t nCallback); + +typedef enum { + XPACKET_TYPE_LITE = 0, + XPACKET_TYPE_INVALID, + XPACKET_TYPE_INCOMPLETE, + XPACKET_TYPE_MULTY, + XPACKET_TYPE_ERROR, + XPACKET_TYPE_DUMMY, + XPACKET_TYPE_DATA, + XPACKET_TYPE_PING, + XPACKET_TYPE_PONG, + XPACKET_TYPE_INFO, + XPACKET_TYPE_CMD, + XPACKET_TYPE_EOS, + XPACKET_TYPE_KA +} xpacket_type_t; + +typedef struct XPacketHeader { + xpacket_type_t eType; + uint32_t nPacketID; + uint32_t nSessionID; + uint32_t nTimeStamp; + uint32_t nPayloadSize; + uint32_t nSSRCHash; + uint8_t nCrypted; + + char sPayloadType[XPACKET_TYPE_MAX]; + char sVersion[XPACKET_VER_MAX]; + char sTime[XPACKET_TIME_MAX]; + char sTZ[XPACKET_TZ_MAX]; +} xpacket_header_t; + +typedef struct XPacket { + xpacket_header_t header; // Packet header information + xbyte_buffer_t rawData; // Raw data of assembled packet + xjson_obj_t *pHeaderObj; // JSON object of parsed header + xpacket_cb_t callback; // Packet callback for parse/update + uint32_t nHeaderLength; // The length of raw packet header + uint32_t nPacketSize; // The size of whole packet + uint8_t nAllocated; // Flag to check if packet is allocated + uint8_t *pPayload; // Payload pointed from original raw data + void *pUserData; // User data pointer for packet extension +} xpacket_t; + +#ifdef __cplusplus +extern "C" { +#endif + +xpacket_type_t XPacket_GetType(const char *pType); +const char *XPacket_GetTypeStr(xpacket_type_t eType); + +int XPacket_Init(xpacket_t *pPacket, uint8_t *pData, uint32_t nSize); +xpacket_t *XPacket_New(uint8_t *pData, uint32_t nSize); +void XPacket_Clear(xpacket_t *pPacket); + +uint8_t *XPacket_Parse(xpacket_t *pPacket, const uint8_t *pData, size_t nSize); +void XPacket_ParseHeader(xpacket_header_t *pHeader, xjson_obj_t *pHeaderObj); +int XPacket_UpdateHeader(xpacket_t *pPacket); + +int XPacket_Create(xbyte_buffer_t *pBuffer, const char *pHeader, size_t nHdrLen, uint8_t *pData, size_t nSize); +xbyte_buffer_t * XPacket_Assemble(xpacket_t *pPacket); + +const uint8_t *XPacket_GetHeader(xpacket_t *pPacket); +const uint8_t *XPacket_GetPayload(xpacket_t *pPacket); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XPACKET_H__ */ \ No newline at end of file diff --git a/media/ntp.c b/media/ntp.c new file mode 100644 index 0000000..bbb116c --- /dev/null +++ b/media/ntp.c @@ -0,0 +1,95 @@ +/*! + * @file libxutils/media/ntp.c + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of NTP protocol functionality + */ + +#include "xstd.h" +#include "xtime.h" +#include "sock.h" +#include "ntp.h" + +#define XNTP_BUF_SIZE 12 +#define XNTP_DEF_PORT 123 +#define XNTP_TIMEO_SEC 10 + +#define XNTP_TIME_GAP 2208988800U +#define XNTP_JAN_1970 0x83aa7e80 +#define XNTP_FRAC(x) (4294 * (x) + ((1981 * (x)) >> 11)) + +#define XNTP_LI 0 +#define XNTP_VN 3 +#define XNTP_MODE 3 +#define XNTP_STRA 0 +#define XNTP_POLL 4 +#define XNTP_PREC -6 + +#define XNTP_HDR \ + (XNTP_LI << 30) | \ + (XNTP_VN << 27) | \ + (XNTP_MODE << 24) | \ + (XNTP_STRA << 16) | \ + (XNTP_POLL << 8) | \ + (XNTP_PREC & 0xff) + +int XNTP_SendRequest(xsock_t *pSock) +{ + uint32_t buffer[XNTP_BUF_SIZE]; + memset(buffer, 0, sizeof(buffer)); + + /* Root Delay (seconds) */ + buffer[0] = htonl(XNTP_HDR); + buffer[1] = htonl(1 << 16); + buffer[2] = htonl(1 << 16); + + struct timeval now; + xtime_spec_t ts; + + /* Get time */ + XTime_GetClock(&ts); + now.tv_sec = (long) ts.nSec; + now.tv_usec = (long)ts.nNanoSec / 1000; + + /* Transmit Timestamp coarse */ + buffer[10] = htonl(now.tv_sec + XNTP_JAN_1970); + buffer[11] = htonl(XNTP_FRAC(now.tv_usec)); + + if (XSock_TimeOutS(pSock, XNTP_TIMEO_SEC, 0) < 0) return XSTDERR; + return XSock_Send(pSock, buffer, sizeof(buffer)); +} + +uint32_t XNTP_ReceiveTime(xsock_t *pSock) +{ + uint32_t buffer[XNTP_BUF_SIZE]; + XSock_TimeOutR(pSock, XNTP_TIMEO_SEC, 0); + XSock_Read(pSock, buffer, sizeof(buffer)); + if (XSock_Status(pSock) != XSOCK_ERR_NONE) return XSTDNON; + + time_t rawtime = ntohl((time_t)buffer[10]); + return (uint32_t)rawtime - XNTP_TIME_GAP; +} + +int XNTP_GetDate(const char *pAddr, uint16_t nPort, xtime_t *pTime) +{ + if (pAddr == NULL) return XSTDERR; + if (!nPort) nPort = XNTP_DEF_PORT; + + xsock_addr_t sockAddr; + XSock_GetAddr(&sockAddr, pAddr); + sockAddr.nPort = nPort; + + xsock_t sock; + XSock_Open(&sock, XSOCK_UDP_CLIENT, &sockAddr); + if (sock.eStatus != XSOCK_ERR_NONE) return XSTDERR; + + if (XNTP_SendRequest(&sock) <= 0) return XSTDERR; + time_t nEpoch = XNTP_ReceiveTime(&sock); + if (!nEpoch) return XSTDERR; + + XTime_FromEpoch(pTime, nEpoch); + XSock_Close(&sock); + return XSTDOK; +} diff --git a/media/ntp.h b/media/ntp.h new file mode 100644 index 0000000..a17725a --- /dev/null +++ b/media/ntp.h @@ -0,0 +1,25 @@ +/*! + * @file libxutils/media/ntp.h + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of NTP protocol functionality + */ + +#ifndef __XUTILS_NTP_H__ +#define __XUTILS_NTP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtime.h" + +int XNTP_GetDate(const char *pAddr, uint16_t nPort, xtime_t *pTime); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_NTP_H__ */ \ No newline at end of file diff --git a/media/rtp.c b/media/rtp.c new file mode 100644 index 0000000..a921407 --- /dev/null +++ b/media/rtp.c @@ -0,0 +1,142 @@ +/*! + * @file libxutils/media/rtp.c + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of RTP packet parser functionality + */ + +#include "xstd.h" +#include "rtp.h" + +#define XRTP_PACKET_SIZE 1500 + +uint32_t XRTP_GetTimestamp(float fRate) +{ + static uint32_t nRTPTime = 0; + nRTPTime += (uint32_t)(90000/fRate); + return nRTPTime; +} + +int XRTP_ValidatePayload(const uint8_t *pPayload, size_t nSize) +{ + if (nSize % 188) return XSTDERR; + int nBlocks = (int)nSize / 188; + int i, nOffset = 0; + + /* Check pPayload with 0x47 byte */ + for (i = 0; i < nBlocks; i++) + { + if (pPayload[nOffset] != 0x47) return XSTDNON; + else nOffset += 188; + } + + return XSTDOK; +} + +int XRTP_ParseHeader(xrtp_header_t *pHeader, const uint8_t *pData, size_t nLength) +{ + /* Check correct rtp version */ + if ((pData[0] & 0xc0) != (2 << 6)) return XSTDERR; + + /* Parse RTP header */ + pHeader->nVersion = (pData[0] & 0xc0) >> 6; + pHeader->nPadding = (pData[0] & 0x40) >> 5; + pHeader->nExtension = (pData[0] & 0x20) >> 4; + pHeader->nSCRCCount = (pData[0] & 0x0f); + pHeader->nMarkerBit = (pData[1] & 0x80) >> 7; + pHeader->nPayloadType = (pData[1] & 0x7F); + pHeader->nSequence = ntohs(((unsigned short *) pData)[1]); + pHeader->nTimeStamp = ntohl(((unsigned int *) pData)[1]); + pHeader->nSSRC = ntohl(((unsigned int *) pData)[2]); + + uint32_t i; + if (pHeader->nSCRCCount) + { + for (i = 0; i < pHeader->nSCRCCount; i++) + { + pHeader->SCRC[i] = ntohl(((unsigned int *) pData)[3 + i]); + if (i >= SCRC_MAX) break; + } + } + else + { + for (i = 0; i < SCRC_MAX; i++) + pHeader->SCRC[i] = 0; + } + + /* Offset to pPayload header */ + int nOffset = (3 + pHeader->nSCRCCount) * 4; + if (nOffset >= nLength) return XSTDERR; + + return nOffset; +} + +int XRTP_ParsePacket(xrtp_packet_t *pPacket, uint8_t *pData, size_t nLength) +{ + /* Parse RTP header */ + int nOffset = XRTP_ParseHeader(&pPacket->rtpHeader, pData, nLength); + if (nOffset < 0) return XSTDERR; + + /* Parse pPayload header */ + pPacket->nIdent = pData[nOffset++] << 16; + pPacket->nIdent += pData[nOffset++] << 8; + pPacket->nIdent += pData[nOffset++]; + pPacket->nFragType = (pData[nOffset] & 0xc0) >> 6; + pPacket->nDataType = (pData[nOffset] & 0x30) >> 4; + pPacket->nPackets = (pData[nOffset] & 0x0F); + pPacket->nPayloadSize = (int)nLength - 4 * ((int)pPacket->rtpHeader.nSCRCCount + 3); + pPacket->pPayload = &pData[nOffset]; + + nOffset++; + int i; + + /* Get data bytes from the blocks */ + for (i = 0; i < pPacket->nPackets; i++) + { + /* Corrupt packet (?) */ + if (nOffset >= nLength) return XSTDERR; + pPacket->nLength = pData[nOffset++] << 8; + pPacket->nLength += pData[nOffset++]; + nOffset += pPacket->nLength; + } + + /* Get data bytes from the fragment */ + if (pPacket->nPackets == 0) + { + pPacket->nLength = pData[nOffset++] << 8; + pPacket->nLength += pData[nOffset++]; + nOffset += pPacket->nLength; + } + + /* Unused bytes in the packet */ + if (nLength - nOffset > 0) + pPacket->nUnusedBytes = (int)nLength - nOffset; + + return nOffset; +} + +uint8_t *XRTP_AssemblePacket(xrtp_header_t *pHeader, const uint8_t *pData, size_t nLength) +{ + uint8_t *pPacket = (uint8_t*)malloc(XRTP_PACKET_SIZE); + if (pPacket == NULL) return NULL; + + /* Get Ready */ + uint16_t nSequenceNumber = htons(pHeader->nSequence); + uint32_t nTimeStamp = htonl(pHeader->nTimeStamp); + uint32_t nVideoHeader = htonl(0x00000000); + uint16_t nNetworkHeader = htons(0x8020); + + /* Assemble packet */ + memcpy(&pPacket[0], &nNetworkHeader, 2); + memcpy(&pPacket[2], &nSequenceNumber, 2); + memcpy(&pPacket[4], &nTimeStamp, 4); + memcpy(&pPacket[8], &pHeader->nSSRC, 4); + memcpy(&pPacket[12], &nVideoHeader, 4); + + size_t nCopySize = XSTD_MIN(XRTP_PACKET_SIZE - 16, nLength); + if (nCopySize) memcpy(&pPacket[16], &pData, nCopySize); + + return pPacket; +} diff --git a/media/rtp.h b/media/rtp.h new file mode 100644 index 0000000..a7248be --- /dev/null +++ b/media/rtp.h @@ -0,0 +1,59 @@ +/*! + * @file libxutils/media/rtp.h + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of RTP packet parser functionality + */ + +#ifndef __XUTILS_RTP_H__ +#define __XUTILS_RTP_H__ + +#include +#include +#include + +#define SCRC_MAX 15 + +typedef struct XRTPHeader{ + uint32_t nVersion; + uint32_t nPadding; + uint32_t nSequence; + uint32_t nExtension; + uint32_t nSCRCCount; + uint32_t nMarkerBit; + uint32_t nPayloadType; + uint32_t SCRC[SCRC_MAX]; + uint32_t nTimeStamp; + uint32_t nSSRC; +} xrtp_header_t; + +typedef struct XRTPPacket { + xrtp_header_t rtpHeader; + uint32_t nIdent; + uint8_t *pPayload; + int nPayloadSize; + int nUnusedBytes; + int nFragType; + int nDataType; + int nPackets; + int nLength; +} xrtp_packet_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t XRTP_GetTimestamp(float fRate); +int XRTP_ValidatePayload(const uint8_t *pPayload, size_t nSize); +int XRTP_ParseHeader(xrtp_header_t *pHeader, const uint8_t *pData, size_t nLength); +int XRTP_ParsePacket(xrtp_packet_t *pPacket, uint8_t *pData, size_t nLength); +uint8_t *XRTP_AssemblePacket(xrtp_header_t *pHeader, const uint8_t *pData, size_t nLength); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_RTP_H__ */ \ No newline at end of file diff --git a/media/ts.c b/media/ts.c new file mode 100644 index 0000000..ff71d0c --- /dev/null +++ b/media/ts.c @@ -0,0 +1,498 @@ +/*! + * @file libxutils/media/ts.c + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of TS packet parser functionality + */ + +#include +#include +#include +#include +#include "ts.h" + +void XBitParser_Init(xbit_parser_t *pParser, uint8_t *pData, size_t nSize) +{ + pParser->pData = pData; + pParser->nSize = nSize; + pParser->nMask = 0x80; + pParser->nOffset = 0; + pParser->nError = 0; + pParser->nData = 0; +} + +uint32_t XBitParser_NextBit(xbit_parser_t *pParser) +{ + if (pParser->nMask != 0x01) + { + pParser->nMask >>= 1; + return pParser->nOffset; + } + + pParser->nMask = 0x80; + pParser->nOffset++; + return pParser->nOffset; +} + +uint8_t XBitParser_ReadBit(xbit_parser_t *pParser) +{ + uint8_t nRead = !!(pParser->nMask & pParser->pData[pParser->nOffset]); + if (XBitParser_NextBit(pParser) >= pParser->nSize) pParser->nError = 1; + return nRead; +} + +uint64_t XBitParser_ReadBits(xbit_parser_t *pParser, uint8_t nBits) +{ + uint64_t nReadBits = 0; + while(nBits-- && !pParser->nError) + { + nReadBits <<= 1; + nReadBits |= XBitParser_ReadBit(pParser); + } + return nReadBits; +} + +uint64_t XBitParser_WriteBit(xbit_parser_t *pParser, uint64_t nData, uint64_t nMask) +{ + if (nData & nMask) pParser->pData[pParser->nOffset] |= pParser->nMask; + else pParser->pData[pParser->nOffset] &= ~pParser->nMask; + + XBitParser_NextBit(pParser); + return nMask >> 1; +} + +void XBitParser_WriteBits(xbit_parser_t *pParser, uint8_t nBits, uint64_t nData) +{ + uint64_t nWriteMask = (uint64_t)(((uint64_t)1) << (uint64_t)(nBits - 1)); + while(nBits--) nWriteMask = XBitParser_WriteBit(pParser, nData, nWriteMask); +} + +int XTSParser_ParseHeader(xbit_parser_t *pParser, xts_packet_header_t *pHdr) +{ + pHdr->sync_byte = (uint8_t)XBitParser_ReadBits(pParser, 8); + pHdr->transport_error_indicator = (uint8_t)XBitParser_ReadBits(pParser, 1); + pHdr->payload_unit_start_indicator = (uint8_t)XBitParser_ReadBits(pParser, 1); + pHdr->transport_priority = (uint8_t)XBitParser_ReadBits(pParser, 1); + pHdr->PID = (uint16_t)XBitParser_ReadBits(pParser, 13); + pHdr->transports_crambling_control = (uint8_t)XBitParser_ReadBits(pParser, 2); + pHdr->adaptation_field_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pHdr->payload_data_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pHdr->continuty_counter = (uint8_t)XBitParser_ReadBits(pParser, 4); + return !pParser->nError; +} + +int XTSParser_ParseAdaptationField(xbit_parser_t *pParser, xadaptation_field_t *pField) +{ + pField->adaptation_field_length = (uint8_t)XBitParser_ReadBits(pParser, 8); + if (!pField->adaptation_field_length) return 0; + + pField->discontinuity_indicator = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->random_access_indicator = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->elementary_stream_priority_indicator = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->PCR_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->OPCR_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->splicing_point_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->transport_private_data_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->adaptation_field_extension_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + + if (pField->PCR_flag) pField->PCR = XBitParser_ReadBits(pParser, 48);; + if (pField->OPCR_flag) pField->OPCR = XBitParser_ReadBits(pParser, 48); + if (pField->splicing_point_flag) pField->splice_countdown = (uint8_t)XBitParser_ReadBits(pParser, 8); + + if (pField->transport_private_data_flag && !pParser->nError) + { + pField->transport_private_data_length = (uint8_t)XBitParser_ReadBits(pParser, 8); + pField->transport_private_data = &pParser->pData[pParser->nOffset]; + XBitParser_ReadBits(pParser, 8 * pField->transport_private_data_length); + } + + if (pField->adaptation_field_extension_flag) + { + pField->adaptation_extension_length = (uint8_t)XBitParser_ReadBits(pParser, 8); + pField->legal_time_window = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->piecewise_rate_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->seamless_splice_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->reserved = (uint8_t)XBitParser_ReadBits(pParser, 5); + + if (pField->legal_time_window) + { + pField->LTW_valid_flag = (uint8_t)XBitParser_ReadBits(pParser, 1); + pField->LTW_offset = (uint16_t)XBitParser_ReadBits(pParser, 15); + } + + if (pField->piecewise_rate_flag) + { + pField->piecewise_reserved = (uint8_t)XBitParser_ReadBits(pParser, 2); + pField->piecewise_rate = (uint32_t)XBitParser_ReadBits(pParser, 22); + } + + if (pField->seamless_splice_flag) + { + pField->splice_type = (uint8_t)XBitParser_ReadBits(pParser, 4); + pField->DTS_next_access_unit = XBitParser_ReadBits(pParser, 36); + } + } + + return !pParser->nError; +} + +int XTSParser_Parse(xts_packet_t *pTS, uint8_t *pData, uint32_t nSize) +{ + if (pData == NULL || nSize < XTS_PACKET_SIZE) return 0; + xadaptation_field_t *pField = &pTS->header.adaptationField; + pTS->payload_data = NULL; + pTS->payload_size = 0; + + xbit_parser_t parser; + XBitParser_Init(&parser, pData, nSize); + + XTSParser_ParseHeader(&parser, &pTS->header); + if (pTS->header.sync_byte != 0x47) return 0; + + if (pTS->header.adaptation_field_flag) + XTSParser_ParseAdaptationField(&parser, pField); + + if (pTS->header.payload_data_flag) + { + if (pTS->header.adaptation_field_flag) + { + int nOffset = pField->adaptation_field_length + 5; + if (nOffset < XTS_PACKET_SIZE) + { + pTS->payload_size = XTS_PACKET_SIZE - nOffset; + pTS->payload_data = &pData[nOffset]; + } + } + else + { + pTS->payload_size = XTS_PACKET_SIZE - 4; + pTS->payload_data = &pData[4]; + } + } + + return !parser.nError; +} + +int XTSParser_ParsePAT(xpat_t *pPat, uint8_t *pData, uint32_t nSize) +{ + if (pData == NULL || nSize < 8) return 0; + unsigned int i; + + xbit_parser_t parser; + XBitParser_Init(&parser, pData, nSize); + + pPat->pointer_field = (uint8_t)XBitParser_ReadBits(&parser, 8); + XBitParser_ReadBits(&parser, pPat->pointer_field * 8); + pPat->table_id = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPat->section_syntax_indicator = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPat->private_bit = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPat->reserved_bits = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPat->section_length = (uint16_t)XBitParser_ReadBits(&parser, 12); + pPat->transport_stream_id = (uint16_t)XBitParser_ReadBits(&parser, 16); + pPat->reserved = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPat->version_number = (uint8_t)XBitParser_ReadBits(&parser, 5); + pPat->current_next_indicator = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPat->section_number = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPat->last_section_number = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPat->programs = pPat->section_length/4 - 2; + + for (i = 0; i < pPat->programs && !parser.nError; i++) + { + if (i >= XTSPAT_TABLE_MAX) return 0; + xpat_table_t *pTable = &pPat->patTable[i]; + + pTable->program_number = (uint16_t)XBitParser_ReadBits(&parser, 16); + XBitParser_ReadBits(&parser, 3); + + if (pTable->program_number == 0) + pTable->network_PID = (uint16_t)XBitParser_ReadBits(&parser, 13); + else + pTable->program_map_PID = (uint16_t)XBitParser_ReadBits(&parser, 13); + } + + pPat->CRC_32 = (uint32_t)XBitParser_ReadBits(&parser, 32); + return !parser.nError; +} + +int XTSParser_ParsePMT(xpmt_t *pPmt, uint8_t *pData, uint32_t nSize) +{ + if (pData == NULL || nSize < 8) return 0; + uint32_t nRead, nDiff; + nRead = nDiff = 0; + + xbit_parser_t parser; + XBitParser_Init(&parser, pData, nSize); + + pPmt->pointer_field = (uint8_t)XBitParser_ReadBits(&parser, 8); + XBitParser_ReadBits(&parser, pPmt->pointer_field * 8); + pPmt->table_id = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPmt->section_syntax_indicator = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPmt->private_bit = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPmt->reserved_bits = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPmt->section_length = (uint16_t)XBitParser_ReadBits(&parser, 12); + + uint32_t nCurrentSize = (uint32_t)parser.nSize - parser.nOffset; + if (pPmt->section_length > nCurrentSize) return 0; + nDiff = nCurrentSize - pPmt->section_length; + + pPmt->program_number = (uint16_t)XBitParser_ReadBits(&parser, 16); + pPmt->reserved_2 = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPmt->version_number = (uint8_t)XBitParser_ReadBits(&parser, 5); + pPmt->current_next_indicator = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPmt->section_number = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPmt->last_section_number = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPmt->reserved_3 = (uint8_t)XBitParser_ReadBits(&parser, 3); + pPmt->PCR_PID = (uint16_t)XBitParser_ReadBits(&parser, 13); + pPmt->reserved_4 = (uint8_t)XBitParser_ReadBits(&parser, 4); + pPmt->program_info_length = (uint16_t)XBitParser_ReadBits(&parser, 12); + + while (nRead < pPmt->program_info_length) + { + pPmt->desc[pPmt->desc_count].descriptor_tag = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPmt->desc[pPmt->desc_count].descriptor_length = (uint8_t)XBitParser_ReadBits(&parser, 8); + + uint8_t nSize = pPmt->desc[pPmt->desc_count].descriptor_length; + uint8_t *pDstData = pPmt->desc[pPmt->desc_count].data; + uint8_t *pSrcData = &parser.pData[parser.nOffset]; + memcpy(pDstData, pSrcData, nSize); + + XBitParser_ReadBits(&parser, nSize * 8); + nRead += (nSize + 2); + pPmt->desc_count++; + } + + while (((parser.nSize - parser.nOffset) - nDiff) > 4) + { + xpmt_stream_t *pStream = &pPmt->streams[pPmt->stream_count]; + pStream->stream_type = (uint8_t)XBitParser_ReadBits(&parser, 8); + XBitParser_ReadBits(&parser, 3); + pStream->elementary_PID = (uint16_t)XBitParser_ReadBits(&parser, 13); + XBitParser_ReadBits(&parser, 4); + pStream->ES_info_length = (uint16_t)XBitParser_ReadBits(&parser, 12); + + int ES_info_length = pStream->ES_info_length; + while(ES_info_length > 0) + { + xpmt_desc_t *pDesc = &pStream->desc[pStream->desc_count]; + if (ES_info_length <= 0) return 0; + pDesc->descriptor_tag = (uint8_t)XBitParser_ReadBits(&parser, 8); + ES_info_length--; + if (ES_info_length <= 0) return 0; + pDesc->descriptor_length = (uint8_t)XBitParser_ReadBits(&parser, 8); + ES_info_length--; + if (ES_info_length < pDesc->descriptor_length) return 0; + + memcpy(pDesc->data, &parser.pData[parser.nOffset], pDesc->descriptor_length); + ES_info_length -= pDesc->descriptor_length; + XBitParser_ReadBits(&parser, pDesc->descriptor_length * 8); + pStream->desc_count++; + } + + pPmt->stream_count++; + } + + return !parser.nError; +} + +int XTSParser_ParsePES(xpes_packet_t *pPES, uint8_t* pData, int nSize) +{ + if (pData == NULL || nSize <= 0) return 0; + memset(pPES, 0, sizeof(xpes_packet_t)); + + xbit_parser_t parser; + XBitParser_Init(&parser, pData, nSize); + + pPES->packet_start_code_prefix = (uint32_t)XBitParser_ReadBits(&parser, 24); + pPES->stream_id = (uint8_t)XBitParser_ReadBits(&parser, 8); + pPES->PES_packet_length = (uint16_t)XBitParser_ReadBits(&parser, 16); + + XBitParser_ReadBits(&parser, 2); + pPES->PES_scrambling_control = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPES->PES_priority = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->data_alignment_indicator = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->copyright = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->original_or_copy = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->PTS_DTS_flags = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPES->ESCR_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->ES_rate_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->DSM_trick_mode_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->additional_copy_info_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->PES_CRC_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->PES_extension_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->PES_header_data_length = (uint8_t)XBitParser_ReadBits(&parser, 8); + + uint8_t* pExtPtr = &parser.pData[parser.nOffset]; + uint64_t nBuff[3]; + unsigned int i; + + if (pPES->PTS_DTS_flags == 2) + { + nBuff[2] = nBuff[1] = nBuff[0] = 0; + XBitParser_ReadBits(&parser, 4); + nBuff[2] = (uint8_t)XBitParser_ReadBits(&parser, 3); + XBitParser_ReadBits(&parser, 1); + nBuff[1] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + nBuff[0] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + pPES->PTS = nBuff[0] | (nBuff[1] << 15) | (nBuff[2] << 30); + } + else if (pPES->PTS_DTS_flags == 3) + { + nBuff[2] = nBuff[1] = nBuff[0] = 0; + XBitParser_ReadBits(&parser, 4); + nBuff[2] = (uint8_t)XBitParser_ReadBits(&parser, 3); + XBitParser_ReadBits(&parser, 1); + nBuff[1] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + nBuff[0] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + pPES->PTS = nBuff[0] | (nBuff[1] << 15) | (nBuff[2] << 30); + + nBuff[2] = nBuff[1] = nBuff[0] = 0; + XBitParser_ReadBits(&parser, 4); + nBuff[2] = (uint8_t)XBitParser_ReadBits(&parser, 3); + XBitParser_ReadBits(&parser, 1); + nBuff[1] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + nBuff[0] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + pPES->DTS = nBuff[0] | (nBuff[1] << 15) | (nBuff[2] << 30); + } + + if (pPES->ESCR_flag == 1) + { + nBuff[2] = nBuff[1] = nBuff[0] = 0; + XBitParser_ReadBits(&parser, 2); + nBuff[2] = (uint8_t)XBitParser_ReadBits(&parser, 3); + XBitParser_ReadBits(&parser, 1); + nBuff[1] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + nBuff[0] = (uint16_t)XBitParser_ReadBits(&parser, 15); + XBitParser_ReadBits(&parser, 1); + pPES->ESCR_ext = (uint16_t)XBitParser_ReadBits(&parser, 9); + XBitParser_ReadBits(&parser, 1); + pPES->ESCR_base = nBuff[0] | (nBuff[1] << 15) | (nBuff[2] << 30); + } + + if (pPES->ES_rate_flag == 1) + { + XBitParser_ReadBits(&parser, 1); + pPES->ES_rate = (uint32_t)XBitParser_ReadBits(&parser, 22); + XBitParser_ReadBits(&parser, 1); + } + + if (pPES->DSM_trick_mode_flag == 1) + { + pPES->trick_mode_control = (uint8_t)XBitParser_ReadBits(&parser, 3); + if (pPES->trick_mode_control == 0) + { + pPES->field_id = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPES->intra_slice_refresh = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->frequency_truncation = (uint8_t)XBitParser_ReadBits(&parser, 2); + } + else if (pPES->trick_mode_control == 1) + { + pPES->rep_cntrl = (uint8_t)XBitParser_ReadBits(&parser, 5); + } + else if (pPES->trick_mode_control == 2) + { + pPES->field_id = (uint8_t)XBitParser_ReadBits(&parser, 2); + XBitParser_ReadBits(&parser, 3); + } + else if (pPES->trick_mode_control == 3) + { + pPES->field_id = (uint8_t)XBitParser_ReadBits(&parser, 2); + pPES->intra_slice_refresh = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->frequency_truncation = (uint8_t)XBitParser_ReadBits(&parser, 2); + } + else if (pPES->trick_mode_control == 4) + { + pPES->rep_cntrl = (uint8_t)XBitParser_ReadBits(&parser, 5); + } + else + { + XBitParser_ReadBits(&parser, 5); + } + } + + if (pPES->additional_copy_info_flag == 1) + { + XBitParser_ReadBits(&parser, 1); + pPES->additional_copy_info = (uint8_t)XBitParser_ReadBits(&parser, 7); + } + + if (pPES->PES_CRC_flag == 1) + { + pPES->previous_PES_packet_CRC = (uint16_t)XBitParser_ReadBits(&parser, 16); + } + + if (pPES->PES_extension_flag == 1) + { + pPES->PES_private_data_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->pack_header_field_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->program_packet_sequence_counter_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->P_STD_buffer_flag = (uint8_t)XBitParser_ReadBits(&parser, 1); + XBitParser_ReadBits(&parser, 3); + pPES->PES_extension_flag_2 = (uint8_t)XBitParser_ReadBits(&parser, 1); + + if (pPES->PES_private_data_flag == 1) + { + for (i = 0; i < 16 && !parser.nError; i++) + { + pPES->private_data[i] = (uint8_t)XBitParser_ReadBits(&parser, 8); + } + } + + if (pPES->pack_header_field_flag == 1) + { + pPES->pack_field_length = (uint8_t)XBitParser_ReadBits(&parser, 8); + for (i = 0; i < pPES->pack_field_length && !parser.nError; i++) + { + pPES->pack_field[i] = (uint8_t)XBitParser_ReadBits(&parser, 8); + } + } + + if (pPES->program_packet_sequence_counter_flag == 1) + { + XBitParser_ReadBits(&parser, 1); + pPES->program_packet_sequence_counter = (uint8_t)XBitParser_ReadBits(&parser, 7); + XBitParser_ReadBits(&parser, 1); + pPES->MPEG1_MPEG2_identifier = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->original_stuff_length = (uint8_t)XBitParser_ReadBits(&parser, 6); + } + + if (pPES->P_STD_buffer_flag == 1) + { + XBitParser_ReadBits(&parser, 2); + pPES->P_STD_buffer_scale = (uint8_t)XBitParser_ReadBits(&parser, 1); + pPES->P_STD_buffer_size = (uint16_t)XBitParser_ReadBits(&parser, 13); + } + + if (pPES->PES_extension_flag_2 == 1) + { + XBitParser_ReadBits(&parser, 1); + pPES->PES_extension_field_length = (uint8_t)XBitParser_ReadBits(&parser, 7); + for (i = 0; i < pPES->PES_extension_field_length && !parser.nError; i++) + { + pPES->PES_extension_field[i] = (uint8_t)XBitParser_ReadBits(&parser, 8); + } + } + } + + uint8_t *pOffset = &parser.pData[parser.nOffset]; + while((pOffset - pExtPtr) < pPES->PES_header_data_length) + { + XBitParser_ReadBits(&parser, 8); + if (parser.nError) return 0; + pOffset = &parser.pData[parser.nOffset]; + } + + pPES->data = &parser.pData[parser.nOffset]; + pPES->data_size = (uint32_t)parser.nSize - parser.nOffset; + if (pPES->packet_start_code_prefix == 1) return 1; + + return !parser.nError; +} \ No newline at end of file diff --git a/media/ts.h b/media/ts.h new file mode 100644 index 0000000..3634d73 --- /dev/null +++ b/media/ts.h @@ -0,0 +1,216 @@ +/*! + * @file libxutils/media/ts.h + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of TS packet parser functionality + */ + +#ifndef __XUTILS_TS_H__ +#define __XUTILS_TS_H__ + +#include +#include +#include + +#define XTSPMT_DESC_DATA_MAX 1024 +#define XTSPMT_DESCRIPTIONS_MAX 16 +#define XTSPMT_STREAMS_MAX 16 +#define XTSPAT_TABLE_MAX 64 +#define XTS_PACKET_SIZE 188 + +typedef struct XAdaptationField { + uint8_t adaptation_field_length:8; + uint8_t discontinuity_indicator:1; + uint8_t random_access_indicator:1; + uint8_t elementary_stream_priority_indicator:1; + uint8_t PCR_flag:1; + uint8_t OPCR_flag:1; + uint8_t splicing_point_flag:1; + uint8_t transport_private_data_flag:1; + uint8_t adaptation_field_extension_flag:1; + /* Optional fields */ + uint64_t PCR:48; + uint64_t OPCR:48; + uint8_t splice_countdown:8; + uint8_t transport_private_data_length:8; + uint8_t *transport_private_data; + /* Adaptation extension */ + uint8_t adaptation_extension_length:8; + uint8_t legal_time_window:1; + uint8_t piecewise_rate_flag:1; + uint8_t seamless_splice_flag:1; + uint8_t reserved:5; + uint8_t LTW_valid_flag:1; + uint16_t LTW_offset:15; + uint8_t piecewise_reserved:2; + uint32_t piecewise_rate:22; + uint8_t splice_type:4; + uint64_t DTS_next_access_unit:36; +} xadaptation_field_t; + +typedef struct xts_packet_header_t_ { + uint8_t sync_byte:8; + uint8_t transport_error_indicator:1; + uint8_t payload_unit_start_indicator:1; + uint8_t transport_priority:1; + uint16_t PID:13; + uint8_t transports_crambling_control:2; + uint8_t adaptation_field_flag:1; + uint8_t payload_data_flag:1; + uint8_t continuty_counter:4; + xadaptation_field_t adaptationField; +} xts_packet_header_t; + +typedef struct XTSParser { + xts_packet_header_t header; + uint32_t payload_size; + uint8_t *payload_data; +} xts_packet_t; + +typedef struct XPESPacket { + /* Common */ + uint32_t packet_start_code_prefix:24; + uint8_t stream_id:8; + uint16_t PES_packet_length:16; + uint8_t PES_scrambling_control:2; + uint8_t PES_priority:1; + uint8_t data_alignment_indicator:1; + uint8_t copyright:1; + uint8_t original_or_copy:1; + uint8_t PTS_DTS_flags:2; + uint8_t ESCR_flag:1; + uint8_t ES_rate_flag:1; + uint8_t DSM_trick_mode_flag:1; + uint8_t additional_copy_info_flag:1; + uint8_t PES_CRC_flag:1; + uint8_t PES_extension_flag:1; + uint8_t PES_header_data_length:1; + /* Timestamps */ + uint64_t PTS; + uint64_t DTS; + /* ESCR */ + uint64_t ESCR_base; + uint16_t ESCR_ext; + uint32_t ES_rate:22; + /* Trick control */ + uint8_t trick_mode_control:3; + uint8_t field_id:2; + uint8_t intra_slice_refresh:1; + uint8_t frequency_truncation:2; + uint8_t rep_cntrl:5; + /* Additional */ + uint8_t additional_copy_info:7; + uint16_t previous_PES_packet_CRC:16; + uint8_t PES_private_data_flag:1; + uint8_t pack_header_field_flag:1; + uint8_t program_packet_sequence_counter_flag:1; + uint8_t P_STD_buffer_flag:1; + uint8_t PES_extension_flag_2:1; + uint8_t private_data[16]; + uint8_t pack_field_length; + uint8_t pack_field[256]; + uint8_t program_packet_sequence_counter:7; + uint8_t MPEG1_MPEG2_identifier:1; + uint8_t original_stuff_length:6; + uint8_t P_STD_buffer_scale:1; + uint16_t P_STD_buffer_size:13; + uint8_t PES_extension_field_length:7; + uint8_t PES_extension_field[128]; + /* Data and size */ + uint8_t* data; + uint32_t data_size; +} xpes_packet_t; + +typedef struct XPATTable { + uint16_t program_number:16; + uint16_t network_PID:13; + uint16_t program_map_PID:13; +} xpat_table_t; + +typedef struct XPAT { + uint8_t pointer_field:8; + uint8_t table_id:8; + uint8_t section_syntax_indicator:1; + uint8_t private_bit:1; + uint8_t reserved_bits:2; + uint16_t section_length:12; + uint16_t transport_stream_id:16; + uint8_t reserved:2; + uint8_t version_number:5; + uint8_t current_next_indicator:1; + uint8_t section_number:8; + uint8_t last_section_number:8; + uint16_t programs; + uint32_t CRC_32; + xpat_table_t patTable[XTSPAT_TABLE_MAX]; +} xpat_t; + +typedef struct XPMTDesc { + uint8_t descriptor_tag:8; + uint8_t descriptor_length:8; + uint8_t data[XTSPMT_DESC_DATA_MAX]; +} xpmt_desc_t; + +typedef struct XPMTStream { + uint8_t stream_type:8; + uint16_t elementary_PID:13; + uint16_t ES_info_length:12; + uint16_t desc_count; + xpmt_desc_t desc[XTSPMT_DESCRIPTIONS_MAX]; +} xpmt_stream_t; + +typedef struct XPMT { + uint8_t pointer_field:8; + uint8_t table_id:8; + uint8_t section_syntax_indicator:1; + uint8_t private_bit:1; + uint8_t reserved_bits:2; + uint16_t section_length:12; + uint16_t program_number:16; + uint8_t reserved_2:2; + uint8_t version_number:5; + uint8_t current_next_indicator:1; + uint8_t section_number:8; + uint8_t last_section_number:8; + uint8_t reserved_3:3; + uint16_t PCR_PID:13; + uint8_t reserved_4:4; + uint16_t program_info_length:12; + uint32_t desc_count; + uint16_t stream_count; + xpmt_desc_t desc[XTSPMT_DESCRIPTIONS_MAX]; + xpmt_stream_t streams[XTSPMT_STREAMS_MAX]; + uint32_t CRC_32; +} xpmt_t; + +typedef struct XBitParser { + uint32_t nOffset; + uint64_t nData; + uint8_t *pData; + uint8_t nError; + uint8_t nMask; + size_t nSize; +} xbit_parser_t; + +#ifdef __cplusplus +extern "C" { +#endif + +void XBitParser_Init(xbit_parser_t *pParser, uint8_t *pData, size_t nSize); +void XBitParser_WriteBits(xbit_parser_t *pParser, uint8_t nBits, uint64_t nData); +uint64_t XBitParser_ReadBits(xbit_parser_t *pParser, uint8_t nBits); + +int XTSParser_ParseAdaptationField(xbit_parser_t *pParser, xadaptation_field_t *pField); +int XTSParser_ParseHeader(xbit_parser_t *pParser, xts_packet_header_t *pHdr); +int XTSParser_ParsePES(xpes_packet_t *pPES, uint8_t* pData, int nSize); +int XTSParser_ParsePAT(xpat_t *pPat, uint8_t *pData, uint32_t nSize); +int XTSParser_ParsePMT(xpmt_t *pPmt, uint8_t *pData, uint32_t nSize); +int XTSParser_Parse(xts_packet_t *pTS, uint8_t *pData, uint32_t nSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_TS_H__ */ \ No newline at end of file diff --git a/media/xapi.c b/media/xapi.c new file mode 100644 index 0000000..8b7859a --- /dev/null +++ b/media/xapi.c @@ -0,0 +1,437 @@ +/*! + * @file libxutils/media/xapi.c + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of high performance event based non-blocking REST API listener. + * The library will use poll(), epoll() or WSAPoll() depending on the operating system. + */ + +#include "xapi.h" +#include "xbuf.h" + +const char* XAPI_GetStatus(xapi_status_t eStatus) +{ + switch (eStatus) + { + case XAPI_EREGISTER: + return "Failed to register event"; + case XAPI_ERECYCLE: + return "Failed to recycle HTTP handle"; + case XAPI_EALLOC: + return "Memory allocation failure"; + case XAPI_CLOSED: + return "Closed remote connection"; + case XAPI_HUNGED: + return "Connection hunged"; + case XAPI_DESTROY: + return "Service destroyed"; + default: + break; + } + + return "Unknown status"; +} + +const char* XAPI_GetStatusStr(xapi_ctx_t *pCtx) +{ + switch (pCtx->eStType) + { + case XAPI_ST_EVENT: + return XEvents_Status((xevent_status_t)pCtx->nStatus); + case XAPI_ST_HTTP: + return XHTTP_GetStatusStr((xhttp_status_t)pCtx->nStatus); + case XAPI_ST_SOCK: + return XSock_GetStatusStr((xsock_status_t)pCtx->nStatus); + case XAPI_ST_API: + return XAPI_GetStatus((xapi_status_t)pCtx->nStatus); + default: break; + } + + return "Unknown status"; +} + +static xapi_data_t* XAPI_NewData(xapi_t *pApi, XSOCKET nFD) +{ + xapi_data_t *pData = (xapi_data_t*)malloc(sizeof(xapi_data_t)); + if (pData == NULL) return NULL; + + pData->pSessionData = NULL; + pData->ePktType = XAPI_NONE; + pData->pPacket = NULL; + pData->pEvData = NULL; + pData->pApi = pApi; + pData->nFD = nFD; + return pData; +} + +static int XAPI_Callback(xapi_t *pApi, xapi_data_t *pApiData, xapi_cb_type_t eCbType, xapi_st_type_t eStType, uint8_t nStat) +{ + if (pApi == NULL) return XSTDERR; + else if (pApi->callback == NULL) return XSTDOK; + + xapi_ctx_t ctx; + ctx.eCbType = eCbType; + ctx.eStType = eStType; + ctx.nStatus = nStat; + ctx.pApi = pApi; + + return pApi->callback(&ctx, pApiData); +} + +static int XAPI_ServiceCb(xapi_t *pApi, xapi_data_t *pApiData, xapi_cb_type_t eCbType) +{ + return XAPI_Callback( + pApi, + pApiData, + eCbType, + XAPI_ST_API, + XAPI_NONE + ); +} + +static int XAPI_ErrorCb(xapi_t *pApi, xapi_data_t *pApiData, xapi_st_type_t eType, uint8_t nStat) +{ + return XAPI_Callback( + pApi, + pApiData, + XAPI_CB_ERROR, + eType, + nStat + ); +} + +static int XAPI_StatusCb(xapi_t *pApi, xapi_data_t *pApiData, xapi_st_type_t eType, uint8_t nStat) +{ + return XAPI_Callback( + pApi, + pApiData, + XAPI_CB_STATUS, + eType, + nStat + ); +} + +int XAPI_SetEvents(xapi_data_t *pData, int nEvents) +{ + xevent_data_t *pEvData = pData->pEvData; + xapi_t *pApi = pData->pApi; + + xevent_status_t eStatus = XEvents_Modify(&pApi->events, pEvData, nEvents); + if (eStatus != XEVENT_STATUS_SUCCESS) + { + XAPI_ErrorCb(pApi, pData, XAPI_ST_EVENT, eStatus); + return XSTDERR; + } + + return XSTDOK; +} + +xbyte_buffer_t* XAPI_GetTxBuff(xapi_data_t *pData) +{ + if (pData == NULL) return NULL; + xhttp_t *pHandle = (xhttp_t*)pData->pPacket; + return pHandle != NULL ? &pHandle->dataRaw : NULL; +} + +static int XAPI_ClearEvent(xapi_t *pApi, xevent_data_t *pEvData) +{ + if (pEvData == NULL) return XEVENTS_CONTINUE; + + if (pEvData->nFD >= 0 && pEvData->bIsOpen) + { + shutdown(pEvData->nFD, XSHUT_RDWR); + xclosesock(pEvData->nFD); + pEvData->bIsOpen = XFALSE; + pEvData->nFD = XSOCK_INVALID; + } + + if (pEvData->pContext != NULL) + { + xapi_data_t *pApiData = (xapi_data_t*)pEvData->pContext; + XAPI_ServiceCb(pApi, pApiData, XAPI_CB_CLOSED); + + if (pApiData->pPacket != NULL) + { + XHTTP_Clear((xhttp_t*)pApiData->pPacket); + pApiData->pPacket = NULL; + } + + free(pApiData); + pEvData->pContext = NULL; + } + + return XEVENTS_CONTINUE; +} + +static int XAPI_ReadEvent(xevents_t *pEvents, xevent_data_t *pEvData) +{ + if (pEvents == NULL || pEvData == NULL) return XEVENTS_DISCONNECT; + xapi_t *pApi = (xapi_t*)pEvents->pUserSpace; + xsock_t *pListener = (xsock_t*)&pApi->listener; + xsock_t clientSock; + + if (pListener->nFD == pEvData->nFD && + pEvData->nType == XAPI_EVENT_LISTENER) + { + if (XSock_Accept(pListener, &clientSock) == XSOCK_INVALID || + XSock_NonBlock(&clientSock, XTRUE) == XSOCK_INVALID) + { + XAPI_ErrorCb(pApi, NULL, XAPI_ST_SOCK, clientSock.eStatus); + return XEVENTS_CONTINUE; + } + + xapi_data_t *pApiData = XAPI_NewData(pApi, clientSock.nFD); + if (pApiData == NULL) + { + XAPI_ErrorCb(pApi, NULL, XAPI_ST_API, XAPI_EALLOC); + XSock_Close(&clientSock); + return XEVENTS_CONTINUE; + } + + xhttp_t *pHandle = XHTTP_Alloc(XHTTP_DUMMY, XSTDNON); + if (pHandle == NULL) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_API, XAPI_EALLOC); + XSock_Close(&clientSock); + return XEVENTS_CONTINUE; + } + + pApiData->ePktType = XPKT_HTTP; + pApiData->pPacket = pHandle; + + xevent_data_t *pEventData = XEvents_RegisterEvent(pEvents, pApiData, pApiData->nFD, XSTDNON, XAPI_EVENT_PEER); + if (pEventData == NULL) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_API, XAPI_EREGISTER); + XSock_Close(&clientSock); + XHTTP_Clear(pHandle); + free(pApiData); + return XEVENTS_CONTINUE; + } + + pApiData->pSessionData = NULL; + pApiData->pEvData = pEventData; + + if (XAPI_ServiceCb(pApi, pApiData, XAPI_CB_ACCEPTED) < 0) + { + XEvents_Delete(pEvents, pEventData); + return XEVENTS_CONTINUE; + } + + return XEVENTS_ACCEPT; + } + else if (pEvData->nType == XAPI_EVENT_PEER) + { + XSock_Init(&clientSock, XSOCK_TCP_PEER, pEvData->nFD, XTRUE); + xapi_data_t *pApiData = (xapi_data_t*)pEvData->pContext; + + xhttp_t *pHandle = (xhttp_t*)pApiData->pPacket; + if (pHandle == NULL) return XEVENTS_DISCONNECT; + + xhttp_status_t eStatus = XHTTP_Receive(pHandle, &clientSock); + if (eStatus == XHTTP_COMPLETE) + { + int nStatus = XAPI_ServiceCb(pApi, pApiData, XAPI_CB_REQUEST); + if (nStatus < 0) return XEVENTS_DISCONNECT; + + if (XHTTP_Recycle(pHandle) < 0) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_API, XAPI_ERECYCLE); + pEvData->pContext = NULL; + return XEVENTS_DISCONNECT; + } + + pHandle->eType = XHTTP_RESPONSE; + pApiData->ePktType = XPKT_HTTP; + pApiData->pPacket = pHandle; + + return (nStatus == XSTDUSR) ? + XEVENTS_USERCB : + XEVENTS_CONTINUE; + } + else if (eStatus == XHTTP_ERRREAD) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_SOCK, clientSock.eStatus); + pEvData->bIsOpen = XFALSE; + return XEVENTS_DISCONNECT; + } + else if (eStatus != XHTTP_PARSED && + eStatus != XHTTP_INCOMPLETE) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_HTTP, eStatus); + return XEVENTS_DISCONNECT; + } + } + + return XEVENTS_CONTINUE; +} + +static int XAPI_WriteEvent(xevents_t *pEvents, xevent_data_t *pEvData) +{ + if (pEvents == NULL || pEvData == NULL) return XEVENTS_DISCONNECT; + xapi_data_t *pApiData = (xapi_data_t*)pEvData->pContext; + xhttp_t *pHandle = (xhttp_t*)pApiData->pPacket; + xapi_t *pApi = (xapi_t*)pEvents->pUserSpace; + XSTATUS nStatus = XSTDNON; + + xbyte_buffer_t *pBuffer = &pHandle->dataRaw; + if (!pBuffer->nUsed) + { + nStatus = XAPI_ServiceCb(pApi, pApiData, XAPI_CB_WRITE); + if (!nStatus) return XEVENTS_CONTINUE; + else if (nStatus < 0) return XEVENTS_DISCONNECT; + else if (nStatus == XSTDUSR) return XEVENTS_USERCB; + else if (!pBuffer->nUsed) return XEVENTS_CONTINUE; + } + + xsock_t socket; + XSock_Init(&socket, XSOCK_TCP_PEER, pEvData->nFD, XTRUE); + + int nSent = XSock_Write(&socket, pBuffer->pData, pBuffer->nUsed); + if (nSent <= 0) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_SOCK, socket.eStatus); + pEvData->bIsOpen = XFALSE; + return XEVENTS_DISCONNECT; + } + + if (!XByteBuffer_Advance(pBuffer, nSent)) + { + nStatus = XAPI_ServiceCb(pApi, pApiData, XAPI_CB_COMPLETE); + if (nStatus < 0) return XEVENTS_DISCONNECT; + + if (XHTTP_Recycle(pHandle) < 0) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_API, XAPI_ERECYCLE); + pEvData->pContext = NULL; + return XEVENTS_DISCONNECT; + } + } + + return nStatus == XSTDUSR ? + XEVENTS_USERCB : + XEVENTS_CONTINUE; +} + +static int XAPI_InterruptEvent(xapi_t *pApi) +{ + int nStatus = XAPI_ServiceCb(pApi, NULL, XAPI_CB_INTERRUPT); + if (nStatus == XSTDUSR) return XEVENTS_USERCB; + else if (nStatus < 0) return XEVENTS_DISCONNECT; + return XEVENTS_CONTINUE; +} + +static int XAPI_UserEvent(xapi_t *pApi) +{ + int nStatus = XAPI_ServiceCb(pApi, NULL, XAPI_CB_USER); + if (nStatus == XSTDUSR) return XEVENTS_USERCB; + else if (nStatus < 0) return XEVENTS_DISCONNECT; + return XEVENTS_CONTINUE; +} + +static int XAPI_EventCallback(void *events, void* data, XSOCKET fd, int reason) +{ + xevent_data_t *pData = (xevent_data_t*)data; + xevents_t *pEvents = (xevents_t*)events; + xapi_t *pApi = (xapi_t*)pEvents->pUserSpace; + + xapi_data_t *pApiData = (pData != NULL) ? + (xapi_data_t*)pData->pContext : NULL; + + switch(reason) + { + case XEVENT_USER: + return XAPI_UserEvent(pApi); + case XEVENT_INTERRUPT: + return XAPI_InterruptEvent(pApi); + case XEVENT_CLEAR: + return XAPI_ClearEvent(pApi, pData); + case XEVENT_READ: + return XAPI_ReadEvent(pEvents, pData); + case XEVENT_WRITE: + return XAPI_WriteEvent(pEvents, pData); + case XEVENT_HUNGED: + XAPI_StatusCb(pApi, pApiData, XAPI_ST_API, XAPI_HUNGED); + return XEVENTS_DISCONNECT; + case XEVENT_CLOSED: + XAPI_StatusCb(pApi, pApiData, XAPI_ST_API, XAPI_CLOSED); + return XEVENTS_DISCONNECT; + case XEVENT_DESTROY: + XAPI_StatusCb(pApi, NULL, XAPI_ST_API, XAPI_DESTROY); + break; + default: + break; + } + + return XEVENTS_CONTINUE; +} + +int XAPI_StartListener(xapi_t *pApi, const char *pAddr, uint16_t nPort) +{ + /* Used variables */ + xevents_t *pEvents = &pApi->events; + xsock_t *pSock = &pApi->listener; + xevent_status_t status; + + /* Create server socket */ + XSock_Create(pSock, XSOCK_TCP_SERVER, pAddr, nPort); + if (pSock->nFD == XSOCK_INVALID) + { + XAPI_ErrorCb(pApi, NULL, XAPI_ST_SOCK, pSock->eStatus); + return XSTDERR; + } + + xapi_data_t *pApiData = XAPI_NewData(pApi, pSock->nFD); + if (pApiData == NULL) + { + XAPI_ErrorCb(pApi, NULL, XAPI_ST_API, XAPI_EALLOC); + XSock_Close(pSock); + return XSTDERR; + } + + /* Create event instance */ + status = XEvents_Create(pEvents, XSTDNON, pApi, XAPI_EventCallback, XTRUE); + if (status != XEVENT_STATUS_SUCCESS) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_EVENT, status); + XSock_Close(pSock); + free(pApiData); + return XSTDERR; + } + + /* Add listener socket to the event instance */ + xevent_data_t *pEvData = XEvents_RegisterEvent(pEvents, pApiData, pSock->nFD, XPOLLIN, XAPI_EVENT_LISTENER); + if (pEvData == NULL) + { + XAPI_ErrorCb(pApi, pApiData, XAPI_ST_API, XAPI_EREGISTER); + XEvents_Destroy(pEvents); + XSock_Close(pSock); + free(pApiData); + return XSTDERR; + } + + pApiData->pSessionData = NULL; + pApiData->pEvData = pEvData; + + if (XAPI_ServiceCb(pApi, pApiData, XAPI_CB_STARTED) < 0) + { + XEvents_Delete(pEvents, pEvData); + return XSTDERR; + } + + return XSTDOK; +} + +xevent_status_t XAPI_Service(xapi_t *pApi, int nTimeoutMs) +{ + xevents_t *pEvents = &pApi->events; + return XEvents_Service(pEvents, nTimeoutMs); +} + +void XAPI_Destroy(xapi_t *pApi) +{ + xevents_t *pEvents = &pApi->events; + XEvents_Destroy(pEvents); +} \ No newline at end of file diff --git a/media/xapi.h b/media/xapi.h new file mode 100644 index 0000000..b3f60c7 --- /dev/null +++ b/media/xapi.h @@ -0,0 +1,92 @@ +/*! + * @file libxutils/media/xapi.h + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of high performance event based non-blocking REST API listener. + * The library will use poll(), epoll() or WSAPoll() depending on the operating system. + */ + +#include "xstd.h" +#include "sock.h" +#include "http.h" +#include "event.h" + +#define XAPI_EVENT_PEER 0 +#define XAPI_EVENT_LISTENER 1 + +typedef enum +{ + XAPI_CB_ERROR = 0, + XAPI_CB_STATUS, + XAPI_CB_INTERRUPT, + XAPI_CB_COMPLETE, + XAPI_CB_ACCEPTED, + XAPI_CB_REQUEST, + XAPI_CB_STARTED, + XAPI_CB_CLOSED, + XAPI_CB_WRITE, + XAPI_CB_USER +} xapi_cb_type_t; + +typedef enum +{ + XAPI_ST_EVENT = 0, + XAPI_ST_HTTP, + XAPI_ST_SOCK, + XAPI_ST_API +} xapi_st_type_t; + +typedef enum +{ + XAPI_NONE = (uint8_t)0, + XAPI_EREGISTER, + XAPI_ERECYCLE, + XAPI_EALLOC, + XAPI_DESTROY, + XAPI_HUNGED, + XAPI_CLOSED +} xapi_status_t; + +typedef enum { + XPKT_INVALID = 0, + XPKT_MDTP, + XPKT_HTTP +} xpacket_type_t; + +typedef struct XAPI xapi_t; + +typedef struct XAPIData { + xpacket_type_t ePktType; + xevent_data_t *pEvData; + void *pSessionData; + void *pPacket; + xapi_t *pApi; + XSOCKET nFD; +} xapi_data_t; + +typedef struct XAPICTX { + xapi_cb_type_t eCbType; + xapi_st_type_t eStType; + uint8_t nStatus; + xapi_t *pApi; +} xapi_ctx_t; + +typedef int(*xapi_cb_t)(xapi_ctx_t *pCtx, xapi_data_t *pData); +int XAPI_SetEvents(xapi_data_t *pData, int nEvents); +xbyte_buffer_t* XAPI_GetTxBuff(xapi_data_t *pData); + +struct XAPI { + xsock_t listener; + xevents_t events; + xapi_cb_t callback; + void *pUserCtx; +}; + +const char* XAPI_GetStatus(xapi_status_t eStatus); +const char* XAPI_GetStatusStr(xapi_ctx_t *pCtx); + +int XAPI_StartListener(xapi_t *pApi, const char *pAddr, uint16_t nPort); +xevent_status_t XAPI_Service(xapi_t *pApi, int nTimeoutMs); +void XAPI_Destroy(xapi_t *pApi); \ No newline at end of file diff --git a/smake.json b/smake.json new file mode 100644 index 0000000..0caf829 --- /dev/null +++ b/smake.json @@ -0,0 +1,16 @@ +{ + "build": { + "name": "libxutils.a", + "outputDir": "./obj", + "flags": "-g -O2 -Wall -D_XUTILS_USE_SSL", + "libs": "-lpthread -lssl -lcrypto", + "excludes": [ + "./examples", + "./cmake" + ] + }, + "install": { + "headerDir": "/usr/local/include/xutils", + "binaryDir": "/usr/local/lib" + } +} diff --git a/src/addr.c b/src/addr.c new file mode 100644 index 0000000..8b174bd --- /dev/null +++ b/src/addr.c @@ -0,0 +1,282 @@ +/*! + * @file libxutils/src/addr.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes functions for detect + * IP and Mac address of the host operating system + */ + +#include "xstd.h" +#include "xstr.h" +#include "sock.h" +#include "addr.h" +#include "array.h" + +typedef struct XProtocolPorts { + const char* pProtocol; + const int nPort; + const int nRange; +} xprotocol_ports_t; + +static xprotocol_ports_t g_defaultPorts[] = +{ + { "ftp", 21, 0 }, + { "ssh", 22, 0 }, + { "smtp", 25, 0 }, + { "snmp", 161, 0 }, + { "http", 80, 0 }, + { "https", 443, 0 }, + { "unknown", -1, -1} +}; + +int XAddr_GetDefaultPort(const char *pProtocol) +{ + size_t i, nLength = strlen(pProtocol); + if (!nLength) return XSTDERR; + + for (i = 0;; i++) + { + if (!strncmp(g_defaultPorts[i].pProtocol, pProtocol, nLength) || + g_defaultPorts[i].nPort < 0) return g_defaultPorts[i].nPort; + } + + /* Never reached */ + return XSTDERR; +} + +void XLink_Init(xlink_t *pLink) +{ + pLink->nPort = 0; + pLink->sUrl[0] = XSTR_NUL; + pLink->sAddr[0] = XSTR_NUL; + pLink->sHost[0] = XSTR_NUL; + pLink->sUser[0] = XSTR_NUL; + pLink->sPass[0] = XSTR_NUL; + pLink->sFile[0] = XSTR_NUL; + pLink->sProtocol[0] = XSTR_NUL; +} + +int XLink_Parse(xlink_t *pLink, const char *pInput) +{ + XLink_Init(pLink); + + size_t nPosit = 0, nLength = strlen(pInput); + if (!nLength) return XSTDERR; + + int nTokenLen = xstrsrc(pInput, "://"); + if (nTokenLen > 0) + { + xstrncpys(pLink->sProtocol, sizeof(pLink->sProtocol), pInput, nTokenLen); + xstrcase(pLink->sProtocol, XSTR_LOWER); + nPosit += (size_t)nTokenLen + 3; + } + + nTokenLen = xstrnsrc(pInput, nLength, "/", 0); + if (nTokenLen < 0) nTokenLen = nLength; + + nTokenLen = xstrnsrc(pInput, nTokenLen, "@", nPosit); + if (nTokenLen > 0) + { + xstrncpys(pLink->sUser, sizeof(pLink->sUser), &pInput[nPosit], nTokenLen); + + int nUserLen = xstrnsrc(pLink->sUser, strlen(pLink->sUser), ":", 0); + if (nUserLen > 0) + { + nUserLen++; + if (nUserLen < nTokenLen) + { + size_t nPassLen = (size_t)nTokenLen - (size_t)nUserLen; + xstrncpys(pLink->sPass, sizeof(pLink->sPass), &pLink->sUser[nUserLen], nPassLen); + } + + size_t nTermPos = XSTD_MIN(nUserLen - 1, nTokenLen); + pLink->sUser[nTermPos] = XSTR_NUL; + } + + nPosit += (size_t)nTokenLen + 1; + } + + char *pDst = &pLink->sUrl[0]; + size_t nDstSize = sizeof(pLink->sUrl); + if (!nPosit && pInput[0] == '/') nPosit++; + + nTokenLen = xstrnsrc(pInput, nLength, "/", nPosit); + if (nTokenLen > 0) + { + xstrncpys(pLink->sHost, sizeof(pLink->sHost), &pInput[nPosit], nTokenLen); + nPosit += nTokenLen; + } + else + { + nDstSize = sizeof(pLink->sHost); + pDst = &pLink->sHost[0]; + } + + size_t nHostLen = 0, nLeft = (nPosit < nLength) ? (nLength - nPosit) : 0; + if (nLeft > 0) xstrncpys(pDst, nDstSize, &pInput[nPosit], nLeft); + + size_t nAddrLen = nHostLen = strlen(pLink->sHost); + nTokenLen = xstrnsrc(pLink->sHost, nAddrLen, ":", 0); + + if (nTokenLen > 0) + { + nTokenLen++; + if (nTokenLen < (int)nAddrLen) pLink->nPort = atoi(&pLink->sHost[nTokenLen]); + nHostLen = XSTD_MIN(nAddrLen, (size_t)nTokenLen - 1); + } + + if (!pLink->nPort) + { + int nPort = XAddr_GetDefaultPort(pLink->sProtocol); + if (nPort > 0) + { + pLink->nPort = nPort; + int nAvail = (int)sizeof(pLink->sHost) - (int)strlen(pLink->sHost); + if (nAvail > 0) xstrncatf(pLink->sHost, (size_t)nAvail - 1, ":%d", pLink->nPort); + } + } + + xstrncpys(pLink->sAddr, sizeof(pLink->sAddr), pLink->sHost, nHostLen); + if (!xstrused(pLink->sUrl)) xstrncpy(pLink->sUrl, sizeof(pLink->sUrl), "/"); + + size_t nUrlLength = strlen(pLink->sUrl); + if (nUrlLength > 0 && pLink->sUrl[nUrlLength - 1] != '/') + { + xarray_t *pTokens = xstrsplit(pLink->sUrl, "/"); + if (pTokens != NULL) + { + size_t nUsed = XArray_Used(pTokens); + if (nUsed > 0) + { + const char *pLast = (const char*)XArray_GetData(pTokens, nUsed - 1); + if (pLast != NULL) xstrncpy(pLink->sFile, sizeof(pLink->sFile), pLast); + } + + XArray_Destroy(pTokens); + } + } + + return XSTDOK; +} + +int XAddr_GetIP(const char *pDNS, char *pAddr, int nSize) +{ + int nPort = 53; + struct sockaddr_in serv; + + XSOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == XSOCK_INVALID) return XSTDERR; + + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = XSock_NetAddr(pDNS); + serv.sin_port = htons(nPort); + + if (connect(sock, (const struct sockaddr*) &serv , sizeof(serv)) < 0) + { + xclosesock(sock); + return XSTDERR; + } + + struct sockaddr_in name; + xsocklen_t nameLen = sizeof(name); + + if (getsockname(sock, (struct sockaddr*) &name, &nameLen) < 0) + { + xclosesock(sock); + return XSTDERR; + } + + inet_ntop(AF_INET, &name.sin_addr, pAddr, nSize); + xclosesock(sock); + + return nameLen; +} + +#ifdef __linux__ +int XAddr_GetIFCIP(const char *pIFace, char *pAddr, int nSize) +{ + int nFD = socket(AF_INET, SOCK_DGRAM, 0); + if (nFD < 0) return XSTDERR; + + struct ifreq ifbuf; + ifbuf.ifr_addr.sa_family = AF_INET; + xstrncpy(ifbuf.ifr_name, sizeof(ifbuf.ifr_name), pIFace); + + if (ioctl(nFD, SIOCGIFADDR, &ifbuf) < 0) + { + close(nFD); + return XSTDERR; + } + + close(nFD); + + char *pIPAddr = inet_ntoa(((struct sockaddr_in *)&ifbuf.ifr_addr)->sin_addr); + return (pAddr != NULL) ? xstrncpyf(pAddr, nSize, "%s", pIPAddr) : XSTDERR; +} + +int XAddr_GetIFCMac(const char *pIFace, char *pAddr, int nSize) +{ + int nFD = socket(AF_INET, SOCK_DGRAM, 0); + if (nFD < 0) return XSTDERR; + + struct ifreq ifbuf; + xstrncpy(ifbuf.ifr_name, sizeof(ifbuf.ifr_name), pIFace); + + if (ioctl(nFD, SIOCGIFHWADDR, &ifbuf) < 0) + { + close(nFD); + return XSTDERR; + } + + unsigned char *hwaddr = (unsigned char*) ifbuf.ifr_hwaddr.sa_data; + close(nFD); + + return xstrncpyf(pAddr, nSize, "%02x:%02x:%02x:%02x:%02x:%02x", + hwaddr[0],hwaddr[1],hwaddr[2],hwaddr[3],hwaddr[4],hwaddr[5]); +} + +int XAddr_GetMAC(char *pAddr, int nSize) +{ + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) return XSTDERR; + + struct ifconf ifc; char buf[1024]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) + { + close(sock); + return XSTDERR; + } + + struct ifreq* it = ifc.ifc_req; + const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); + struct ifreq ifr; + int nLength = 0; + + for (;it != end; ++it) + { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr)) break; + + if (!(ifr.ifr_flags & IFF_LOOPBACK) && + !ioctl(sock, SIOCGIFHWADDR, &ifr)) + { + nLength = xstrncpyf(pAddr, nSize, "%02x:%02x:%02x:%02x:%02x:%02x", + ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], + ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], + ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); + + break; + } + } + + close(sock); + return nLength; +} +#endif /* __linux__ */ + diff --git a/src/addr.h b/src/addr.h new file mode 100644 index 0000000..abb20d7 --- /dev/null +++ b/src/addr.h @@ -0,0 +1,110 @@ +/*! + * @file libxutils/src/addr.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes functions for detect + * IP and Mac address of the host operating system + */ + +#ifndef __XUTILS_XADDR_H__ +#define __XUTILS_XADDR_H__ + +#define XADDR_DNS_DEFAULT "8.8.8.8" +#define XADDR_IFC_DEFAULT "eth0" + +#define XLINK_PROTOCOL_MAX 32 +#define XLINK_INFO_MAX 32 +#define XLINK_ADDR_MAX 256 +#define XLINK_NAME_MAX 1024 +#define XLINK_URL_MAX 2048 +#define XLINK_MAX 4096 + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * @struct XLink + * @brief Parsed information from the link + */ +typedef struct XLink { + int nPort; + char sUrl[XLINK_URL_MAX]; + char sHost[XLINK_ADDR_MAX]; + char sAddr[XLINK_ADDR_MAX]; + char sUser[XLINK_INFO_MAX]; + char sPass[XLINK_INFO_MAX]; + char sFile[XLINK_NAME_MAX]; + char sProtocol[XLINK_INFO_MAX]; +} xlink_t; + +/*! + * @brief Initialize xlink structure + * @param pLink The pointer of the variable to init + */ +void XLink_Init(xlink_t *pLink); + +/*! + * @brief Parse link and store values in structure + * + * @param pLink The pointer to the XLink variable + * @param pInput The string containing link to parse + * @return On success, XSTDOK is returned, or XSTDERR in case of error + */ +int XLink_Parse(xlink_t *pLink, const char *pInput); + +/*! + * @brief Get default port for particular protocol + * + * @param pProtocol The string containing protocol name + * @return On success, port number is returned, or -1 if not found + */ +int XAddr_GetDefaultPort(const char *pProtocol); + +/*! + * @brief Get MAC address of particular interface + * + * @param pIFace The string containing interface name + * @param pAddr The pointer of the destination buffer + * @param nSize Destination buffer size + * @return On success, MAC address length is returned, or XSTDERR in case of error + */ +int XAddr_GetIFCMac(const char *pIFace, char *pAddr, int nSize); + +/*! + * @brief Get IP address of particular interface + * + * @param pIFace The string containing interface name + * @param pAddr The pointer of the destination buffer + * @param nSize Destination buffer size + * @return On success, IP address length is returned, or XSTDERR in case of error + */ +int XAddr_GetIFCIP(const char *pIFace, char *pAddr, int nSize); + +/*! + * @brief Get MAC address of first available interface (except loopback) + * + * @param pAddr The pointer of the destination buffer + * @param nSize Destination buffer size + * @return On success, MAC address length is returned, or XSTDERR in case of error + */ +int XAddr_GetMAC(char *pAddr, int nSize); + +/*! + * @brief Get IP address by connecting to the DNS server + * + * @param pDNS The string containing DNS server address + * @param pAddr The pointer of the destination buffer + * @param nSize Destination buffer size + * @return On success, IP address length is returned, or XSTDERR in case of error + */ +int XAddr_GetIP(const char *pDNS, char *pAddr, int nSize); + +#ifdef __cplusplus +} +#endif + + +#endif /* __XUTILS_XADDR_H__ */ \ No newline at end of file diff --git a/src/array.c b/src/array.c new file mode 100644 index 0000000..498dfef --- /dev/null +++ b/src/array.c @@ -0,0 +1,519 @@ +/*! + * @file libxutils/src/array.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Dynamically allocated data holder + * with some sorting and search algorithms. + */ + +#include +#include +#include +#include "array.h" + +xarray_data_t *XArray_NewData(void *pData, size_t nSize, uint64_t nKey) +{ + xarray_data_t *pNewData = (xarray_data_t*)malloc(sizeof(xarray_data_t)); + if (pNewData == NULL) return NULL; + + if (pData != NULL && nSize > 0) + { + pNewData->pData = malloc(nSize + 1); + if (pNewData->pData == NULL) + { + free(pNewData); + return NULL; + } + + memcpy(pNewData->pData, pData, nSize); + pNewData->nSize = nSize; + } + else + { + pNewData->pData = pData; + pNewData->nSize = 0; + } + + pNewData->nKey = nKey; + return pNewData; +} + +void XArray_FreeData(xarray_data_t *pArrData) +{ + if (pArrData != NULL) + { + if (pArrData->pData && + pArrData->nSize > 0) + free(pArrData->pData); + + free(pArrData); + } +} + +void XArray_ClearData(xarray_t *pArr, xarray_data_t *pArrData) +{ + if (pArr != NULL && + pArrData != NULL && + pArr->clearCb != NULL) + { + pArr->clearCb(pArrData); + pArrData->pData = NULL; + pArrData->nSize = 0; + } + + XArray_FreeData(pArrData); +} + +void* XArray_Init(xarray_t *pArr, size_t nSize, uint8_t nFixed) +{ + pArr->eStatus = XARRAY_STATUS_EMPTY; + pArr->clearCb = NULL; + pArr->pData = NULL; + pArr->nSize = nSize; + pArr->nFixed = nFixed; + pArr->nAlloc = 0; + pArr->nUsed = 0; + + if (nSize) + { + pArr->pData = (xarray_data_t**)calloc(nSize, sizeof(xarray_data_t*)); + if (pArr->pData == NULL) return NULL; + } + + size_t i; + for (i = 0; i < nSize; i++) + pArr->pData[i] = NULL; + + return pArr->pData; +} + +xarray_t* XArray_New(size_t nSize, uint8_t nFixed) +{ + xarray_t *pArr = (xarray_t*)malloc(sizeof(xarray_t)); + if (pArr == NULL) return NULL; + + if (XArray_Init(pArr, nSize, nFixed) == NULL && nSize) + { + free(pArr); + return NULL; + } + + pArr->nAlloc = 1; + return pArr; +} + +void XArray_Clear(xarray_t *pArr) +{ + if (pArr->pData != NULL) + { + size_t i; + for (i = 0; i < pArr->nSize; i++) + { + XArray_ClearData(pArr, pArr->pData[i]); + pArr->pData[i] = NULL; + } + } + + pArr->eStatus = XARRAY_STATUS_EMPTY; + pArr->nUsed = 0; +} + +void XArray_Destroy(xarray_t *pArr) +{ + XArray_Clear(pArr); + if (pArr->pData != NULL) + { + free(pArr->pData); + pArr->pData = NULL; + } + + pArr->nSize = 0; + pArr->nFixed = 0; + + if (pArr->nAlloc) + free(pArr); +} + +size_t XArray_Realloc(xarray_t *pArr) +{ + if (pArr->nFixed) return pArr->nSize; + size_t nSize = 0, nUsed = pArr->nUsed; + float fQuotient = (float)nUsed / (float)pArr->nSize; + + if (nUsed && nUsed == pArr->nSize) nSize = pArr->nSize * 2; + else if (nUsed && fQuotient < 0.25) nSize = pArr->nSize / 2; + + if (nSize) + { + xarray_data_t **pData = (xarray_data_t**)malloc(sizeof(xarray_data_t*) * nSize); + if (pData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return 0; + } + + size_t nCopySize = sizeof(xarray_data_t*) * pArr->nSize; + memcpy(pData, pArr->pData, nCopySize); + + free(pArr->pData); + pArr->pData = pData; + pArr->nSize = nSize; + + size_t i; + for (i = nUsed; i < nSize; i++) + pArr->pData[i] = NULL; + } + + return pArr->nSize; +} + +size_t XArray_CheckSpace(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + + if (pArr->pData == NULL) + { + uint8_t nAlloc = pArr->nAlloc; + xarray_clear_cb_t clearCb = pArr->clearCb; + XArray_Init(pArr, XARRAY_INITIAL_SIZE, 0); + pArr->clearCb = clearCb; + pArr->nAlloc = nAlloc; + } + else if (pArr->nUsed >= pArr->nSize) + return XArray_Realloc(pArr); + + return pArr->pData == NULL ? 0 : 1; +} + +int XArray_Add(xarray_t *pArr, xarray_data_t *pNewData) +{ + if (!XArray_CheckSpace(pArr)) + { + XArray_ClearData(pArr, pNewData); + return XARRAY_FAILURE; + } + + pArr->pData[pArr->nUsed++] = pNewData; + return (int)pArr->nUsed; +} + +int XArray_AddData(xarray_t *pArr, void *pData, size_t nSize) +{ + if (pArr == NULL) return XARRAY_FAILURE; + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + return XArray_Add(pArr, pNewData); +} + +int XArray_PushData(xarray_t *pArr, void *pData, size_t nSize) +{ + xarray_data_t *pNewData = XArray_NewData(pData, 0, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + pNewData->nSize = nSize; + return XArray_Add(pArr, pNewData); +} + +int XArray_AddDataKey(xarray_t *pArr, void *pData, size_t nSize, uint64_t nKey) +{ + xarray_data_t *pNewData = XArray_NewData(pData, nSize, nKey); + + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + return XArray_Add(pArr, pNewData); +} + +xarray_data_t* XArray_Get(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return NULL; + return pArr->pData[nIndex]; +} + +void* XArray_GetData(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return NULL; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->pData : NULL; +} + +size_t XArray_GetSize(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return 0; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->nSize : 0; +} + +uint64_t XArray_GetKey(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return 0; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->nKey : 0; +} + +xarray_data_t* XArray_Remove(xarray_t *pArr, size_t nIndex) +{ + xarray_data_t *pData = XArray_Get(pArr, nIndex); + if (pData == NULL) return NULL; + + size_t i; + for (i = nIndex; i < pArr->nUsed; i++) + { + if ((i + 1) >= pArr->nUsed) break; + pArr->pData[i] = pArr->pData[i+1]; + } + + pArr->pData[--pArr->nUsed] = NULL; + XArray_Realloc(pArr); + return pData; +} + +void XArray_Delete(xarray_t *pArr, size_t nIndex) +{ + xarray_data_t *pData = XArray_Get(pArr, nIndex); + if (pData != NULL) XArray_ClearData(pArr, pData); +} + +xarray_data_t* XArray_Set(xarray_t *pArr, size_t nIndex, xarray_data_t *pNewData) +{ + xarray_data_t *pOldData = NULL; + if (nIndex < pArr->nSize) + { + pOldData = pArr->pData[nIndex]; + pArr->pData[nIndex] = pNewData; + } + + return pOldData; +} + +xarray_data_t* XArray_SetData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize) +{ + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return NULL; + } + + xarray_data_t *pOldData = XArray_Set(pArr, nIndex, pNewData); + return pOldData; +} + +xarray_data_t* XArray_Insert(xarray_t *pArr, size_t nIndex, xarray_data_t *pData) +{ + if (!XArray_CheckSpace(pArr)) return NULL; + + xarray_data_t *pOldData = XArray_Set(pArr, nIndex, pData); + if (pOldData == NULL) return NULL; + + size_t i, nNextIndex = nIndex + 1; + + for (i = nNextIndex; i < pArr->nUsed; i++) + pOldData = XArray_Set(pArr, i, pOldData); + + XArray_Add(pArr, pOldData); + return pArr->pData[nNextIndex]; +} + +xarray_data_t* XArray_InsertData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize) +{ + if (!XArray_CheckSpace(pArr)) return NULL; + + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return NULL; + } + + return XArray_Insert(pArr, nIndex, pNewData); +} + +void XArray_Swap(xarray_t *pArr, size_t nIndex1, size_t nIndex2) +{ + if (nIndex1 >= pArr->nUsed || + nIndex2 >= pArr->nUsed) return; + + xarray_data_t *pData1 = pArr->pData[nIndex1]; + pArr->pData[nIndex1] = pArr->pData[nIndex2]; + pArr->pData[nIndex2] = pData1; +} + +static int XArray_CompareSize(const void *pData1, const void *pData2, void *pCtx) +{ + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + return (int)pFirst->nSize - (int)pSecond->nSize; +} + +static int XArray_CompareKey(const void *pData1, const void *pData2, void *pCtx) +{ + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + return (int)pFirst->nKey - (int)pSecond->nKey; +} + +int XArray_Partitioning(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish) +{ + int nPivot = nStart; + while(1) + { + while (compare((void*)pArr->pData[nStart], (void*)pArr->pData[nPivot], pCtx) < 0) nStart++; + while (compare((void*)pArr->pData[nFinish], (void*)pArr->pData[nPivot], pCtx) > 0) nFinish--; + if (!compare((void*)pArr->pData[nStart], (void*)pArr->pData[nFinish], pCtx)) nFinish--; + if (nStart >= nFinish) return nStart; + XArray_Swap(pArr, nStart, nFinish); + } + + return nStart; +} + +void XArray_QuickSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish) +{ + if (nStart < nFinish) + { + int nPartitioning = XArray_Partitioning(pArr, compare, pCtx, nStart, nFinish); + XArray_QuickSort(pArr, compare, pCtx, nStart, nPartitioning); + XArray_QuickSort(pArr, compare, pCtx, nPartitioning+1, nFinish); + } +} + +void XArray_Sort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx) +{ + if (pArr == NULL || !pArr->nUsed) return; + XArray_QuickSort(pArr, compare, pCtx, 0, (int)pArr->nUsed-1); +} + +void XArray_SortBy(xarray_t *pArr, int nSortBy) +{ + if (nSortBy == XARRAY_SORTBY_SIZE) + XArray_Sort(pArr, XArray_CompareSize, NULL); + else if (nSortBy == XARRAY_SORTBY_KEY) + XArray_Sort(pArr, XArray_CompareKey, NULL); +} + +void XArray_BubbleSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx) +{ + if (pArr == NULL || !pArr->nUsed) return; + size_t i, j; + + for (i = 0; i < pArr->nUsed-1; i++) + { + for (j = 0 ; j < pArr->nUsed-i-1; j++) + { + if (compare((void*)pArr->pData[j], (void*)pArr->pData[j+1], pCtx)) + { + xarray_data_t *pData = pArr->pData[j]; + pArr->pData[j] = pArr->pData[j+1]; + pArr->pData[j+1] = pData; + } + } + } +} + +int XArray_LinearSearch(xarray_t *pArr, uint64_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + size_t i = 0; + + for (i = 0; i < pArr->nUsed; i++) + { + xarray_data_t *pData = pArr->pData[i]; + if (nKey == pData->nKey) return (int)i; + } + + return XARRAY_FAILURE; +} + +int XArray_SentinelSearch(xarray_t *pArr, uint64_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int i, nRet = 0, nLast = (int)pArr->nUsed - 1; + + xarray_data_t *pLast = pArr->pData[nLast]; + if (pLast->nKey == nKey) return nLast; + + xarray_data_t term = {.nKey = nKey, .nSize = 0, .pData = NULL}; + pArr->pData[nLast] = &term; + + for (i = 0;; i++) + { + xarray_data_t *pData = pArr->pData[i]; + if (nKey == pData->nKey) + { + pArr->pData[nLast] = pLast; + nRet = (i < nLast) ? i : -1; + break; + } + } + + return nRet; +} + +int XArray_DoubleSearch(xarray_t *pArr, uint64_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int nFront = 0, nBack = (int)pArr->nUsed - 1; + + while (nFront <= nBack) + { + xarray_data_t *pData = pArr->pData[nFront]; + if (nKey == pData->nKey) return nFront; + + pData = pArr->pData[nBack]; + if (nKey == pData->nKey) return nBack; + + nFront++; + nBack--; + } + + return XARRAY_FAILURE; +} + +int XArray_BinarySearch(xarray_t *pArr, uint64_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int nLeft = 0, nRight = (int)pArr->nUsed - 1; + + xarray_data_t *pData = pArr->pData[nLeft]; + if (pData->nKey == nKey) return nLeft; + + while (nLeft <= nRight) + { + int nMiddle = nLeft + (nRight - nLeft) / 2; + pData = pArr->pData[nMiddle]; + + if (pData->nKey < nKey) nLeft = nMiddle + 1; + else if (pData->nKey == nKey) return nMiddle; + else nRight = nMiddle - 1; + } + + return XARRAY_FAILURE; +} + +size_t XArray_Used(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + return pArr->nUsed; +} + +size_t XArray_Size(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + return pArr->nSize; +} diff --git a/src/array.h b/src/array.h new file mode 100644 index 0000000..7345cf0 --- /dev/null +++ b/src/array.h @@ -0,0 +1,99 @@ +/*! + * @file libxutils/src/array.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Dynamically allocated data holder + * with some sorting and search algorithms. + */ + +#ifndef __XUTILS_XARRAY_H__ +#define __XUTILS_XARRAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define XARRAY_SUCCESS 0 +#define XARRAY_FAILURE -1 + +#define XARRAY_INITIAL_SIZE 8 +#define XARRAY_SORTBY_SIZE 1 +#define XARRAY_SORTBY_KEY 0 + +typedef enum { + XARRAY_STATUS_OK = (uint8_t)0, + XARRAY_STATUS_EMPTY, + XARRAY_STATUS_NO_MEMORY +} xarray_status_t; + +typedef struct XArrayData { + void* pData; + size_t nSize; + uint64_t nKey; +} xarray_data_t; + +typedef int(*xarray_comparator_t)(const void*, const void*, void*); +typedef void(*xarray_clear_cb_t)(xarray_data_t *pArrData); + +typedef struct XArray_ { + xarray_data_t** pData; + xarray_clear_cb_t clearCb; + xarray_status_t eStatus; + uint8_t nFixed; + uint8_t nAlloc; + size_t nSize; + size_t nUsed; +} xarray_t; + +xarray_data_t *XArray_NewData(void *pData, size_t nSize, uint64_t nKey); +void XArray_FreeData(xarray_data_t *pArrData); +void XArray_ClearData(xarray_t *pArr, xarray_data_t *pArrData); + +xarray_t* XArray_New(size_t nSize, uint8_t nFixed); +void* XArray_Init(xarray_t *pArr, size_t nSize, uint8_t nFixed); +size_t XArray_Realloc(xarray_t *pArr); +void XArray_Destroy(xarray_t *pArr); +void XArray_Clear(xarray_t *pArr); + +int XArray_Add(xarray_t *pArr, xarray_data_t *pNewData); +int XArray_AddData(xarray_t *pArr, void* pData, size_t nSize); +int XArray_PushData(xarray_t *pArr, void *pData, size_t nSize); +int XArray_AddDataKey(xarray_t *pArr, void* pData, size_t nSize, uint64_t nKey); +void* XArray_GetData(xarray_t *pArr, size_t nIndex); +size_t XArray_GetSize(xarray_t *pArr, size_t nIndex); +uint64_t XArray_GetKey(xarray_t *pArr, size_t nIndex); + +xarray_data_t* XArray_Remove(xarray_t *pArr, size_t nIndex); +void XArray_Delete(xarray_t *pArr, size_t nIndex); +void XArray_Swap(xarray_t *pArr, size_t nIndex1, size_t nIndex2); + +void XArray_Sort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx); +void XArray_BubbleSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx); +void XArray_QuickSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish); +void XArray_SortBy(xarray_t *pArr, int nSortBy); + +int XArray_SentinelSearch(xarray_t *pArr, uint64_t nKey); +int XArray_LinearSearch(xarray_t *pArr, uint64_t nKey); +int XArray_DoubleSearch(xarray_t *pArr, uint64_t nKey); +int XArray_BinarySearch(xarray_t *pArr, uint64_t nKey); + +xarray_data_t* XArray_Get(xarray_t *pArr, size_t nIndex); +xarray_data_t* XArray_Set(xarray_t *pArr, size_t nIndex, xarray_data_t *pNewData); +xarray_data_t* XArray_Insert(xarray_t *pArr, size_t nIndex, xarray_data_t *pData); +xarray_data_t* XArray_SetData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize); +xarray_data_t* XArray_InsertData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize); + +size_t XArray_Used(xarray_t *pArr); +size_t XArray_Size(xarray_t *pArr); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XARRAY_H__ */ diff --git a/src/crypt.c b/src/crypt.c new file mode 100644 index 0000000..f416210 --- /dev/null +++ b/src/crypt.c @@ -0,0 +1,911 @@ +/*! + * @file libxutils/src/crypt.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the various crypt algorithms + */ + +#include "xstd.h" +#include "xstr.h" +#include "xaes.h" +#include "xbuf.h" +#include "crypt.h" + +#ifdef _WIN32 +#pragma warning(disable : 4146) +#endif + +#define XMD5_LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) + +#define XSHA_UPPER(x) w[(x) & 0x0F] +#define XSHA_CH(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define XSHA_MAJ(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define XSHA_ROR32(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) + +#define XSHA_SIGMA1(x) (XSHA_ROR32(x, 2) ^ XSHA_ROR32(x, 13) ^ XSHA_ROR32(x, 22)) +#define XSHA_SIGMA2(x) (XSHA_ROR32(x, 6) ^ XSHA_ROR32(x, 11) ^ XSHA_ROR32(x, 25)) +#define XSHA_SIGMA3(x) (XSHA_ROR32(x, 7) ^ XSHA_ROR32(x, 18) ^ (x >> 3)) +#define XSHA_SIGMA4(x) (XSHA_ROR32(x, 17) ^ XSHA_ROR32(x, 19) ^ (x >> 10)) + +static const uint8_t XSHA256P[64] = + { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static const uint32_t XSHA256K[64] = + { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 + }; + +static const unsigned char g_base64DecTable[XBASE64_TABLE_SIZE] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static const uint32_t g_crc32Table[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +const uint32_t g_intRadians[64] = + { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + +static char g_base64EncTable[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + +static char g_charMap[XCHAR_MAP_SIZE] = + { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + +static const uint32_t g_Radians[] = + { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 + }; + +//////////////////////////////////////////////////////// +// SHA-256 computing implementation for C/C++ based on +// pseudocode for the SHA-256 algorithm from Wikipedia. +//////////////////////////////////////////////////////// + +void XSHA256_Init(xsha256_t *pSha) +{ + memset(pSha, 0, sizeof(xsha256_t)); + pSha->uDigest.hBytes[0] = 0x6A09E667; + pSha->uDigest.hBytes[1] = 0xBB67AE85; + pSha->uDigest.hBytes[2] = 0x3C6EF372; + pSha->uDigest.hBytes[3] = 0xA54FF53A; + pSha->uDigest.hBytes[4] = 0x510E527F; + pSha->uDigest.hBytes[5] = 0x9B05688C; + pSha->uDigest.hBytes[6] = 0x1F83D9AB; + pSha->uDigest.hBytes[7] = 0x5BE0CD19; +} + +void XSHA256_Update(xsha256_t *pSha, const uint8_t *pData, size_t nLength) +{ + while (nLength > 0) + { + size_t nPart = XSTD_MIN(nLength, XSHA256_BLOCK_SIZE - pSha->nSize); + memcpy(pSha->uBlock.block + pSha->nSize, pData, nPart); + + pSha->nTotalSize += nPart; + pSha->nSize += nPart; + pData = pData + nPart; + nLength -= nPart; + + if (pSha->nSize == XSHA256_BLOCK_SIZE) + { + XSHA256_ProcessBlock(pSha); + pSha->nSize = 0; + } + } +} + +void XSHA256_Final(xsha256_t *pSha, uint8_t *pDigest) +{ + size_t nPaddingSize = (pSha->nSize < 56) ? (56 - pSha->nSize) : (120 - pSha->nSize); + size_t i, nTotalSize = pSha->nTotalSize * 8; + + XSHA256_Update(pSha, XSHA256P, nPaddingSize); + pSha->uBlock.wBytes[14] = htobe32((uint32_t) (nTotalSize >> 32)); + pSha->uBlock.wBytes[15] = htobe32((uint32_t) nTotalSize); + XSHA256_ProcessBlock(pSha); + + for (i = 0; i < 8; i++) pSha->uDigest.hBytes[i] = htobe32(pSha->uDigest.hBytes[i]); + if (pDigest != NULL) memcpy(pDigest, pSha->uDigest.digest, XSHA256_DIGEST_SIZE); +} + +void XSHA256_FinalRaw(xsha256_t *pSha, uint8_t *pDigest) +{ + uint8_t i; + for (i = 0; i < 8; i++) pSha->uDigest.hBytes[i] = htobe32(pSha->uDigest.hBytes[i]); + memcpy(pDigest, pSha->uDigest.digest, XSHA256_DIGEST_SIZE); + for (i = 0; i < 8; i++) pSha->uDigest.hBytes[i] = be32toh(pSha->uDigest.hBytes[i]); +} + +void XSHA256_ProcessBlock(xsha256_t *pSha) +{ + uint32_t *w = pSha->uBlock.wBytes; + uint32_t nReg[8]; + uint8_t i; + + for (i = 0; i < 8; i++) nReg[i] = pSha->uDigest.hBytes[i]; + for (i = 0; i < 16; i++) w[i] = be32toh(w[i]); + + for (i = 0; i < 64; i++) + { + if (i >= 16) XSHA_UPPER(i) += XSHA_SIGMA4(XSHA_UPPER(i + 14)) + XSHA_UPPER(i + 9) + XSHA_SIGMA3(XSHA_UPPER(i + 1)); + uint32_t nT1 = nReg[7] + XSHA_SIGMA2(nReg[4]) + XSHA_CH(nReg[4], nReg[5], nReg[6]) + XSHA256K[i] + XSHA_UPPER(i); + uint32_t nT2 = XSHA_SIGMA1(nReg[0]) + XSHA_MAJ(nReg[0], nReg[1], nReg[2]); + + nReg[7] = nReg[6]; + nReg[6] = nReg[5]; + nReg[5] = nReg[4]; + nReg[4] = nReg[3] + nT1; + nReg[3] = nReg[2]; + nReg[2] = nReg[1]; + nReg[1] = nReg[0]; + nReg[0] = nT1 + nT2; + } + + for (i = 0; i < 8; i++) + pSha->uDigest.hBytes[i] += nReg[i]; +} + +char* XCrypt_SHA256(const uint8_t *pInput, size_t nLength) +{ + size_t nAvail = XSHA256_LENGTH; + char *pHash = (char*)malloc(nAvail + 1); + if (pHash == NULL) return NULL; + + xsha256_t xsha; + XSHA256_Init(&xsha); + + uint8_t i, nDigest[XSHA256_DIGEST_SIZE]; + XSHA256_Update(&xsha, pInput, nLength); + XSHA256_Final(&xsha, nDigest); + + for (i = 0; i < XSHA256_DIGEST_SIZE; i++) + { + xstrncpyf(pHash + i * 2, nAvail, "%02x", nDigest[i]); + nAvail -= i * 2; + } + + pHash[XSHA256_LENGTH] = XSTR_NUL; + return pHash; +} + +//////////////////////////////////////////////////////// +// End of SHA-256 implementation +//////////////////////////////////////////////////////// + +uint32_t XCrypt_CRC32(const uint8_t *pInput, size_t nLength) +{ + if (pInput == NULL || !nLength) return 0; + uint32_t nCRC = 0; + unsigned int i; + + for (i = 0; i < nLength; i++) + { + uint16_t nIndex = (nCRC ^ pInput[i]) & 0xff; + nCRC = g_crc32Table[nIndex] ^ (nCRC >> 8); + } + + return nCRC; +} + +uint32_t XCrypt_CRC32B(const uint8_t *pInput, size_t nLength) +{ + if (pInput == NULL || !nLength) return 0; + uint32_t nCRC = 0xFFFFFFFF; + unsigned int i; + + for (i = 0; i < nLength; i++) + { + uint32_t nByte = pInput[i]; + nCRC = nCRC ^ nByte; + int j; + + for (j = 7; j >= 0; j--) + { + uint32_t nMask = -(nCRC & 1); + nCRC = (nCRC >> 1) ^ (0xEDB88320 & nMask); + } + } + + return ~nCRC; +} + +char* XCrypt_MD5(const uint8_t *pInput, size_t nLength) +{ + if (pInput == NULL || !nLength) return NULL; + size_t nNewLen = 0; + + uint32_t hash0 = 0x67452301; + uint32_t hash1 = 0xefcdab89; + uint32_t hash2 = 0x98badcfe; + uint32_t hash3 = 0x10325476; + + for (nNewLen = (nLength * 8 + 1); (nNewLen % 512) != 448; nNewLen++); + nNewLen /= 8; + + uint8_t *pMessage = (uint8_t*)calloc(nNewLen + 64, 1); + if (pMessage == NULL) return NULL; + + memcpy(pMessage, pInput, nLength); + pMessage[nLength] = 128; + + uint32_t nBitsLen = 8 * (uint32_t)nLength; + memcpy(pMessage + nNewLen, &nBitsLen, 4); + + uint32_t i, f, g; + int nOffset = 0; + + for (nOffset = 0; (size_t)nOffset < nNewLen; nOffset += (512 / 8)) + { + uint32_t *w = (uint32_t*)(pMessage + nOffset); + uint32_t a = hash0; + uint32_t b = hash1; + uint32_t c = hash2; + uint32_t d = hash3; + + for(i = 0; i < 64; i++) + { + if (i < 16) + { + f = (b & c) | ((~b) & d); + g = i; + } + else if (i < 32) + { + f = (d & b) | ((~d) & c); + g = (5 * i + 1) % 16; + } + else if (i < 48) + { + f = b ^ c ^ d; + g = (3 * i + 5) % 16; + } + else + { + f = c ^ (b | (~d)); + g = (7 * i) % 16; + } + + uint32_t temp = d; + d = c; + c = b; + b = b + XMD5_LEFTROTATE((a + f + g_intRadians[i] + w[g]), g_Radians[i]); + a = temp; + } + + hash0 += a; + hash1 += b; + hash2 += c; + hash3 += d; + } + + char *pOutput = (char*)malloc(XMD5_LENGTH + 1); + if (pOutput == NULL) return NULL; + + xstrncpyf(pOutput, XMD5_LENGTH + 1, + "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x" + "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + ((uint8_t *)&hash0)[0], ((uint8_t *)&hash0)[1], + ((uint8_t *)&hash0)[2], ((uint8_t *)&hash0)[3], + ((uint8_t *)&hash1)[0], ((uint8_t *)&hash1)[1], + ((uint8_t *)&hash1)[2], ((uint8_t *)&hash1)[3], + ((uint8_t *)&hash2)[0], ((uint8_t *)&hash2)[1], + ((uint8_t *)&hash2)[2], ((uint8_t *)&hash2)[3], + ((uint8_t *)&hash3)[0], ((uint8_t *)&hash3)[1], + ((uint8_t *)&hash3)[2], ((uint8_t *)&hash3)[3]); + + free(pMessage); + return pOutput; +} + +char *XCrypt_Casear(const char *pInput, size_t nLength, size_t nKey) +{ + if (pInput == NULL) return NULL; + char *pRetVal = (char*)malloc(nLength + 1); + if (pRetVal == NULL) return NULL; + + size_t nChars = sizeof(g_charMap); + size_t i, x, nHalf = nChars / 2; + + for (i = 0; i < nLength; i++) + { + char cByte = pInput[i]; + + for (x = 0; x < nChars; x++) + { + size_t nStep = x + nKey; + if (x < nHalf) while (nStep >= nHalf) nStep -= nHalf; + else { nStep += nHalf; while (nStep >= nChars) nStep -= nHalf; } + if (pInput[i] == g_charMap[x]) { cByte = g_charMap[nStep]; break; } + } + + pRetVal[i] = cByte; + } + + pRetVal[nLength] = '\0'; + return pRetVal; +} + +char *XDecrypt_Casear(const char *pInput, size_t nLength, size_t nKey) +{ + if (pInput == NULL) return NULL; + char *pRetVal = (char*)malloc(nLength + 1); + if (pRetVal == NULL) return NULL; + + size_t nChars = sizeof(g_charMap); + size_t i, x, nHalf = nChars / 2; + + for (i = 0; i < nLength; i++) + { + char cByte = pInput[i]; + + for (x = 0; x < nChars; x++) + { + size_t nStep = x - nKey; + while(nStep < XCHAR_MAP_SIZE) nStep += nHalf; + + if (x < nHalf) while (nStep >= nHalf) nStep -= nHalf; + else { nStep += nHalf; while (nStep >= nChars) nStep -= nHalf; } + if (pInput[i] == g_charMap[x]) { cByte = g_charMap[nStep]; break; } + } + + pRetVal[i] = cByte; + } + + pRetVal[nLength] = '\0'; + return pRetVal; +} + +char* XCrypt_Reverse(const char *pInput, size_t nLength) +{ + if (pInput == NULL || !nLength) return NULL; + char *pReversed = (char*)malloc(nLength + 1); + if (pReversed == NULL) return NULL; + + size_t i; + for (i = 0; i < nLength; i++) + pReversed[i] = pInput[(nLength - (i + 1))]; + + pReversed[nLength] = '\0'; + return pReversed; +} + +uint8_t* XCrypt_XOR(const uint8_t *pInput, size_t nLength, const uint8_t *pKey, size_t nKeyLen) +{ + if (pInput == NULL || !nLength || + pKey == NULL || !nKeyLen) return NULL; + + uint8_t *pCrypted = (uint8_t*)malloc(nLength + 1); + if (pCrypted == NULL) return NULL; + + size_t i; + for (i = 0; i < nLength; ++i) + pCrypted[i] = pInput[i] ^ pKey[i % nKeyLen]; + + pCrypted[nLength] = '\0'; + return pCrypted; +} + +char *XCrypt_Base64(const uint8_t *pInput, size_t *pLength) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + size_t i, j, nLength = *pLength; + size_t nOutLength = ((nLength + 2) / 3) * 4; + if (nOutLength < nLength) return NULL; + + char *pEncodedData = (char *)calloc(1, nOutLength + 1); + if (pEncodedData == NULL) return NULL; + + for (i = 0, j = 0; i < nLength;) + { + uint32_t nOctetA = i < nLength ? (unsigned char)pInput[i++] : 0; + uint32_t nOctetB = i < nLength ? (unsigned char)pInput[i++] : 0; + uint32_t nOctetC = i < nLength ? (unsigned char)pInput[i++] : 0; + uint32_t nTriple = (nOctetA << 0x10) + (nOctetB << 0x08) + nOctetC; + + pEncodedData[j++] = g_base64EncTable[(nTriple >> 3 * 6) & 0x3F]; + pEncodedData[j++] = g_base64EncTable[(nTriple >> 2 * 6) & 0x3F]; + pEncodedData[j++] = g_base64EncTable[(nTriple >> 1 * 6) & 0x3F]; + pEncodedData[j++] = g_base64EncTable[(nTriple >> 0 * 6) & 0x3F]; + } + + uint8_t nModTable[3] = {0, 2, 1}; + for (i = 0; i < nModTable[nLength % 3]; i++) + pEncodedData[nOutLength - 1 - i] = '='; + + while (pEncodedData[nOutLength] == '\0') nOutLength--; + *pLength = nOutLength + 1; + + return pEncodedData; +} + +char *XDecrypt_Base64(const uint8_t *pInput, size_t *pLength) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + size_t i, j, nLength = *pLength; + + if (nLength % 4 != 0) return NULL; + size_t nOutLength = nLength / 4 * 3 + 1; + + if (pInput[nLength - 1] == '=') (nOutLength)--; + if (pInput[nLength - 2] == '=') (nOutLength)--; + + char *pDecodedData = (char *)calloc(1, nOutLength + 1); + if (pDecodedData == NULL) return NULL; + + for (i = 0, j = 0; i < nLength;) + { + uint32_t nSextetA = pInput[i] == '=' ? 0 & i++ : g_base64DecTable[pInput[i++]]; + uint32_t nSextetB = pInput[i] == '=' ? 0 & i++ : g_base64DecTable[pInput[i++]]; + uint32_t nSextetC = pInput[i] == '=' ? 0 & i++ : g_base64DecTable[pInput[i++]]; + uint32_t nSextetD = pInput[i] == '=' ? 0 & i++ : g_base64DecTable[pInput[i++]]; + + uint32_t nTriple = (nSextetA << 3 * 6) + (nSextetB << 2 * 6) + + (nSextetC << 1 * 6) + (nSextetD << 0 * 6); + + if (j < nOutLength-1) pDecodedData[j++] = (nTriple >> 2 * 8) & 0xFF; + if (j < nOutLength-1) pDecodedData[j++] = (nTriple >> 1 * 8) & 0xFF; + if (j < nOutLength-1) pDecodedData[j++] = (nTriple >> 0 * 8) & 0xFF; + } + + while (pDecodedData[nOutLength] == '\0') nOutLength--; + *pLength = nOutLength + 1; + + return pDecodedData; +} + +uint8_t* XCrypt_AES(const uint8_t *pInput, size_t *pLength, const uint8_t *pKey, size_t nKeyLen, const uint8_t *pIV) +{ + if (pInput == NULL || pKey == NULL || !nKeyLen || + pLength == NULL || !(*pLength)) return NULL; + + xaes_context_t ctx; + XAES_SetKey(&ctx, pKey, nKeyLen, pIV); + return XAES_Encrypt(&ctx, pInput, pLength); +} + +uint8_t* XDecrypt_AES(const uint8_t *pInput, size_t *pLength, const uint8_t *pKey, size_t nKeyLen, const uint8_t *pIV) +{ + if (pInput == NULL || pKey == NULL || !nKeyLen || + pLength == NULL || !(*pLength)) return NULL; + + xaes_context_t ctx; + XAES_SetKey(&ctx, pKey, nKeyLen, pIV); + return XAES_Decrypt(&ctx, pInput, pLength); +} + +uint8_t* XCrypt_HEX(const uint8_t *pInput, size_t *pLength, const char* pSpace, size_t nColumns, xbool_t bLowCase) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + size_t i, nCount = 0, nLength = *pLength; + + xbyte_buffer_t buffer; + XByteBuffer_Init(&buffer, nLength, 0); + if (!buffer.nSize) return NULL; + + for (i = 0; i < nLength; i++) + { + const char *pFmt = bLowCase ? "%02x%s" : "%02X%s"; + const char *pDlmt = xstrused(pSpace) ? pSpace : XSTR_EMPTY; + + if (XByteBuffer_AddFmt(&buffer, pFmt, pInput[i], pDlmt) <= 0) + { + XByteBuffer_Clear(&buffer); + *pLength = 0; + return NULL; + } + + if (!nColumns) continue; + nCount++; + + if (nCount == nColumns) + { + if (XByteBuffer_AddByte(&buffer, '\n') <= 0) + { + XByteBuffer_Clear(&buffer); + *pLength = 0; + return NULL; + } + + nCount = 0; + } + } + + *pLength = buffer.nUsed; + return buffer.pData; +} + +uint8_t* XDecrypt_HEX(const uint8_t *pInput, size_t *pLength, xbool_t bLowCase) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + const char *pFmt = bLowCase ? " %02x%n" : " %02X%n"; + const char *pData = (const char*)pInput; + + xbyte_buffer_t buffer; + XByteBuffer_Init(&buffer, *pLength, 0); + if (!buffer.nSize) return NULL; + + uint8_t nVal = 0; + int nOffset = 0; + + while (sscanf(pData, pFmt, (unsigned int*)&nVal, &nOffset) == 1) + { + if (XByteBuffer_AddByte(&buffer, nVal) <= 0) + { + XByteBuffer_Clear(&buffer); + *pLength = 0; + return NULL; + } + + pData += nOffset; + } + + *pLength = buffer.nUsed; + return buffer.pData; +} + +xcrypt_chipher_t XCrypt_GetCipher(const char *pCipher) +{ + if (!strncmp(pCipher, "aes", 3)) return XC_AES; + else if (!strncmp(pCipher, "hex", 3)) return XC_HEX; + else if (!strncmp(pCipher, "xor", 3)) return XC_XOR; + else if (!strncmp(pCipher, "md5", 3)) return XC_MD5; + else if (!strncmp(pCipher, "crc32", 5)) return XC_CRC32; + else if (!strncmp(pCipher, "crc32b", 6)) return XC_CRC32B; + else if (!strncmp(pCipher, "casear", 6)) return XC_CASEAR; + else if (!strncmp(pCipher, "base64", 6)) return XC_BASE64; + else if (!strncmp(pCipher, "sha256", 6)) return XC_SHA256; + else if (!strncmp(pCipher, "reverse", 7)) return XC_REVERSE; + return XC_INVALID; +} + +const char* XCrypt_GetCipherStr(xcrypt_chipher_t eCipher) +{ + switch (eCipher) + { + case XC_AES: return "aes"; + case XC_HEX: return "hex"; + case XC_XOR: return "xor"; + case XC_MD5: return "md5"; + case XC_CRC32: return "crc32"; + case XC_CRC32B: return "crc32b"; + case XC_CASEAR: return "casear"; + case XC_BASE64: return "base64"; + case XC_SHA256: return "sha256"; + case XC_REVERSE: return "reverse"; + case XC_MULTY: return "multy"; + default: + break; + } + + return "invalid"; +} + +static xbool_t XCrypt_NeedsKey(xcrypt_chipher_t eCipher) +{ + switch (eCipher) + { + case XC_AES: + case XC_XOR: + case XC_CASEAR: + return XTRUE; + case XC_HEX: + case XC_MD5: + case XC_CRC32: + case XC_BASE64: + case XC_SHA256: + case XC_REVERSE: + return XFALSE; + default: + break; + } + + return XFALSE; +} + +void XCrypt_ErrorCallback(xcrypt_ctx_t *pCtx, const char *pStr, ...) +{ + if (pCtx->callback == NULL) return; + char sBuff[XSTR_MIN]; + + va_list args; + va_start(args, pStr); + xstrncpyarg(sBuff, sizeof(sBuff), pStr, args); + va_end(args); + + pCtx->callback(XCB_ERROR, sBuff, pCtx->pUserPtr); +} + +xbool_t XCrypt_KeyCallback(xcrypt_ctx_t *pCtx, xcrypt_chipher_t eCipher, xcrypt_key_t *pKey) +{ + memset(pKey, 0, sizeof(xcrypt_key_t)); + pKey->eCipher = eCipher; + + if (!XCrypt_NeedsKey(eCipher) || + pCtx->callback == NULL) return XTRUE; + + return pCtx->callback(XCB_KEY, pKey, pCtx->pUserPtr); +} + +void XCrypt_Init(xcrypt_ctx_t *pCtx, xbool_t bDecrypt, char *pCiphers, xcrypt_cb_t callback, void *pUser) +{ + pCtx->callback = callback; + pCtx->pUserPtr = pUser; + pCtx->bDecrypt = bDecrypt; + pCtx->pCiphers = pCiphers; + pCtx->nColumns = 0; +} + +uint8_t* XCrypt_Single(xcrypt_ctx_t *pCtx, xcrypt_chipher_t eCipher, const uint8_t *pInput, size_t *pLength) +{ + xcrypt_key_t encKey; + if (!XCrypt_KeyCallback(pCtx, eCipher, &encKey)) return XFALSE; + + const uint8_t *pKey = (const uint8_t*)encKey.sKey; + size_t nKeyLength = encKey.nLength; + + uint8_t *pCrypted = NULL; + uint32_t nCRC32 = 0; + + switch (eCipher) + { + case XC_CRC32: nCRC32 = XCrypt_CRC32(pInput, *pLength); break; + case XC_CRC32B: nCRC32 = XCrypt_CRC32B(pInput, *pLength); break; + case XC_AES: pCrypted = XCrypt_AES(pInput, pLength, pKey, nKeyLength, NULL); break; + case XC_HEX: pCrypted = XCrypt_HEX(pInput, pLength, XSTR_SPACE, pCtx->nColumns, XFALSE); break; + case XC_XOR: pCrypted = XCrypt_XOR(pInput, *pLength, pKey, nKeyLength); break; + case XC_MD5: pCrypted = (uint8_t*)XCrypt_MD5(pInput, *pLength); *pLength = XMD5_LENGTH; break; + case XC_SHA256: pCrypted = (uint8_t*)XCrypt_SHA256(pInput, *pLength); *pLength = XSHA256_LENGTH; break; + case XC_CASEAR: pCrypted = (uint8_t*)XCrypt_Casear((const char*)pInput, *pLength, atoi(encKey.sKey)); break; + case XC_BASE64: pCrypted = (uint8_t*)XCrypt_Base64(pInput, pLength); break; + case XC_REVERSE: pCrypted = (uint8_t*)XCrypt_Reverse((const char*)pInput, *pLength); break; + default: break; + } + + if (nCRC32 > 0) + { + pCrypted = (uint8_t*)malloc(XCRC32_MAX_SIZE); + if (pCrypted == NULL) + { + XCrypt_ErrorCallback(pCtx, "Can not allocate memory for CRC32 buffer"); + return NULL; + } + + *pLength = xstrncpyf((char*)pCrypted, XCRC32_MAX_SIZE, "%u", nCRC32); + } + + if (pCrypted == NULL) + { + XCrypt_ErrorCallback(pCtx, + "Failed to encrypt data with cipher: %s", + XCrypt_GetCipherStr(eCipher)); + + return NULL; + } + + return pCrypted; +} + +uint8_t* XDecrypt_Single(xcrypt_ctx_t *pCtx, xcrypt_chipher_t eCipher, const uint8_t *pInput, size_t *pLength) +{ + xcrypt_key_t decKey; + if (!XCrypt_KeyCallback(pCtx, eCipher, &decKey)) return XFALSE; + + const uint8_t *pKey = (const uint8_t*)decKey.sKey; + size_t nKeyLength = decKey.nLength; + uint8_t *pDecrypted = NULL; + + switch (eCipher) + { + case XC_HEX: pDecrypted = XDecrypt_HEX(pInput, pLength, XFALSE); break; + case XC_AES: pDecrypted = XDecrypt_AES(pInput, pLength, pKey, nKeyLength, NULL); break; + case XC_XOR: pDecrypted = XCrypt_XOR(pInput, *pLength, pKey, nKeyLength); break; + case XC_CASEAR: pDecrypted = (uint8_t*)XDecrypt_Casear((const char*)pInput, *pLength, atoi(decKey.sKey)); break; + case XC_BASE64: pDecrypted = (uint8_t*)XDecrypt_Base64(pInput, pLength); break; + case XC_REVERSE: pDecrypted = (uint8_t*)XCrypt_Reverse((const char*)pInput, *pLength); break; + default: break; + } + + if (pDecrypted == NULL) + { + XCrypt_ErrorCallback(pCtx, + "Failed to decrypt data with cipher: %s", + XCrypt_GetCipherStr(eCipher)); + + return NULL; + } + + return pDecrypted; +} + +uint8_t* XCrypt_Multy(xcrypt_ctx_t *pCtx, const uint8_t *pInput, size_t *pLength) +{ + uint8_t* (*XCrypt_Func)(xcrypt_ctx_t*, xcrypt_chipher_t, const uint8_t*, size_t*); + XCrypt_Func = pCtx->bDecrypt ? XDecrypt_Single : XCrypt_Single; + + xarray_t *pCiphersArr = xstrsplit(pCtx->pCiphers, ":"); + if (pCiphersArr == NULL) + { + pCiphersArr = XArray_New(XSTDNON, XFALSE); + if (pCiphersArr == NULL) + { + XCrypt_ErrorCallback(pCtx, "Can not allocate memory for cipher array"); + return NULL; + } + + XArray_AddData(pCiphersArr, pCtx->pCiphers, strlen(pCtx->pCiphers)); + if (!pCiphersArr->nUsed) + { + XCrypt_ErrorCallback(pCtx, "Can append cipher to the array"); + XArray_Destroy(pCiphersArr); + return NULL; + } + } + + uint8_t *pData = XByteData_Dup(pInput, *pLength); + if (pData == NULL) + { + XCrypt_ErrorCallback(pCtx, "Can allocate memory for input buffer"); + XArray_Destroy(pCiphersArr); + return NULL; + } + + size_t i, nUsed = XArray_Used(pCiphersArr); + uint8_t *pRet = NULL; + + for (i = 0; i < nUsed && pData != NULL; i++) + { + const char *pCipher = XArray_GetData(pCiphersArr, i); + if (pCipher == NULL) continue; + + xcrypt_chipher_t eCipher = XCrypt_GetCipher(pCipher); + if (eCipher == XC_INVALID) + { + XCrypt_ErrorCallback(pCtx, + "Invalid or unsupported cipher: %s", + XCrypt_GetCipherStr(eCipher)); + + XArray_Destroy(pCiphersArr); + free(pData); + return NULL; + } + + pRet = XCrypt_Func(pCtx, eCipher, pData, pLength); + free(pData); + pData = pRet; + } + + XArray_Destroy(pCiphersArr); + return pData; +} diff --git a/src/crypt.h b/src/crypt.h new file mode 100644 index 0000000..c094870 --- /dev/null +++ b/src/crypt.h @@ -0,0 +1,130 @@ +/*! + * @file libxutils/src/crypt.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the various crypt algorithms + */ + +#ifndef __XUTILS_CRYPT_H__ +#define __XUTILS_CRYPT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "xstr.h" +#include "xtype.h" + +#define XMD5_LENGTH 32 +#define XCHAR_MAP_SIZE 52 +#define XCRC32_MAX_SIZE 16 +#define XBASE64_TABLE_SIZE 256 +#define XSHA256_BLOCK_SIZE 64 +#define XSHA256_DIGEST_SIZE 32 +#define XSHA256_LENGTH 64 + +typedef union +{ + uint32_t hBytes[8]; + uint8_t digest[32]; +} xsha256_digest_t; + +typedef union +{ + uint32_t wBytes[16]; + uint8_t block[64]; +} xsha256_block_t; + +typedef struct XSHA256 +{ + xsha256_digest_t uDigest; + xsha256_block_t uBlock; + size_t nTotalSize; + size_t nSize; +} xsha256_t; + +void XSHA256_Init(xsha256_t *pSha); +void XSHA256_ProcessBlock(xsha256_t *pSha); +void XSHA256_Final(xsha256_t *pSha, uint8_t *pDigest); +void XSHA256_FinalRaw(xsha256_t *pSha, uint8_t *pDigest); +void XSHA256_Update(xsha256_t *pSha, const uint8_t *pData, size_t nLength); + +char* XCrypt_SHA256(const uint8_t *pInput, size_t nLength); +char* XCrypt_MD5(const uint8_t *pInput, size_t nLength); + +char* XCrypt_Reverse(const char *pInput, size_t nLength); +uint8_t* XCrypt_XOR(const uint8_t *pInput, size_t nLength, const uint8_t *pKey, size_t nKeyLen); + +uint32_t XCrypt_CRC32(const uint8_t *pInput, size_t nLength); +uint32_t XCrypt_CRC32B(const uint8_t *pInput, size_t nLength); + +char* XCrypt_Casear(const char *pInput, size_t nLength, size_t nKey); +char *XDecrypt_Casear(const char *pInput, size_t nLength, size_t nKey); + +char* XCrypt_Base64(const uint8_t *pInput, size_t *pLength); +char* XDecrypt_Base64(const uint8_t *pInput, size_t *pLength); + +uint8_t* XCrypt_AES(const uint8_t *pInput, size_t *pLength, const uint8_t *pKey, size_t nKeyLen, const uint8_t *pIV); +uint8_t* XDecrypt_AES(const uint8_t *pInput, size_t *pLength, const uint8_t *pKey, size_t nKeyLen, const uint8_t *pIV); + +uint8_t* XCrypt_HEX(const uint8_t *pInput, size_t *pLength, const char* pSpace, size_t nColumns, xbool_t bLowCase); +uint8_t* XDecrypt_HEX(const uint8_t *pInput, size_t *pLength, xbool_t bLowCase); + +typedef enum +{ + XC_AES = (uint8_t)0, + XC_HEX, + XC_XOR, + XC_MD5, + XC_CRC32, + XC_CRC32B, + XC_CASEAR, + XC_BASE64, + XC_SHA256, + XC_REVERSE, + XC_MULTY, + XC_INVALID +} xcrypt_chipher_t; + +typedef enum +{ + XCB_INVALID = (uint8_t)0, + XCB_ERROR, + XCB_KEY +} xcrypt_cb_type_t; + +typedef struct XCryptKey +{ + xcrypt_chipher_t eCipher; + char sKey[XSTR_MIN]; + size_t nLength; +} xcrypt_key_t; + +typedef xbool_t(*xcrypt_cb_t)(xcrypt_cb_type_t, void*, void*); + +typedef struct XCryptCtx +{ + xcrypt_cb_t callback; + void *pUserPtr; + + xbool_t bDecrypt; + size_t nColumns; + char *pCiphers; +} xcrypt_ctx_t; + +xcrypt_chipher_t XCrypt_GetCipher(const char *pCipher); +const char* XCrypt_GetCipherStr(xcrypt_chipher_t eCipher); + +void XCrypt_Init(xcrypt_ctx_t *pCtx, xbool_t bDecrypt, char *pCiphers, xcrypt_cb_t callback, void *pUser); +uint8_t* XDecrypt_Single(xcrypt_ctx_t *pCtx, xcrypt_chipher_t eCipher, const uint8_t *pInput, size_t *pLength); +uint8_t* XCrypt_Single(xcrypt_ctx_t *pCtx, xcrypt_chipher_t eCipher, const uint8_t *pInput, size_t *pLength); +uint8_t* XCrypt_Multy(xcrypt_ctx_t *pCtx, const uint8_t *pInput, size_t *pLength); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_CRYPT_H__ */ \ No newline at end of file diff --git a/src/errex.c b/src/errex.c new file mode 100644 index 0000000..2b52963 --- /dev/null +++ b/src/errex.c @@ -0,0 +1,116 @@ +/*! + * @file libxutils/src/errex.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Exit signal handling and backtrace. + */ + +#include "xstd.h" +#include "errex.h" +#ifdef __linux__ +#include +#endif + +#define BACKTRACE_SIZE 10 + +#ifdef __linux__ +void xutils_dbg_backtrace(void) +{ + void *pAddrs[BACKTRACE_SIZE]; + char **pSymbol; + int i = 0; + + int nSize = backtrace(pAddrs, sizeof(pAddrs) / sizeof(pAddrs[0])); + if (nSize <= 0) + { + printf("No backtrace available\n"); + return; + } + printf("Backtrace with %d functions\n", nSize); + + pSymbol = backtrace_symbols(pAddrs, nSize); + if (pSymbol == NULL) + { + printf("Can not get symbols for backtrace\n"); + return; + } + + for (i = 0; i < nSize; ++i) + printf("Function %d: %s\n", i, pSymbol[i]); + + if (pSymbol) free(pSymbol); +} +#endif + +void errex(const char * pMsg, ...) +{ + if (pMsg != NULL) + { + char sMsg[XMSG_MIN]; + va_list args; + + va_start(args, pMsg); + vsnprintf(sMsg, sizeof(sMsg), pMsg, args); + va_end(args); + + printf("<%s:%d> %s: %s\n", + __FILE__, __LINE__, + __FUNCTION__, sMsg); + } + + exit(EXIT_FAILURE); +} + +void XSig_Callback(int nSig) +{ + /* Handle signals */ + switch(nSig) + { + case SIGSEGV: + case SIGILL: +#ifdef __linux__ + case SIGBUS: + xutils_dbg_backtrace(); +#endif + break; + case SIGINT: + case SIGTERM: + printf("Received interrupt/termination signal\n"); + break; + default: + break; + } + + exit(EXIT_FAILURE); +} + + +int XSig_Register(int *pSignals, size_t nCount, xsig_cb_t callback) +{ + struct sigaction sact; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; + sact.sa_handler = callback; + + size_t i; + for (i = 0; i < nCount; i++) + if (sigaction(pSignals[i], &sact, NULL)) + return pSignals[i]; + + return 0; +} + +int XSig_ExitSignals(void) +{ +#ifndef WIN32 + int nSignals[5]; + nSignals[0] = SIGINT; + nSignals[1] = SIGILL; + nSignals[2] = SIGBUS; + nSignals[3] = SIGSEGV; + nSignals[4] = SIGTERM; + return XSig_Register(nSignals, 5, XSig_Callback); +#endif +} diff --git a/src/errex.h b/src/errex.h new file mode 100644 index 0000000..5d1113b --- /dev/null +++ b/src/errex.h @@ -0,0 +1,30 @@ +/*! + * @file libxutils/src/errex.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Exit signal handling and backtrace. + */ + + +#ifndef __XUTILS_ERREX_H__ +#define __XUTILS_ERREX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void(*xsig_cb_t)(int); + +int XSig_Register(int *pSignals, size_t nCount, xsig_cb_t callback); +int XSig_ExitSignals(void); + +void xutils_dbg_backtrace(void); +void errex(const char * errmsg, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_ERREX_H__ */ \ No newline at end of file diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..0462e88 --- /dev/null +++ b/src/event.c @@ -0,0 +1,419 @@ +/*! + * @file libxutils/src/event.c + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the cross-platform + * async event engine based on POLL and EPOLL. + */ + +#include "xstd.h" +#include "sock.h" +#include "event.h" + +#define XEVENTS_DEFAULT_FD_MAX 1024 + +const char *XEvents_Status(xevent_status_t status) +{ + switch(status) + { + case XEVENT_STATUS_EMAX: + return "Maximum number of file descriptors reached"; + case XEVENT_STATUS_ECTL: + return "Failed to call poll()/epoll_ctl()"; + case XEVENT_STATUS_EWAIT: + return "Failed to call poll()/epoll_wait()"; + case XEVENT_STATUS_ENOCB: + return "Servise callback is not set up"; + case XEVENT_STATUS_EOMAX: + return "Unable to detect max file descriptors"; + case XEVENT_STATUS_EALLOC: + return "Can not allocate memory for event array"; + case XEVENT_STATUS_EINTR: + return "Event service loop interrupted by signal"; + case XEVENT_STATUS_BREAK: + return "Requested break from main service loop"; + case XEVENT_STATUS_ECREATE: + return "Can not create event instance"; + case XEVENT_STATUS_EINSERT: + return "Can not insert data to hash map"; + case XEVENT_STATUS_EINVALID: + return "Invalid argument"; + case XEVENT_STATUS_SUCCESS: + return "Success"; + case XEVENT_STATUS_NONE: + default: break; + } + + return "Undefined error"; +} + +static int XEvents_EventCb(xevents_t *pEv, xevent_data_t *pData, XSOCKET nFD, int nReason) +{ + int nRetVal = pEv->eventCallback(pEv, pData, nFD, nReason); + if (nRetVal == XEVENTS_DISCONNECT) { XEvents_Delete(pEv, pData); return XEVENTS_DISCONNECT; } + else if (nRetVal == XEVENTS_USERCB) return pEv->eventCallback(pEv, pData, nFD, XEVENT_USER); + else if (nRetVal == XEVENTS_ACCEPT) return XEVENTS_ACTION; + else if (nRetVal != XEVENTS_CONTINUE) return nRetVal; + return XEVENTS_CONTINUE; +} + +static int XEvents_ServiceCb(xevents_t *pEvents, xevent_data_t *pData, XSOCKET nFD, uint32_t nEvents) +{ + if (pData != NULL) pData->nEvents = nEvents; + int nRetVal = 0; + + /* Check error condition */ +#ifdef XPOLLRDHUP + if (nEvents & XPOLLRDHUP) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_CLOSED); + return nRetVal != XEVENTS_CONTINUE ? nRetVal : XEVENTS_ACTION; + } +#endif + + if (nEvents & XPOLLHUP) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_HUNGED); + return nRetVal != XEVENTS_CONTINUE ? nRetVal : XEVENTS_ACTION; + } + + if (nEvents & XPOLLERR) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_ERROR); + return nRetVal != XEVENTS_CONTINUE ? nRetVal : XEVENTS_ACTION; + } + + if (nEvents & XPOLLPRI) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_EXCEPTION); + return nRetVal != XEVENTS_CONTINUE ? nRetVal : XEVENTS_ACTION; + } + + if (nEvents & XPOLLOUT) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_WRITE); + if (nRetVal != XEVENTS_CONTINUE) return nRetVal; + } + + if (nEvents & XPOLLIN) + { + nRetVal = XEvents_EventCb(pEvents, pData, nFD, XEVENT_READ); + if (nRetVal != XEVENTS_CONTINUE) return nRetVal; + } + + return XEVENTS_CONTINUE; +} + +static int XEvents_InterruptCb(void *pCtx) +{ + xevents_t *pEvents = (xevents_t*)pCtx; + int nRetVal = pEvents->eventCallback(pEvents, NULL, XSOCK_INVALID, XEVENT_INTERRUPT); + return (nRetVal == XEVENTS_CONTINUE) ? XEVENT_STATUS_SUCCESS : XEVENT_STATUS_EINTR; +} + +static void XEvents_ClearCb(void *pCtx, void *pData, int nKey) +{ + xevent_data_t *pEvData = (xevent_data_t*)pData; + xevents_t *pEvents = (xevents_t*)pCtx; + XSOCKET nFD = (XSOCKET)nKey; + + if (pEvData != NULL) + { + if (pEvents != NULL) + pEvents->eventCallback(pEvents, pEvData, nFD, XEVENT_CLEAR); + + free(pEvData); + } +} + +static void XEvents_DestroyMap(xevents_t *pEvents) +{ + if (pEvents->bUseHash) + { + pEvents->hashMap.clearCb = XEvents_ClearCb; + pEvents->hashMap.pUserContext = pEvents; + XHash_Destroy(&pEvents->hashMap); + pEvents->bUseHash = XFALSE; + } +} + +xevent_status_t XEvents_Create(xevents_t *pEvents, uint32_t nMax, void *pUser, xevent_cb_t callBack, xbool_t bUseHash) +{ + if (callBack == NULL) return XEVENT_STATUS_ENOCB; +#ifdef _WIN32 + uint32_t nSysMax = XEVENTS_DEFAULT_FD_MAX; +#else + uint32_t nSysMax = sysconf(_SC_OPEN_MAX); +#endif + + if (nSysMax && nMax) pEvents->nEventMax = nMax > nSysMax ? nSysMax : nMax; + else if (nSysMax) pEvents->nEventMax = nSysMax; + else if (nMax) pEvents->nEventMax = nMax; + else return XEVENT_STATUS_EOMAX; + + pEvents->eventCallback = callBack; + pEvents->pUserSpace = pUser; + pEvents->bUseHash = bUseHash; + + if (pEvents->bUseHash && XHash_Init(&pEvents->hashMap, XEvents_ClearCb, pEvents) < 0) + { + pEvents->bUseHash = XFALSE; + return XEVENT_STATUS_EALLOC; + } + +#ifdef __linux__ + pEvents->pEventArray = calloc(pEvents->nEventMax, sizeof(struct epoll_event)); + if (pEvents->pEventArray == NULL) + { + XEvents_DestroyMap(pEvents); + return XEVENT_STATUS_EALLOC; + } + + pEvents->nEventFd = epoll_create1(0); + if (pEvents->nEventFd < 0) + { + XEvents_DestroyMap(pEvents); + free(pEvents->pEventArray); + pEvents->pEventArray = NULL; + return XEVENT_STATUS_ECREATE; + } +#else + pEvents->pEventArray = calloc(pEvents->nEventMax, sizeof(struct pollfd)); + if (pEvents->pEventArray == NULL) + { + XEvents_DestroyMap(pEvents); + return XEVENT_STATUS_EALLOC; + } + + pEvents->nEventCount = 0; + uint32_t i = 0; + + for (i = 0; i < pEvents->nEventMax; i++) + { + pEvents->pEventArray[i].revents = 0; + pEvents->pEventArray[i].events = 0; + pEvents->pEventArray[i].fd = XSOCK_INVALID; + } +#endif + + return XEVENT_STATUS_SUCCESS; +} + +void XEvents_Destroy(xevents_t *pEvents) +{ + if (pEvents->pEventArray) + { + free(pEvents->pEventArray); + pEvents->pEventArray = NULL; + } + +#ifdef __linux__ + if (pEvents->nEventFd >= 0) + { + close(pEvents->nEventFd); + pEvents->nEventFd = 0; + } +#endif + + XEvents_DestroyMap(pEvents); + pEvents->eventCallback(pEvents, NULL, XSOCK_INVALID, XEVENT_DESTROY); +} + +xevent_data_t *XEvents_NewData(void *pCtx, XSOCKET nFd, int nType) +{ + xevent_data_t* pData = (xevent_data_t*)malloc(sizeof(xevent_data_t)); + if (pData == NULL) return NULL; + + pData->pContext = pCtx; + pData->bIsOpen = XTRUE; + pData->nEvents = 0; + pData->nIndex = -1; + pData->nType = nType; + pData->nFD = nFd; + return pData; +} + +xevent_status_t XEvents_Add(xevents_t *pEv, xevent_data_t* pData, int nEvents) +{ + if (pData == NULL || pData->nFD == XSOCK_INVALID) return XEVENT_STATUS_EINVALID; + +#ifdef __linux__ + struct epoll_event event; + event.data.ptr = pData; + event.events = nEvents; + if (epoll_ctl(pEv->nEventFd, EPOLL_CTL_ADD, pData->nFD, &event) < 0) return XEVENT_STATUS_ECTL; +#else + if (pEv->nEventCount >= pEv->nEventMax) return XEVENT_STATUS_ECTL; + pData->nIndex = pEv->nEventCount++; + pEv->pEventArray[pData->nIndex].revents = 0; + pEv->pEventArray[pData->nIndex].events = (short)nEvents; + pEv->pEventArray[pData->nIndex].fd = pData->nFD; +#endif + + if (pEv->bUseHash && XHash_Insert(&pEv->hashMap, pData, 0, (int)pData->nFD) < 0) + { +#ifdef __linux__ + epoll_ctl(pEv->nEventFd, EPOLL_CTL_DEL, pData->nFD, NULL); +#else + pEv->pEventArray[pData->nIndex].revents = 0; + pEv->pEventArray[pData->nIndex].events = 0; + pEv->pEventArray[pData->nIndex].fd = XSOCK_INVALID; + pEv->nEventCount--; + pData->nIndex = -1; +#endif + return XEVENT_STATUS_EINSERT; + } + + return XEVENT_STATUS_SUCCESS; +} + +xevent_data_t* XEvents_RegisterEvent(xevents_t *pEv, void *pCtx, XSOCKET nFd, int nEvents, int nType) +{ + xevent_data_t* pData = XEvents_NewData(pCtx, nFd, nType); + if (pData == NULL) return NULL; + + if (XEvents_Add(pEv, pData, nEvents) != XEVENT_STATUS_SUCCESS) + { + free(pData); + return NULL; + } + + return pData; +} + +int XEvent_Write(xevent_data_t *pData) +{ + const char nVal = 1; + return send(pData->nFD, &nVal, sizeof(nVal), XMSG_NOSIGNAL); +} + +int XEvent_Read(xevent_data_t *pData) +{ + char nVal = 0; + return recv(pData->nFD, &nVal, sizeof(nVal), XMSG_NOSIGNAL); +} + +#ifdef __linux__ +xevent_data_t* XEvents_CreateEvent(xevents_t *pEv, void *pCtx) +{ + int nEventFD = eventfd(0, EFD_NONBLOCK); + if (nEventFD < 0) return NULL; + + xevent_data_t* pData = XEvents_NewData(pCtx, nEventFD, XEVENT_TYPE_USER); + if (pData == NULL) + { + close(nEventFD); + return NULL; + } + + int nStatus = XEvents_Add(pEv, pData, XPOLLIN); + if (nStatus != XEVENT_STATUS_SUCCESS) + { + close(nEventFD); + free(pData); + return NULL; + } + + return pData; +} +#endif + +xevent_status_t XEvents_Modify(xevents_t *pEv, xevent_data_t *pData, int nEvents) +{ + if (pData == NULL) return XEVENT_STATUS_EINVALID; +#ifdef __linux__ + struct epoll_event event; + event.data.ptr = pData; + event.events = nEvents; + if (epoll_ctl(pEv->nEventFd, EPOLL_CTL_MOD, pData->nFD, &event) < 0) return XEVENT_STATUS_ECTL; +#else + if (pData->nIndex < 0 && (uint32_t)pData->nIndex >= pEv->nEventCount) return XEVENT_STATUS_ECTL; + pEv->pEventArray[pData->nIndex].events = (short)nEvents; +#endif + return XEVENT_STATUS_SUCCESS; +} + +xevent_status_t XEvents_Delete(xevents_t *pEv, xevent_data_t *pData) +{ + if (pData == NULL) return XEVENT_STATUS_SUCCESS; + int nStatus = -1; + +#ifdef __linux__ + if (pData->nFD >= 0) nStatus = epoll_ctl(pEv->nEventFd, EPOLL_CTL_DEL, pData->nFD, NULL); +#else + if (pData->nIndex >= 0 && (uint32_t)pData->nIndex < pEv->nEventCount) + { + pEv->pEventArray[pData->nIndex].revents = 0; + pEv->pEventArray[pData->nIndex].events = 0; + pEv->pEventArray[pData->nIndex].fd = XSOCK_INVALID; + int i = nStatus = 0; + + for (i = pData->nIndex; (uint32_t)i < pEv->nEventCount; i++) + pEv->pEventArray[i] = pEv->pEventArray[i + 1]; + + pEv->nEventCount--; + pData->nIndex = -1; + } +#endif + + if (!pEv->bUseHash || pData->nFD == XSOCK_INVALID || + XHash_Delete(&pEv->hashMap, (int)pData->nFD) < 0) + XEvents_ClearCb(pEv, pData, (int)pData->nFD); + + return (nStatus < 0) ? XEVENT_STATUS_ECTL : XEVENT_STATUS_SUCCESS; +} + +xevent_data_t* XEvents_GetData(xevents_t *pEv, XSOCKET nFD) +{ + if (!pEv->bUseHash || nFD == XSOCK_INVALID) return NULL; + return (xevent_data_t*)XHash_GetData(&pEv->hashMap, (int)nFD); +} + +xevent_status_t XEvents_Service(xevents_t *pEv, int nTimeoutMs) +{ + int nCount, nRet = 0; + uint32_t i = 0; + +#ifdef __linux__ + nCount = epoll_wait(pEv->nEventFd, pEv->pEventArray, pEv->nEventMax, nTimeoutMs); +#elif _WIN32 + nCount = WSAPoll(pEv->pEventArray, pEv->nEventCount, nTimeoutMs); +#else + nCount = poll(pEv->pEventArray, pEv->nEventCount, nTimeoutMs); +#endif + + if (errno == EINTR) return XEvents_InterruptCb(pEv); + else if (nCount < 0) return XEVENT_STATUS_EWAIT; + else if (!nCount) return XEVENT_STATUS_SUCCESS; + +#ifdef __linux__ + for (i = 0; i < nCount; i++) + { + if (pEv->pEventArray[i].data.ptr == NULL) continue; + xevent_data_t *pData = (xevent_data_t*)pEv->pEventArray[i].data.ptr; + uint32_t nEvents = pEv->pEventArray[i].events; + nRet = XEvents_ServiceCb(pEv, pData, pData->nFD, nEvents); + if (nRet) break; + } +#else + for (i = 0; i < pEv->nEventCount; i++) + { + if (pEv->pEventArray[i].revents <= 0) continue; + else if (pEv->pEventArray[i].fd == XSOCK_INVALID) break; + + int nEvents = pEv->pEventArray[i].revents; + XSOCKET nFD = pEv->pEventArray[i].fd; + pEv->pEventArray[i].revents = 0; + + xevent_data_t *pData = XEvents_GetData(pEv, nFD); + nRet = XEvents_ServiceCb(pEv, pData, nFD, nEvents); + if (nRet) break; + } +#endif + + return nRet == XEVENTS_BREAK ? + XEVENT_STATUS_BREAK : + XEVENT_STATUS_SUCCESS; +} diff --git a/src/event.h b/src/event.h new file mode 100644 index 0000000..25df426 --- /dev/null +++ b/src/event.h @@ -0,0 +1,149 @@ +/*! + * @file libxutils/src/event.h + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the cross-platform + * async event engine based on POLL and EPOLL. + */ + +#ifndef __XUTILS_EVENT_H__ +#define __XUTILS_EVENT_H__ + +#define XEVENT_TYPE_USER 0 +#define XEVENT_TYPE_CLIENT 1 +#define XEVENT_TYPE_LISTENER 2 + +#define XEVENT_ERROR 3 +#define XEVENT_READ 4 +#define XEVENT_WRITE 5 +#define XEVENT_HUNGED 6 +#define XEVENT_CLOSED 7 +#define XEVENT_CLEAR 8 +#define XEVENT_DESTROY 9 +#define XEVENT_INTERRUPT 10 +#define XEVENT_EXCEPTION 11 +#define XEVENT_USER 12 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _XUTILS_USE_GNU +#define _GNU_SOURCE +#endif + +#include "xstd.h" +#include "xtype.h" +#include "hash.h" + +#ifdef __linux__ +#include +#include +#elif !defined(_WIN32) +#include +#endif + +#ifdef __linux__ +#define XPOLLRDHUP EPOLLRDHUP +#define XPOLLHUP EPOLLHUP +#define XPOLLERR EPOLLERR +#define XPOLLPRI EPOLLPRI +#define XPOLLOUT EPOLLOUT +#define XPOLLIN EPOLLIN +#else +#ifdef POLLRDHUP +#define XPOLLRDHUP POLLRDHUP +#endif +#define XPOLLHUP POLLHUP +#define XPOLLERR POLLERR +#define XPOLLPRI POLLPRI +#define XPOLLOUT POLLOUT +#define XPOLLIN POLLIN +#endif + +#define XEVENTS_DISCONNECT -1 +#define XEVENTS_CONTINUE 0 +#define XEVENTS_RELOOP 1 +#define XEVENTS_ACCEPT 2 +#define XEVENTS_USERCB 3 +#define XEVENTS_BREAK 4 + +#ifdef __linux__ +#define XEVENTS_ACTION XEVENTS_CONTINUE +#else +#define XEVENTS_ACTION XEVENTS_RELOOP +#endif + +typedef enum { + XEVENT_STATUS_NONE = 0, + XEVENT_STATUS_ECTL, + XEVENT_STATUS_EMAX, + XEVENT_STATUS_ENOCB, + XEVENT_STATUS_EOMAX, + XEVENT_STATUS_EWAIT, + XEVENT_STATUS_EINTR, + XEVENT_STATUS_EALLOC, + XEVENT_STATUS_ECREATE, + XEVENT_STATUS_EINSERT, + XEVENT_STATUS_EINVALID, + XEVENT_STATUS_SUCCESS, + XEVENT_STATUS_BREAK +} xevent_status_t; + +typedef struct XEventData { + void *pContext; + uint32_t nEvents; + xbool_t bIsOpen; + XSOCKET nFD; + int nIndex; + int nType; +} xevent_data_t; + +typedef int(*xevent_cb_t)(void *events, void* data, XSOCKET fd, int reason); + +typedef struct XEvents { +#ifdef __linux__ + struct epoll_event* pEventArray; /* EPOLL event array */ + int nEventFd; /* EPOLL file decriptor */ +#else + struct pollfd* pEventArray; /* POLL event array */ + uint32_t nEventCount; /* Number of event fds in array */ +#endif + + xevent_cb_t eventCallback; /* Service callback */ + void* pUserSpace; /* User space pointer */ + uint32_t nEventMax; /* Max allowed file descriptors */ + + xbool_t bUseHash; /* Flag to enable/disable hash map usage*/ + xhash_t hashMap; /* Hash map for events and related data */ + +} xevents_t; + +const char *XEvents_Status(xevent_status_t status); + +xevent_status_t XEvents_Create(xevents_t *pEv, uint32_t nMax, void *pUser, xevent_cb_t callBack, xbool_t bUseHash); +void XEvents_Destroy(xevents_t *pEvents); + +int XEvent_Write(xevent_data_t *pData); +int XEvent_Read(xevent_data_t *pData); + +xevent_data_t *XEvents_NewData(void *pCtx, XSOCKET nFd, int nType); +xevent_data_t* XEvents_GetData(xevents_t *pEv, XSOCKET nFd); + +#ifdef __linux__ +xevent_data_t* XEvents_CreateEvent(xevents_t *pEv, void *pCtx); +#endif +xevent_data_t* XEvents_RegisterEvent(xevents_t *pEv, void *pCtx, XSOCKET nFd, int nEvents, int nType); + +xevent_status_t XEvents_Add(xevents_t *pEv, xevent_data_t *pData, int nEvents); +xevent_status_t XEvents_Modify(xevents_t *pEv, xevent_data_t *pData, int nEvents); +xevent_status_t XEvents_Delete(xevents_t *pEv, xevent_data_t *pData); +xevent_status_t XEvents_Service(xevents_t *pEv, int nTimeoutMs); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_EVENT_H__ */ \ No newline at end of file diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..54bfdaa --- /dev/null +++ b/src/hash.c @@ -0,0 +1,155 @@ +/*! + * @file libxutils/src/hash.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of hash tables + */ + +#include "xstd.h" +#include "hash.h" +#include "list.h" + +typedef struct XHashIterator { + xhash_itfunc_t itfunc; + void *pUserCtx; +} xhash_iterator_t; + +static int XHash_IteratorCb(void *pCtx, xlist_t *pNode) +{ + xhash_iterator_t *pIter = (xhash_iterator_t*)pCtx; + if (pIter != NULL && pIter->itfunc != NULL) return XSTDERR; + xhash_pair_t *pPair = (xhash_pair_t*)pNode->pData; + return pPair != NULL ? pIter->itfunc(pPair, pIter->pUserCtx) : 0; +} + +static int XHash_SearchCb(void *pKey, xlist_t *pNode) +{ + xhash_pair_t *pPair = (xhash_pair_t*)pNode->pData; + return (pPair != NULL && *(int*)pKey == pPair->nKey) ? XTRUE : XFALSE; +} + +static void XHash_ClearCb(void *pCbCtx, void *pData) +{ + xhash_pair_t* pPair = (xhash_pair_t*)pData; + xhash_t *pHash = (xhash_t*)pCbCtx; + + if (pHash == NULL || + pPair == NULL || + pPair->pData == NULL) return; + + if (pHash->clearCb != NULL) + pHash->clearCb(pHash->pUserContext, pPair->pData, pPair->nKey); + + free(pPair); +} + +xhash_pair_t* XHash_NewPair(void *pData, size_t nSize, int nKey) +{ + xhash_pair_t* pPair = (xhash_pair_t*)malloc(sizeof(xhash_pair_t)); + if (pPair == NULL) return NULL; + + pPair->pData = pData; + pPair->nSize = nSize; + pPair->nKey = nKey; + return pPair; +} + +int XHash_Init(xhash_t *pHash, xhash_clearcb_t clearCb, void *pCtx) +{ + pHash->pUserContext = pCtx; + pHash->clearCb = clearCb; + pHash->nPairCount = 0; + unsigned int i; + + for (i = 0; i < XHASH_MODULES; i++) + { + xlist_t *pTable = (xlist_t*)&pHash->tables[i]; + XList_Init(pTable, NULL, 0, XHash_ClearCb, pHash); + } + + return XSTDOK; +} + +void XHash_Destroy(xhash_t *pHash) +{ + unsigned int i; + for (i = 0; i < XHASH_MODULES; i++) + XList_Clear(&pHash->tables[i]); + + pHash->pUserContext = NULL; + pHash->clearCb = NULL; + pHash->nPairCount = 0; +} + +xlist_t* XHash_GetNode(xhash_t *pHash, int nKey) +{ + uint32_t nHash = XHASH_MIX(nKey, XHASH_MODULES); + xlist_t *pTable = (xlist_t*)&pHash->tables[nHash]; + return XList_Search(pTable, (void*)&nKey, XHash_SearchCb); +} + +xhash_pair_t* XHash_GetPair(xhash_t *pHash, int nKey) +{ + xlist_t *pNode = XHash_GetNode(pHash, nKey); + return pNode != NULL ? (xhash_pair_t*)pNode->pData : NULL; +} + +void* XHash_GetData(xhash_t *pHash, int nKey) +{ + xhash_pair_t *pPair = XHash_GetPair(pHash, nKey); + return pPair != NULL ? pPair->pData : NULL; +} + +int XHash_GetSize(xhash_t *pHash, int nKey) +{ + xhash_pair_t *pPair = XHash_GetPair(pHash, nKey); + return pPair != NULL ? (int)pPair->nSize : XSTDERR; +} + +int XHash_InsertPair(xhash_t *pHash, xhash_pair_t *pData) +{ + uint32_t nHash = XHASH_MIX(pData->nKey, XHASH_MODULES); + xlist_t *pTable = (xlist_t*)&pHash->tables[nHash]; + + if (XList_PushNext(pTable, pData, 0) != NULL) + { + pHash->nPairCount++; + return XSTDOK; + } + + return XSTDERR; +} + +int XHash_Insert(xhash_t *pHash, void *pData, size_t nSize, int nKey) +{ + xhash_pair_t *pPair = XHash_NewPair(pData, nSize, nKey); + if (pPair == NULL) return XSTDERR; + int nStatus = XHash_InsertPair(pHash, pPair); + if (nStatus < 0) free(pPair); + return nStatus; +} + +int XHash_Delete(xhash_t *pHash, int nKey) +{ + xlist_t *pNode = XHash_GetNode(pHash, nKey); + if (pNode == NULL) return XSTDERR; + XList_Unlink(pNode); + pHash->nPairCount--; + return XSTDOK; +} + +void XHash_Iterate(xhash_t *pHash, xhash_itfunc_t itfunc, void *pCtx) +{ + xhash_iterator_t iter; + iter.pUserCtx = pCtx; + iter.itfunc = itfunc; + unsigned int i; + + for (i = 0; i < XHASH_MODULES; i++) + { + xlist_t *pTable = (xlist_t*)&pHash->tables[i]; + if (XList_Search(pTable, &iter, XHash_IteratorCb) == NULL) break; + } +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..8d31533 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,63 @@ +/*! + * @file libxutils/src/hash.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of hash tables + */ + +#ifndef __XUTILS_HASH_H__ +#define __XUTILS_HASH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XHASH_MIX(num, range) ((num ^ (num >> 8) ^ (num >> 16)) % range) + +#include "xstd.h" +#include "list.h" +#include "sock.h" + +#ifdef _XUTILS_HASH_MODULES +#define XHASH_MODULES _XUTILS_HASH_MODULES +#else +#define XHASH_MODULES 32 +#endif + +typedef struct XHashData { + void* pData; + size_t nSize; + int nKey; +} xhash_pair_t; + +typedef void(*xhash_clearcb_t)(void *, void*, int); +typedef int(*xhash_itfunc_t)(xhash_pair_t*, void*); + +typedef struct XHash { + xhash_clearcb_t clearCb; + void *pUserContext; + size_t nPairCount; + xlist_t tables[XHASH_MODULES]; +} xhash_t; + +int XHash_Init(xhash_t *pHash, xhash_clearcb_t clearCb, void *pCtx); +void XHash_Iterate(xhash_t *pHash, xhash_itfunc_t itfunc, void *pCtx); +void XHash_Destroy(xhash_t *pHash); + +xhash_pair_t* XHash_GetPair(xhash_t *pHash, int nKey); +xlist_t* XHash_GetNode(xhash_t *pHash, int nKey); +void* XHash_GetData(xhash_t *pHash, int nKey); +int XHash_GetSize(xhash_t *pHash, int nKey); + +xhash_pair_t* XHash_NewPair(void* pData, size_t nSize, int nKey); +int XHash_InsertPair(xhash_t *pHash, xhash_pair_t *pData); +int XHash_Insert(xhash_t *pHash, void *pData, size_t nSize, int nKey); +int XHash_Delete(xhash_t *pHash, int nKey); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_HASH_H__ */ \ No newline at end of file diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..79f269b --- /dev/null +++ b/src/http.c @@ -0,0 +1,1081 @@ +/*! + * @file libxutils/src/http.c + * + * This source is part of "libxutils" project + * 2018-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes implementation of + * the HTTP request/response parser and assembler. + * + */ + +#include "xstd.h" +#include "xver.h" +#include "xstr.h" +#include "http.h" +#include "addr.h" +#include "crypt.h" + +typedef struct XHTTPCode { + const int nCode; + const char* pDesc; +} xhttp_code_t; + +static xhttp_code_t g_XTTPCodes[] = +{ + { 100, "Continue" }, + { 101, "Switching Protocol" }, + { 102, "Processing" }, + { 103, "Early Hints" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choice" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 306, "Unused" }, + { 307, "Temporary Redirect" }, + { 308, "Permanent Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Timeout" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Payload Too Large" }, + { 414, "URI Too Long" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested Range Not Satisfiable" }, + { 417, "Expectation Failed" }, + { 418, "I'm a teapot" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Timeout" }, + { 505, "HTTP Version Not Supported" }, + { 506, "Variant Also Negotiates" }, + { 507, "Insufficient Storage" }, + { 508, "Loop Detected" }, + { 510, "Not Extended" }, + { 511, "Network Authentication Required" }, + { -1, "Unknown "} +}; + +const char* XHTTP_GetStatusStr(xhttp_status_t eStatus) +{ + switch (eStatus) + { + case XHTTP_ERRINIT: + return "Failed to init HTTP request"; + case XHTTP_ERRASSEMBLE: + return "Failed to assemble HTTP request"; + case XHTTP_ERRCONNECT: + return "Failed to connect remote server"; + case XHTTP_ERRRESOLVE: + return "Failed to resolve remote address"; + case XHTTP_ERRAUTH: + return "Failed to setup auth basic header"; + case XHTTP_ERRLINK: + return "Invalid or unsupported address (link)"; + case XHTTP_ERRPROTO: + return "Invalid or unsupported protocol in link"; + case XHTTP_ERRWRITE: + return "Failed to send request to remote server"; + case XHTTP_ERRREAD: + return "Failed to read HTTP packet from the network"; + case XHTTP_ERRTIMEO: + return "Failed to set receive timeout on the socket"; + case XHTTP_ERRSETHDR: + return "Failed to append header field to the request"; + case XHTTP_ERREXISTS: + return "Header already exists in the HTTP header table"; + case XHTTP_ERRALLOC: + return "Failed to allocate memory for HTTP packet buffer"; + case XHTTP_ERRFDMODE: + return "Non-blocking file descriptor is not allowed for this operation"; + case XHTTP_BIGHDR: + return "HTTP header is not detected in the bytes of active limit"; + case XHTTP_BIGCNT: + return "HTTP Packet payload is greater than the active limit"; + case XHTTP_INCOMPLETE: + return "Data does not contain HTTP packet or it is incomplete"; + case XHTTP_TERMINATED: + return "Termination was requested from the HTTP callback"; + case XHTTP_COMPLETE: + return "Succesfully parsed HTTP packet header and body"; + case XHTTP_PARSED: + return "Succesfully parsed HTTP packet header"; + case XHTTP_INVALID: + return "Invalid or unsupported HTTP packet"; + default: + break; + } + + return "Unknown status"; +} + +const char* XHTTP_GetCodeStr(int nCode) +{ + int i; + for (i = 0;; i++) + { + if (g_XTTPCodes[i].nCode == -1 || + g_XTTPCodes[i].nCode == nCode) + return g_XTTPCodes[i].pDesc; + } + + return "Unknown"; +} + +const char *XHTTP_GetMethodStr(xhttp_method_t eMethod) +{ + switch(eMethod) + { + case XHTTP_PUT: return "PUT"; + case XHTTP_GET: return "GET"; + case XHTTP_POST: return "POST"; + case XHTTP_DELETE: return "DELETE"; + case XHTTP_OPTIONS: return "OPTIONS"; + case XHTTP_DUMMY: return "DUMMY"; + default: break; + } + + return "UNKNOWN"; +} + +xhttp_method_t XHTTP_GetMethodType(const char *pData) +{ + if (!strncmp(pData, "GET", 3)) return XHTTP_GET; + if (!strncmp(pData, "PUT", 3)) return XHTTP_PUT; + if (!strncmp(pData, "POST", 4)) return XHTTP_POST; + if (!strncmp(pData, "DELETE", 6)) return XHTTP_DELETE; + if (!strncmp(pData, "OPTIONS", 7)) return XHTTP_OPTIONS; + return XHTTP_DUMMY; +} + +xbool_t XHTTP_IsSuccessCode(xhttp_t *pHandle) +{ + return (pHandle->nStatusCode >= 200 && + pHandle->nStatusCode < 300) ? + XTRUE : XFALSE; +} + +static int XHTTP_Callback(xhttp_t *pHandle, xhttp_cbtype_t eType, const void *pData, size_t nLength) +{ + if (pData == NULL || !nLength) return XSTDERR; + else if (pHandle->callback == NULL) return XSTDUSR; + else if (!XHTTP_CHECK_FLAG(pHandle->nCbTypes, eType)) return XSTDUSR; + + xhttp_ctx_t cbCtx; + cbCtx.pData = pData; + cbCtx.nLength = nLength; + cbCtx.eCbType = eType; + cbCtx.eStatus = XHTTP_NONE; + return pHandle->callback(pHandle, &cbCtx); +} + +static xhttp_status_t XHTTP_StatusCb(xhttp_t *pHandle, xhttp_status_t eStatus) +{ + xhttp_cbtype_t eType = eStatus < XHTTP_TERMINATED ? XHTTP_ERROR : XHTTP_STATUS; + if (XHTTP_CHECK_FLAG(pHandle->nCbTypes, eType) && pHandle->callback != NULL) + { + char sBuffer[XHTTP_OPTION_MAX]; + const char *pStr = XHTTP_GetStatusStr(eStatus); + size_t nLength = xstrncpyf(sBuffer, sizeof(sBuffer), "%s", pStr); + + xhttp_ctx_t cbCtx; + cbCtx.eCbType = eType; + cbCtx.eStatus = eStatus; + cbCtx.nLength = nLength; + cbCtx.pData = sBuffer; + + if (pHandle->callback(pHandle, &cbCtx) < 0) + eStatus = XHTTP_TERMINATED; + } + + return eStatus; +} + +static void XHTTP_HeaderClearCb(xmap_pair_t *pPair) +{ + if (pPair != NULL) + { + if (pPair->pData != NULL) free(pPair->pData); + if (pPair->pKey != NULL) free(pPair->pKey); + } +} + +static int XHTTP_HeaderWriteCb(xmap_pair_t *pPair, void *pContext) +{ + xhttp_t *pHttp = (xhttp_t*)pContext; + const char *pHeader = (const char *)pPair->pKey; + const char *pValue = (const char *)pPair->pData; + + return XByteBuffer_AddFmt(&pHttp->dataRaw, "%s: %s\r\n", + pHeader, pValue) == XSTDERR ? XMAP_STOP : XMAP_OK; +} + +static int XHTTP_MapIt(xmap_pair_t *pPair, void *pContext) +{ + xmap_t *pDstMap = (xmap_t*)pContext; + int nStatus = XMap_PutPair(pDstMap, pPair); + if (nStatus != XMAP_OK) return XMAP_STOP; + return nStatus; +} + +int XHTTP_SetCallback(xhttp_t *pHttp, xhttp_cb_t callback, void *pCbCtx, uint16_t nCbTypes) +{ + if (pHttp == NULL) return XSTDERR; + pHttp->callback = callback; + pHttp->nCbTypes = nCbTypes; + pHttp->pUserCtx = pCbCtx; + return XSTDOK; +} + +int XHTTP_Init(xhttp_t *pHttp, xhttp_method_t eMethod, size_t nSize) +{ + pHttp->nHeaderLength = pHttp->nContentLength = 0; + pHttp->nStatusCode = pHttp->nHeaderCount = 0; + pHttp->nAllocated = pHttp->nComplete = 0; + pHttp->sVersion[0] = pHttp->sUrl[0] = '\0'; + + pHttp->callback = NULL; + pHttp->pUserCtx = NULL; + pHttp->nAllowUpdate = 0; + pHttp->nCbTypes = 0; + pHttp->nTimeout = 0; + + pHttp->nContentMax = XHTTP_PACKAGE_MAX; + pHttp->nHeaderMax = XHTTP_HEADER_MAX; + + pHttp->eMethod = eMethod; + pHttp->eType = XHTTP_INITIAL; + + XMap_Init(&pHttp->headerMap, 0); + pHttp->headerMap.clearCb = XHTTP_HeaderClearCb; + + xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + return XByteBuffer_Init(pBuffer, nSize, 0); +} + +int XHTTP_InitRequest(xhttp_t *pHttp, xhttp_method_t eMethod, const char *pUri, const char *pVer) +{ + int nStatus = XHTTP_Init(pHttp, eMethod, XHTTP_HEADER_SIZE); + if (nStatus <= 0) return XSTDERR; + + const char *pVersion = pVer != NULL ? pVer : XHTTP_VER_DEFAULT; + const char *pFixedUrl = pUri != NULL ? pUri : "\\"; + + xstrncpy(pHttp->sVersion, sizeof(pHttp->sVersion), pVersion); + xstrncpy(pHttp->sUrl, sizeof(pHttp->sUrl), pFixedUrl); + + pHttp->eType = XHTTP_REQUEST; + return nStatus; +} + +int XHTTP_InitResponse(xhttp_t *pHttp, uint16_t nStatusCode, const char *pVer) +{ + int nStatus = XHTTP_Init(pHttp, XHTTP_DUMMY, XHTTP_HEADER_SIZE); + if (nStatus <= 0) return XSTDERR; + + const char *pVersion = pVer != NULL ? pVer : XHTTP_VER_DEFAULT; + xstrncpy(pHttp->sVersion, sizeof(pHttp->sVersion), pVersion); + + pHttp->nStatusCode = nStatusCode; + pHttp->eType = XHTTP_RESPONSE; + return nStatus; +} + +int XHTTP_Recycle(xhttp_t *pHttp) +{ + xhttp_cb_t callback = pHttp->callback; + uint16_t nCbTypes = pHttp->nCbTypes; + void *pCbCtx = pHttp->pUserCtx; + char sVersion[XHTTP_FIELD_MAX]; + + xstrncpy(sVersion, sizeof(sVersion), pHttp->sVersion); + xbool_t nAllocated = pHttp->nAllocated; + size_t nContentMax = pHttp->nContentMax; + size_t nHeaderMax = pHttp->nHeaderMax; + int nAllowUpdate = pHttp->nAllowUpdate; + int nTimeOut = pHttp->nTimeout; + pHttp->nAllocated = XFALSE; + + XHTTP_Clear(pHttp); // Recycle the HTTP handle + int nStatus = XHTTP_Init(pHttp, XHTTP_DUMMY, XSTDNON); + XHTTP_SetCallback(pHttp, callback, pCbCtx, nCbTypes); + xstrncpy(pHttp->sVersion, sizeof(sVersion), sVersion); + + pHttp->nAllowUpdate = nAllowUpdate; + pHttp->nContentMax = nContentMax; + pHttp->nHeaderMax = nHeaderMax; + pHttp->nAllocated = nAllocated; + pHttp->nTimeout = nTimeOut; + return nStatus; +} + +xhttp_t *XHTTP_Alloc(xhttp_method_t eMethod, size_t nDataSize) +{ + xhttp_t *pHeader = (xhttp_t*)malloc(sizeof(xhttp_t)); + if (pHeader == NULL) return NULL; + + XHTTP_Init(pHeader, eMethod, nDataSize); + xbyte_buffer_t *pBuffer = &pHeader->dataRaw; + + if (pBuffer->nStatus >= 0) + { + pHeader->nAllocated = XTRUE; + return pHeader; + } + + free(pHeader); + return NULL; +} + +int XHTTP_Copy(xhttp_t *pDst, xhttp_t *pSrc) +{ + if (pDst == NULL || pSrc == NULL) return XSTDERR; + XHTTP_Init(pDst, pSrc->eMethod, XSTDNON); + + xbyte_buffer_t *pSrcBuff = &pSrc->dataRaw; + xbyte_buffer_t *pDstBuff = &pDst->dataRaw; + + if (XByteBuffer_Add(pDstBuff, pSrcBuff->pData, pSrcBuff->nUsed) < 0) return XSTDERR; + xstrncpy(pDst->sVersion, sizeof(pDst->sVersion), pSrc->sVersion); + xstrncpy(pDst->sUrl, sizeof(pDst->sUrl), pSrc->sUrl); + + xmap_t *pSrcMap = &pSrc->headerMap; + xmap_t *pDstMap = &pDst->headerMap; + pDstMap->clearCb = XHTTP_HeaderClearCb; + + if (XMap_Iterate(pSrcMap, XHTTP_MapIt, pDstMap) != XMAP_OK) + { + XHTTP_Clear(pDst); + return XSTDERR; + } + + pDst->nHeaderCount = (uint16_t)pDstMap->nUsed; + pDst->nContentLength = pSrc->nContentLength; + pDst->nHeaderLength = pSrc->nHeaderLength; + + pDst->pUserCtx = pSrc->pUserCtx; + pDst->callback = pSrc->callback; + pDst->nCbTypes = pSrc->nCbTypes; + + pDst->nAllowUpdate = pSrc->nAllowUpdate; + pDst->nContentMax = pSrc->nContentMax; + pDst->nHeaderMax = pSrc->nHeaderMax; + pDst->nStatusCode = pSrc->nStatusCode; + pDst->nComplete = pSrc->nComplete; + pDst->nTimeout = pSrc->nTimeout; + pDst->eType = pSrc->eType; + + return XSTDOK; +} + +void XHTTP_Clear(xhttp_t *pHttp) +{ + pHttp->headerMap.clearCb = XHTTP_HeaderClearCb; + pHttp->nComplete = 0; + + XMap_Destroy(&pHttp->headerMap); + XByteBuffer_Clear(&pHttp->dataRaw); + if (pHttp->nAllocated) free(pHttp); +} + +int XHTTP_AddHeader(xhttp_t *pHttp, const char *pHeader, const char *pStr, ...) +{ + char sOption[XHTTP_OPTION_MAX]; + size_t nLength = 0; + + va_list args; + va_start(args, pStr); + nLength = xstrncpyarg(sOption, sizeof(sOption), pStr, args); + va_end(args); + + if (nLength) + { + char *pValue = xstrdup(sOption); + if (pValue == NULL) return XSTDERR; + + char *pKey = xstrdup(pHeader); + if (pKey == NULL) + { + free(pValue); + return XSTDERR; + } + + xmap_pair_t *pPair = XMap_GetPair(&pHttp->headerMap, pKey); + if (pPair != NULL) + { + if (!pHttp->nAllowUpdate) + { + free(pValue); + free(pKey); + return XSTDNON; + } + + free(pPair->pData); + pPair->pData = pValue; + } + else if (XMap_Put(&pHttp->headerMap, pKey, pValue) != XMAP_OK) + { + free(pValue); + free(pKey); + return XSTDERR; + } + } + + pHttp->nComplete = 0; + if (!pHttp->headerMap.nUsed) return XSTDERR; + + return (int)pHttp->headerMap.nUsed; +} + +int XHTTP_SetAuthBasic(xhttp_t *pHttp, const char *pUser, const char *pPwd) +{ + xbool_t nAllowUpdate = pHttp->nAllowUpdate; + char sToken[XHTTP_OPTION_MAX]; + int nStatus = 0; + + size_t nLength = xstrncpyf(sToken, sizeof(sToken), "%s:%s", pUser, pPwd); + char *pEncodedToken = XCrypt_Base64((const uint8_t*)sToken, &nLength); + if (pEncodedToken == NULL) return XSTDERR; + + pHttp->nAllowUpdate = XTRUE; + nStatus = XHTTP_AddHeader(pHttp, "Authorization", "Basic %s", pEncodedToken); + + pHttp->nAllowUpdate = nAllowUpdate; + free(pEncodedToken); + + return nStatus; +} + +xbyte_buffer_t* XHTTP_Assemble(xhttp_t *pHttp, const uint8_t *pContent, size_t nLength) +{ + if (pHttp->nComplete) return &pHttp->dataRaw; + nLength = (pContent != NULL) ? nLength : 0; + xbool_t nAllowUpdate = pHttp->nAllowUpdate; + + xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + xmap_t *pHdrMap = &pHttp->headerMap; + + XByteBuffer_Clear(pBuffer); + pHttp->nHeaderLength = 0; + pHttp->nHeaderCount = 0; + int nStatus = 0; + + if (pHttp->eType == XHTTP_REQUEST) + { + const char *pMethod = XHTTP_GetMethodStr(pHttp->eMethod); + nStatus = XByteBuffer_AddFmt(pBuffer, "%s %s HTTP/%s\r\n", + pMethod, pHttp->sUrl, pHttp->sVersion); + } + else if (pHttp->eType == XHTTP_RESPONSE) + { + const char *pCodeStr = XHTTP_GetCodeStr(pHttp->nStatusCode); + nStatus = XByteBuffer_AddFmt(pBuffer, "HTTP/%s %d %s\r\n", + pHttp->sVersion, pHttp->nStatusCode, pCodeStr); + } + + if (nStatus == XSTDERR) return NULL; + + if (nLength > 0) + { + pHttp->nAllowUpdate = XTRUE; + nStatus = XHTTP_AddHeader(pHttp, "Content-Length", "%zu", nLength); + if (nStatus <= 0) return NULL; + } + + if ((pHdrMap->nUsed > 0 && + XMap_Iterate(pHdrMap, XHTTP_HeaderWriteCb, pHttp) != XMAP_OK) || + XByteBuffer_AddFmt(pBuffer, "%s", "\r\n") == XSTDERR) return NULL; + + pHttp->nHeaderLength = pBuffer->nUsed; + pHttp->nHeaderCount = (uint16_t)pHdrMap->nUsed; + if (nLength > 0 && XByteBuffer_Add(pBuffer, pContent, nLength) <= 0) return NULL; + + pHttp->nAllowUpdate = nAllowUpdate; + pHttp->nContentLength = nLength; + pHttp->nComplete = 1; + return pBuffer; +} + +const char* XHTTP_GetHeader(xhttp_t *pHttp, const char* pHeader) +{ + char *pKey = xstracase(pHeader, XSTR_LOWER); + if (pKey == NULL) return NULL; + + xmap_t *pMap = &pHttp->headerMap; + char *pHdr = (char*)XMap_Get(pMap, pKey); + + free(pKey); + return pHdr; +} + +char* XHTTP_GetHeaderRaw(xhttp_t *pHttp) +{ + if (pHttp == NULL || !pHttp->nHeaderLength) return NULL; + else if (pHttp->dataRaw.nUsed < pHttp->nHeaderLength) return NULL; + + char *pHeaderStr = (char*)malloc(pHttp->nHeaderLength + 1); + if (pHeaderStr == NULL) return NULL; + + size_t nLength = xstrncpys( + pHeaderStr, + pHttp->nHeaderLength + 1, + (char*)pHttp->dataRaw.pData, + pHttp->nHeaderLength + ); + + if (!nLength) + { + free(pHeaderStr); + return NULL; + } + + return pHeaderStr; +} + +const uint8_t* XHTTP_GetBody(xhttp_t *pHttp) +{ + if (pHttp == NULL) return NULL; + xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + return (pBuffer->nUsed > pHttp->nHeaderLength) ? + &pBuffer->pData[pHttp->nHeaderLength] : NULL; +} + +size_t XHTTP_GetBodySize(xhttp_t *pHttp) +{ + if (pHttp == NULL || !pHttp->nHeaderLength) return 0; + const xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + return (pBuffer->nUsed > pHttp->nHeaderLength) ? + (pBuffer->nUsed - pHttp->nHeaderLength) : 0; +} + +static int XHTTP_CheckComplete(xhttp_t *pHttp) +{ + const char *pCntType = XHTTP_GetHeader(pHttp, "Content-Type"); + size_t nPayloadSize = XHTTP_GetBodySize(pHttp); + + pHttp->nComplete = ((pHttp->nContentLength && pHttp->nContentLength <= nPayloadSize) || + (!pHttp->nContentLength && !xstrused(pCntType))) ? XSTDOK : XSTDNON; + + return pHttp->nComplete; +} + +static xhttp_method_t XHTTP_ParseMethod(xhttp_t *pHttp) +{ + const char *pData = (const char *)pHttp->dataRaw.pData; + return XHTTP_GetMethodType(pData); +} + +static xhttp_type_t XHTTP_ParseType(xhttp_t *pHttp) +{ + const char *pData = (const char*)pHttp->dataRaw.pData; + if (!strncmp(pData, "HTTP", 4)) return XHTTP_RESPONSE; + return XHTTP_REQUEST; +} + +static size_t XHTTP_ParseVersion(xhttp_t *pHttp) +{ + const char *pEndPos = (pHttp->eType == XHTTP_REQUEST) ? "\r" : " "; + const char *pData = (const char *)pHttp->dataRaw.pData; + return xstrncuts(pHttp->sVersion, sizeof(pHttp->sVersion), pData, "HTTP/", pEndPos); +} + +static uint16_t XHTTP_ParseCode(xhttp_t *pHttp) +{ + char sField[XHTTP_FIELD_MAX]; + const char *pData = (const char *)pHttp->dataRaw.pData; + size_t nSize = xstrncuts(sField, sizeof(sField), pData, "HTTP/", NULL); + size_t nSkip = strlen(pHttp->sVersion) + 1; + return (nSize > 0 && nSkip < nSize) ? (uint16_t)atoi(&sField[nSkip]) : 0; +} + +static size_t XHTTP_ParseHeaderLength(const char *pHdrStr) +{ + int nPosit = xstrsrc(pHdrStr, "\r\n\r\n"); + if (nPosit <= 0) return 0; + return (size_t)nPosit + 4; +} + +static size_t XHTTP_ParseContentLength(xhttp_t *pHttp) +{ + const char *pHdr = XHTTP_GetHeader(pHttp, "Content-Length"); + return (pHdr != NULL) ? atol(pHdr) : 0; +} + +static size_t XHTTP_ParseUrl(xhttp_t *pHttp) +{ + if (pHttp->eType == XHTTP_RESPONSE) return XSTDOK; + const char *pHeader = (const char *)pHttp->dataRaw.pData; + const char *pMethod = XHTTP_GetMethodStr(pHttp->eMethod); + + char sTmpUrl[XHTTP_URL_MAX]; + sTmpUrl[0] = XSTR_NUL; + size_t nPosit = 0; + + size_t nLength = xstrncuts(sTmpUrl, sizeof(sTmpUrl), pHeader, pMethod, "HTTP/"); + if (!nLength) return XSTDNON; + nLength -= 1; + + while (nPosit < sizeof(sTmpUrl) - 1 && sTmpUrl[nPosit] == XSTR_SPACE_CHAR) nPosit++; + while (nLength > 0 && sTmpUrl[nLength] == XSTR_SPACE_CHAR) sTmpUrl[nLength--] = XSTR_NUL; + return xstrncpy(pHttp->sUrl, sizeof(pHttp->sUrl), &sTmpUrl[nPosit]); +} + +static int XHTTP_ParseHeaders(xhttp_t *pHttp) +{ + const char *pHeader = (const char *)pHttp->dataRaw.pData; + xarray_t *pArr = xstrsplit(pHeader, "\r\n"); + if (pArr == NULL) return XSTDERR; + + int nStatus = XSTDOK; + unsigned int i; + + for (i = 0; i < pArr->nUsed; i++) + { + char *pData = (char*)XArray_GetData(pArr, i); + if (pData != NULL) + { + int nPosit = xstrsrc(pData, ":"); + if (nPosit <= 0) continue; + + char* pHeaderStr = xstracasen(pData, XSTR_LOWER, nPosit); + if (pHeaderStr == NULL) + { + nStatus = XSTDERR; + break; + } + + if (XMap_Get(&pHttp->headerMap, pHeaderStr) != NULL) + { + free(pHeaderStr); + continue; + } + + while (pData[nPosit] == ' ' || pData[nPosit] == ':') nPosit++; + char *pValue = xstracut(pData, nPosit, strlen(pData) - nPosit); + + if (pValue == NULL) + { + nStatus = XSTDERR; + free(pHeaderStr); + break; + } + + if (pValue[0] == XSTR_SPACE_CHAR) xstrnrm(pValue, 0, 1); + if (XMap_Put(&pHttp->headerMap, pHeaderStr, pValue) != XMAP_OK) + { + nStatus = XSTDERR; + free(pHeaderStr); + free(pValue); + break; + } + } + } + + pHttp->nHeaderCount = (uint16_t)pHttp->headerMap.nUsed; + XArray_Destroy(pArr); + return nStatus; +} + +int XHTTP_AppendData(xhttp_t *pHttp, uint8_t* pData, size_t nSize) +{ + xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + return XByteBuffer_Add(pBuffer, pData, nSize); +} + +int XHTTP_InitParser(xhttp_t *pHttp, uint8_t* pData, size_t nSize) +{ + XHTTP_Init(pHttp, XHTTP_DUMMY, XSTDNON); + int nStatus = XHTTP_AppendData(pHttp, pData, nSize); + return nSize > 0 && nStatus <= 0 ? XSTDERR : XSTDOK; +} + +xhttp_status_t XHTTP_Parse(xhttp_t *pHttp) +{ + const char *pDataRaw = (const char*)pHttp->dataRaw.pData; + size_t nHeaderLength = XHTTP_ParseHeaderLength(pDataRaw); + if (!nHeaderLength) return XHTTP_INCOMPLETE; + + pHttp->dataRaw.pData[nHeaderLength - 1] = '\0'; + pHttp->nHeaderLength = nHeaderLength; + pHttp->eType = XHTTP_ParseType(pHttp); + + if (!XHTTP_ParseVersion(pHttp)) + return XHTTP_StatusCb(pHttp, XHTTP_INVALID); + + if (pHttp->eType == XHTTP_RESPONSE) + pHttp->nStatusCode = XHTTP_ParseCode(pHttp); + else if (pHttp->eType == XHTTP_REQUEST) + pHttp->eMethod = XHTTP_ParseMethod(pHttp); + + if (!XHTTP_ParseUrl(pHttp)) + return XHTTP_StatusCb(pHttp, XHTTP_INVALID); + + if (XHTTP_ParseHeaders(pHttp) == XSTDERR) + return XHTTP_StatusCb(pHttp, XHTTP_ERRALLOC); + + pHttp->nContentLength = XHTTP_ParseContentLength(pHttp); + pHttp->dataRaw.pData[nHeaderLength - 1] = '\n'; + + xhttp_status_t nStatus = XHTTP_StatusCb(pHttp, XHTTP_PARSED); + if (nStatus == XHTTP_TERMINATED) return XHTTP_TERMINATED; + else if (XHTTP_CheckComplete(pHttp)) return XHTTP_COMPLETE; + + return nStatus; +} + +xhttp_status_t XHTTP_ParseData(xhttp_t *pHttp, uint8_t* pData, size_t nSize) +{ + int nStatus = XHTTP_InitParser(pHttp, pData, nSize); + return nStatus > 0 ? XHTTP_Parse(pHttp) : XHTTP_ERRALLOC; +} + +xhttp_status_t XHTTP_ReadHeader(xhttp_t *pHttp, xsock_t *pSock) +{ + xbyte_buffer_t *pBuffer = (xbyte_buffer_t*)&pHttp->dataRaw; + xhttp_status_t eStatus = (xhttp_status_t)XHTTP_INCOMPLETE; + uint8_t sBuffer[XHTTP_RX_SIZE]; + + while (eStatus == XHTTP_INCOMPLETE) + { + int nBytes = XSock_Read(pSock, sBuffer, sizeof(sBuffer)); + if (nBytes <= 0) return XHTTP_StatusCb(pHttp, XHTTP_ERRREAD); + + if (XByteBuffer_Add(pBuffer, sBuffer, nBytes) <= 0) + return XHTTP_StatusCb(pHttp, XHTTP_ERRALLOC); + + eStatus = XHTTP_Parse(pHttp); + if (eStatus < XHTTP_TERMINATED) return eStatus; + + if (pHttp->nHeaderMax && + eStatus == XHTTP_INCOMPLETE && + pBuffer->nUsed >= pHttp->nHeaderMax) + return XHTTP_StatusCb(pHttp, XHTTP_BIGHDR); + + if (XSock_IsNB(pSock)) break; + } + + if (eStatus != XHTTP_COMPLETE && + eStatus != XHTTP_PARSED) return eStatus; + + const uint8_t *pBody = XHTTP_GetBody(pHttp); + size_t nBodySize = XHTTP_GetBodySize(pHttp); + if (pBody == NULL || !nBodySize) return eStatus; + + int nRetVal = XHTTP_Callback(pHttp, XHTTP_READ, pBody, nBodySize); + if (nRetVal == XSTDERR) return XHTTP_TERMINATED; + else if (nRetVal == XSTDNON) + { + pHttp->nComplete = 1; + return XHTTP_COMPLETE; + } + + return eStatus; +} + +xhttp_status_t XHTTP_ReadContent(xhttp_t *pHttp, xsock_t *pSock) +{ + if (pHttp->nComplete) return XHTTP_COMPLETE; + xbyte_buffer_t *pBuffer = &pHttp->dataRaw; + + uint8_t sBuffer[XHTTP_RX_SIZE]; + int nRetVal, nBytes = 0; + + if (pHttp->nContentLength) + { + size_t nBodySize = XHTTP_GetBodySize(pHttp); + while (nBodySize < pHttp->nContentLength) + { + nBytes = XSock_Read(pSock, sBuffer, sizeof(sBuffer)); + if (nBytes <= 0) return XHTTP_StatusCb(pHttp, XHTTP_ERRREAD); + + nRetVal = XHTTP_Callback(pHttp, XHTTP_READ, sBuffer, nBytes); + if (nRetVal == XSTDERR) return XHTTP_TERMINATED; + else if (nRetVal == XSTDNON) + { + pHttp->nComplete = 1; + return XHTTP_COMPLETE; + } + else if (nRetVal == XSTDOK) + { + if (XSock_IsNB(pSock)) break; + nBodySize += (size_t)nBytes; + continue; + } + + if (XByteBuffer_Add(pBuffer, sBuffer, (size_t)nBytes) <= 0) + return XHTTP_StatusCb(pHttp, XHTTP_ERRALLOC); + + nBodySize = XHTTP_GetBodySize(pHttp); + if (pSock->eStatus != XSOCK_ERR_NONE || XSock_IsNB(pSock)) break; + else if (pHttp->nContentMax && pBuffer->nUsed >= pHttp->nContentMax) + return XHTTP_StatusCb(pHttp, XHTTP_BIGCNT); + } + + if (nBodySize >= pHttp->nContentLength) + { + pHttp->nComplete = 1; + return XHTTP_COMPLETE; + } + + return XHTTP_INCOMPLETE; + } + + const char *pContentType = XHTTP_GetHeader(pHttp, "Content-Type"); + if (!xstrused(pContentType)) return XHTTP_COMPLETE; + + while (XSock_IsOpen(pSock)) + { + nBytes = XSock_Read(pSock, sBuffer, sizeof(sBuffer)); + if (nBytes <= 0) + { + if ((!pHttp->nContentLength && !XHTTP_GetBodySize(pHttp)) || + XSock_Status(pSock) == XSOCK_EOF) return XHTTP_COMPLETE; + + return XHTTP_StatusCb(pHttp, XHTTP_ERRREAD); + } + + nRetVal = XHTTP_Callback(pHttp, XHTTP_READ, sBuffer, nBytes); + if (nRetVal == XSTDERR) return XHTTP_TERMINATED; + else if (nRetVal == XSTDNON) + { + pHttp->nComplete = 1; + return XHTTP_COMPLETE; + } + else if (nRetVal == XSTDOK) + { + if (XSock_IsNB(pSock)) break; + continue; + } + + if (XByteBuffer_Add(pBuffer, sBuffer, (size_t)nBytes) <= 0) + return XHTTP_StatusCb(pHttp, XHTTP_ERRALLOC); + + if (pSock->eStatus != XSOCK_ERR_NONE || XSock_IsNB(pSock)) break; + else if (pHttp->nContentMax && pBuffer->nUsed >= pHttp->nContentMax) + return XHTTP_StatusCb(pHttp, XHTTP_BIGCNT); + } + + if (XSock_Status(pSock) == XSOCK_EOF) + { + pHttp->nComplete = XTRUE; + return XHTTP_COMPLETE; + } + + return XHTTP_INCOMPLETE; +} + +xhttp_status_t XHTTP_Receive(xhttp_t *pHttp, xsock_t *pSock) +{ + xhttp_status_t eStatus = XHTTP_ReadHeader(pHttp, pSock); + if (eStatus != XHTTP_PARSED) return eStatus; + return XHTTP_ReadContent(pHttp, pSock); +} + +xhttp_status_t XHTTP_Exchange(xhttp_t *pRequest, xhttp_t *pResponse, xsock_t *pSock) +{ + if (XSock_IsNB(pSock)) return XHTTP_StatusCb(pRequest, XHTTP_ERRFDMODE); + XHTTP_Init(pResponse, XHTTP_DUMMY, XSTDNON); + xbyte_buffer_t *pBuff = &pRequest->dataRaw; + + int nStatus = XSock_WriteBuff(pSock, pBuff); + if (nStatus <= 0) return XHTTP_StatusCb(pRequest, XHTTP_ERRWRITE); + + nStatus = XHTTP_Callback( + pRequest, + XHTTP_WRITE, + pBuff->pData, + pBuff->nUsed + ); + + if (nStatus == XSTDERR) + return XHTTP_TERMINATED; + + XHTTP_SetCallback( + pResponse, + pRequest->callback, + pRequest->pUserCtx, + pRequest->nCbTypes + ); + + return XHTTP_Receive(pResponse, pSock); +} + +xhttp_status_t XHTTP_LinkExchange(xhttp_t *pRequest, xhttp_t *pResponse, xlink_t *pLink) +{ + if (!xstrused(pLink->sProtocol)) + xstrncpy(pLink->sProtocol, sizeof(pLink->sProtocol), "http"); + + if (!pLink->nPort) + { + pLink->nPort = XHTTP_DEF_PORT; + xstrncat(pLink->sHost, sizeof(pLink->sHost), ":%d", pLink->nPort); + } + + if (strncmp(pLink->sProtocol, "http", 4)) + return XHTTP_StatusCb(pRequest, XHTTP_ERRPROTO); + + xsock_type_t eType = XSOCK_TCP_CLIENT; + xsock_t sock; + + if (!strncmp(pLink->sProtocol, "https", 5)) + { + eType = XSOCK_SSL_PREFERED_CLIENT; + XSock_InitSSL(); + } + + if (xstrused(pLink->sUser) && xstrused(pLink->sPass) && + XHTTP_SetAuthBasic(pRequest, pLink->sUser, pLink->sPass) <= 0) + return XHTTP_StatusCb(pRequest, XHTTP_ERRAUTH); + + if (XSock_Setup(&sock, eType, pLink->sHost) == XSOCK_INVALID) + return XHTTP_StatusCb(pRequest, XHTTP_ERRCONNECT); + + if (pRequest->nTimeout) + { + XSOCKET nFD = XSock_TimeOutR(&sock, (int)pRequest->nTimeout, 0); + if (nFD == XSOCK_INVALID) return XHTTP_StatusCb(pRequest, XHTTP_ERRTIMEO); + } + + xhttp_status_t eStatus = XHTTP_Exchange(pRequest, pResponse, &sock); + XSock_Close(&sock); + return eStatus; +} + +xhttp_status_t XHTTP_EasyExchange(xhttp_t *pRequest, xhttp_t *pResponse, const char *pLink) +{ + xlink_t link; + int nStatus = XLink_Parse(&link, pLink); + if (nStatus < 0) return XHTTP_StatusCb(pRequest, XHTTP_ERRLINK); + return XHTTP_LinkExchange(pRequest, pResponse, &link); +} + +xhttp_status_t XHTTP_Perform(xhttp_t *pHttp, xsock_t *pSock, const uint8_t *pBody, size_t nLength) +{ + if (XSock_IsNB(pSock)) return XHTTP_StatusCb(pHttp, XHTTP_ERRFDMODE); + xbyte_buffer_t *pBuff = XHTTP_Assemble(pHttp, pBody, nLength); + if (pBuff == NULL) return XHTTP_StatusCb(pHttp, XHTTP_ERRASSEMBLE); + + int nStatus = XSock_WriteBuff(pSock, pBuff); + if (nStatus <= 0) return XHTTP_StatusCb(pHttp, XHTTP_ERRWRITE); + + nStatus = XHTTP_Callback( + pHttp, + XHTTP_WRITE, + pBuff->pData, + pBuff->nUsed + ); + + if (nStatus == XSTDERR) + return XHTTP_TERMINATED; + + XHTTP_Recycle(pHttp); // Recycle HTTP handle + return XHTTP_Receive(pHttp, pSock); +} + +xhttp_status_t XHTTP_LinkPerform(xhttp_t *pHttp, xlink_t *pLink, const uint8_t *pBody, size_t nLength) +{ + if (!xstrused(pLink->sProtocol)) + xstrncpy(pLink->sProtocol, sizeof(pLink->sProtocol), "http"); + + if (!pLink->nPort) + { + pLink->nPort = XHTTP_DEF_PORT; + xstrncat(pLink->sHost, sizeof(pLink->sHost), ":%d", pLink->nPort); + } + + if (strncmp(pLink->sProtocol, "http", 4)) + return XHTTP_StatusCb(pHttp, XHTTP_ERRPROTO); + + xsock_type_t eType = XSOCK_TCP_CLIENT; + xsock_addr_t addrInfo; + xsock_t sock; + + if (!strncmp(pLink->sProtocol, "https", 5)) + { + eType = XSOCK_SSL_PREFERED_CLIENT; + XSock_InitSSL(); + } + + if (xstrused(pLink->sUser) && xstrused(pLink->sPass) && + XHTTP_SetAuthBasic(pHttp, pLink->sUser, pLink->sPass) <= 0) + return XHTTP_StatusCb(pHttp, XHTTP_ERRAUTH); + + if (XSock_GetAddr(&addrInfo, pLink->sHost) < 0) + return XHTTP_StatusCb(pHttp, XHTTP_ERRRESOLVE); + + if (XHTTP_CHECK_FLAG(pHttp->nCbTypes, XHTTP_STATUS) && pHttp->callback != NULL) + { + char sBuffer[XHTTP_OPTION_MAX]; + xhttp_ctx_t cbCtx; + + cbCtx.nLength = xstrncpyf(sBuffer, sizeof(sBuffer), + "Resolved remote addr: %s", addrInfo.sAddr); + + cbCtx.pData = sBuffer; + cbCtx.eCbType = XHTTP_STATUS; + cbCtx.eStatus = XHTTP_RESOLVED; + pHttp->callback(pHttp, &cbCtx); + } + + addrInfo.nPort = addrInfo.nPort ? addrInfo.nPort : + (XSockType_IsSSL(eType) ? XHTTP_SSL_PORT : XHTTP_DEF_PORT); + + if (XSock_Open(&sock, eType, &addrInfo) == XSOCK_INVALID) + return XHTTP_StatusCb(pHttp, XHTTP_ERRCONNECT); + + if (pHttp->nTimeout) + { + XSOCKET nFD = XSock_TimeOutR(&sock, (int)pHttp->nTimeout, 0); + if (nFD == XSOCK_INVALID) return XHTTP_StatusCb(pHttp, XHTTP_ERRTIMEO); + } + + xhttp_status_t eStatus = XHTTP_Perform(pHttp, &sock, pBody, nLength); + XSock_Close(&sock); + return eStatus; +} + +xhttp_status_t XHTTP_EasyPerform(xhttp_t *pHttp, const char *pLink, const uint8_t *pBody, size_t nLength) +{ + xlink_t link; + if (XLink_Parse(&link, pLink) < 0) return XHTTP_ERRLINK; + return XHTTP_LinkPerform(pHttp, &link, pBody, nLength); +} + +xhttp_status_t XHTTP_SoloPerform(xhttp_t *pHttp, xhttp_method_t eMethod, const char *pLink, const uint8_t *pBody, size_t nLength) +{ + xlink_t link; + const char *pVer = XUtils_VersionShort(); + + if (XLink_Parse(&link, pLink) < 0) return XHTTP_ERRLINK; + if (XHTTP_InitRequest(pHttp, eMethod, link.sUrl, NULL) < 0) return XHTTP_ERRINIT; + + int nStatus = XHTTP_AddHeader(pHttp, "Host", "%s", link.sHost); + if (nStatus == XSTDERR) return XHTTP_ERRSETHDR; + else if (nStatus == XSTDNON) return XHTTP_ERREXISTS; + + nStatus = XHTTP_AddHeader(pHttp, "User-Agent", "xutils/%s", pVer); + if (nStatus == XSTDERR) return XHTTP_ERRSETHDR; + else if (nStatus == XSTDNON) return XHTTP_ERREXISTS; + + return XHTTP_LinkPerform(pHttp, &link, pBody, nLength); +} diff --git a/src/http.h b/src/http.h new file mode 100644 index 0000000..397f907 --- /dev/null +++ b/src/http.h @@ -0,0 +1,176 @@ +/*! + * @file libxutils/src/http.h + * + * This source is part of "libxutils" project + * 2018-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes implementation of + * the HTTP request/response parser and assembler. + * + */ + +#ifndef __XUTILS_HTTP_H__ +#define __XUTILS_HTTP_H__ + +#include "xstd.h" +#include "xmap.h" +#include "sock.h" +#include "addr.h" +#include "xbuf.h" +#include "xtype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define XHTTP_CHECK_FLAG(c, f) (((c) & (f)) == (f)) + +#define XHTTP_VER_DEFAULT "1.0" +#define XHTTP_PACKAGE_MAX 5000 * 1024 +#define XHTTP_HEADER_MAX 32 * 1024 +#define XHTTP_HEADER_SIZE 4096 +#define XHTTP_OPTION_MAX 1024 +#define XHTTP_FIELD_MAX 128 +#define XHTTP_URL_MAX 2048 +#define XHTTP_RX_SIZE 4096 + +#define XHTTP_SSL_PORT 443 +#define XHTTP_DEF_PORT 80 + +typedef enum { + XHTTP_DUMMY = 0, + XHTTP_PUT, + XHTTP_GET, + XHTTP_POST, + XHTTP_DELETE, + XHTTP_OPTIONS +} xhttp_method_t; + +typedef enum { + XHTTP_INITIAL = 0, + XHTTP_REQUEST, + XHTTP_RESPONSE +} xhttp_type_t; + +typedef enum { + XHTTP_NONE = (uint8_t)0, + XHTTP_INVALID, + XHTTP_ERRINIT, + XHTTP_ERRLINK, + XHTTP_ERRAUTH, + XHTTP_ERRREAD, + XHTTP_ERRWRITE, + XHTTP_ERRPROTO, + XHTTP_ERRTIMEO, + XHTTP_ERRALLOC, + XHTTP_ERRSETHDR, + XHTTP_ERRFDMODE, + XHTTP_ERREXISTS, + XHTTP_ERRCONNECT, + XHTTP_ERRRESOLVE, + XHTTP_ERRASSEMBLE, + XHTTP_TERMINATED, + XHTTP_INCOMPLETE, + XHTTP_RESOLVED, + XHTTP_COMPLETE, + XHTTP_BIGCNT, + XHTTP_BIGHDR, + XHTTP_PARSED +} xhttp_status_t; + +typedef enum +{ + XHTTP_OTHER = (1 << 0), + XHTTP_READ = (1 << 1), + XHTTP_WRITE = (1 << 2), + XHTTP_ERROR = (1 << 3), + XHTTP_STATUS = (1 << 4), + XHTTP_CB_ALL = 30 +} xhttp_cbtype_t; + +typedef struct xhttp_ctx_ { + xhttp_status_t eStatus; + xhttp_cbtype_t eCbType; + const void* pData; + size_t nLength; +} xhttp_ctx_t; + +struct xhttp_; +typedef struct xhttp_ xhttp_t; +typedef int(*xhttp_cb_t)(xhttp_t *pHttp, xhttp_ctx_t *pCbCtx); + +struct xhttp_ { + xhttp_cb_t callback; + uint16_t nCbTypes; + void *pUserCtx; + + xhttp_method_t eMethod; + xbyte_buffer_t dataRaw; + xhttp_type_t eType; + xmap_t headerMap; + + uint16_t nHeaderCount; + uint16_t nStatusCode; + size_t nContentLength; + size_t nHeaderLength; + + size_t nContentMax; + size_t nHeaderMax; + size_t nTimeout; + + xbool_t nAllowUpdate; + xbool_t nAllocated; + xbool_t nComplete; + + char sVersion[XHTTP_FIELD_MAX]; + char sUrl[XHTTP_URL_MAX]; +}; + +xbool_t XHTTP_IsSuccessCode(xhttp_t *pHandle); +const char* XHTTP_GetCodeStr(int nCode); +const char* XHTTP_GetMethodStr(xhttp_method_t eMethod); +const char* XHTTP_GetStatusStr(xhttp_status_t eStatus); +xhttp_method_t XHTTP_GetMethodType(const char *pData); + +void XHTTP_Clear(xhttp_t *pHttp); +xhttp_t *XHTTP_Alloc(xhttp_method_t eMethod, size_t nDataSize); + +int XHTTP_Copy(xhttp_t *pDst, xhttp_t *pSrc); +int XHTTP_Init(xhttp_t *pHttp, xhttp_method_t eMethod, size_t nSize); +int XHTTP_InitRequest(xhttp_t *pHttp, xhttp_method_t eMethod, const char *pUri, const char *pVer); +int XHTTP_InitResponse(xhttp_t *pHttp, uint16_t nStatusCode, const char *pVer); + +int XHTTP_SetAuthBasic(xhttp_t *pHttp, const char *pUser, const char *pPwd); +int XHTTP_AddHeader(xhttp_t *pHttp, const char *pHeader, const char *pStr, ...); +xbyte_buffer_t* XHTTP_Assemble(xhttp_t *pHttp, const uint8_t *pContent, size_t nLength); + +const char* XHTTP_GetHeader(xhttp_t *pHttp, const char* pHeader); +const uint8_t* XHTTP_GetBody(xhttp_t *pHttp); +char* XHTTP_GetHeaderRaw(xhttp_t *pHttp); +size_t XHTTP_GetBodySize(xhttp_t *pHttp); + +int XHTTP_InitParser(xhttp_t *pHttp, uint8_t* pData, size_t nSize); +int XHTTP_AppendData(xhttp_t *pHttp, uint8_t* pData, size_t nSize); +xhttp_status_t XHTTP_ParseData(xhttp_t *pHttp, uint8_t* pData, size_t nSize); +xhttp_status_t XHTTP_Parse(xhttp_t *pHttp); + +int XHTTP_SetCallback(xhttp_t *pHttp, xhttp_cb_t callback, void *pCbCtx, uint16_t nCbTypes); +xhttp_status_t XHTTP_ReadHeader(xhttp_t *pHttp, xsock_t *pSock); +xhttp_status_t XHTTP_ReadContent(xhttp_t *pHttp, xsock_t *pSock); +xhttp_status_t XHTTP_Receive(xhttp_t *pHttp, xsock_t *pSock); +int XHTTP_Recycle(xhttp_t *pHttp); + +xhttp_status_t XHTTP_Exchange(xhttp_t *pRequest, xhttp_t *pResponse, xsock_t *pSock); +xhttp_status_t XHTTP_LinkExchange(xhttp_t *pRequest, xhttp_t *pResponse, xlink_t *pLink); +xhttp_status_t XHTTP_EasyExchange(xhttp_t *pRequest, xhttp_t *pResponse, const char *pLink); + +xhttp_status_t XHTTP_Perform(xhttp_t *pHttp, xsock_t *pSock, const uint8_t *pBody, size_t nLength); +xhttp_status_t XHTTP_LinkPerform(xhttp_t *pHttp, xlink_t *pLink, const uint8_t *pBody, size_t nLength); +xhttp_status_t XHTTP_EasyPerform(xhttp_t *pHttp, const char *pLink, const uint8_t *pBody, size_t nLength); +xhttp_status_t XHTTP_SoloPerform(xhttp_t *pHttp, xhttp_method_t eMethod, const char *pLink, const uint8_t *pBody, size_t nLength); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_HTTP_H__ */ diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..7ffb367 --- /dev/null +++ b/src/list.c @@ -0,0 +1,221 @@ +/*! + * @file libxutils/src/list.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of linked list + */ + +#include "list.h" + +void XList_Init(xlist_t *pList, void *pData, size_t nSize, xlist_cb_t clearCb, void *pCtx) +{ + pList->clearCb = clearCb; + pList->pCbCtx = pCtx; + pList->pData = pData; + pList->nSize = nSize; + pList->pNext = NULL; + pList->pPrev = NULL; + pList->nAlloc = 0; +} + +xlist_t *XList_New(void *pData, size_t nSize, xlist_cb_t clearCb, void *pCtx) +{ + xlist_t *pList = (xlist_t*)malloc(sizeof(xlist_t)); + if (pList == NULL) return NULL; + + XList_Init(pList, pData, nSize, clearCb, pCtx); + pList->nAlloc = 1; + return pList; +} + +void XList_Free(xlist_t *pList) +{ + if (pList != NULL) + { + if (pList->pData != NULL) + { + if (pList->clearCb != NULL) + pList->clearCb(pList->pCbCtx, pList->pData); + else if (pList->nSize > 0) + free(pList->pData); + } + + if (pList->nAlloc) free(pList); + else XList_Init(pList, NULL, 0, NULL, NULL); + } +} + +xlist_t* XList_Unlink(xlist_t *pList) +{ + if (pList == NULL) return NULL; + xlist_t *pPrev = pList->pPrev; + xlist_t *pNext = pList->pNext; + + if (pPrev != NULL) pPrev->pNext = pNext; + if (pNext != NULL) pNext->pPrev = pPrev; + + XList_Free(pList); + return pNext ? pNext : pPrev; +} + +xlist_t* XList_RemoveHead(xlist_t *pList) +{ + xlist_t *pHead = XList_GetHead(pList); + return XList_Unlink(pHead); +} + +xlist_t* XList_RemoveTail(xlist_t *pList) +{ + xlist_t *pTail = XList_GetTail(pList); + return XList_Unlink(pTail); +} + +xlist_t* XList_GetHead(xlist_t *pList) +{ + if (pList == NULL) return NULL; + + while (pList->pPrev != NULL) + pList = pList->pPrev; + + return pList; +} + +xlist_t* XList_GetTail(xlist_t *pList) +{ + if (pList == NULL) return NULL; + + while (pList->pNext != NULL) + pList = pList->pNext; + + return pList; +} + +xlist_t* XList_MakeRing(xlist_t *pList) +{ + if (pList == NULL) return NULL; + xlist_t *pHead = XList_GetHead(pList); + xlist_t *pTail = XList_GetTail(pList); + pHead->pPrev = pTail; + pTail->pNext = pHead; + return pHead; +} + +uint8_t XList_IsRing(xlist_t *pList) +{ + if (pList == NULL) return 0; + xlist_t *pNode = pList->pNext; + + while (pNode != NULL && pNode != pList) + pNode = pNode->pNext; + + return (pNode == pList); +} + +void XList_Clear(xlist_t *pList) +{ + while (pList != NULL) + pList = XList_Unlink(pList); +} + +xlist_t* XList_InsertNext(xlist_t *pList, xlist_t *pNode) +{ + if (pList == NULL || pNode == NULL) return NULL; + + if (pNode->pCbCtx == NULL) + pNode->pCbCtx = pList->pCbCtx; + + if (pNode->clearCb == NULL) + pNode->clearCb = pList->clearCb; + + xlist_t *pNext = pList->pNext; + pList->pNext = pNode; + pNode->pNext = pNext; + pNode->pPrev = pList; + + if (pNext != NULL) + { + pNext->pPrev = pNode; + return pNext; + } + + return pNode; +} + +xlist_t* XList_InsertHead(xlist_t *pList, xlist_t *pNode) +{ + if (pList == NULL || pNode == NULL) return NULL; + xlist_t *pHead = XList_GetHead(pList); + + if (pNode->pCbCtx == NULL) + pNode->pCbCtx = pHead->pCbCtx; + + if (pNode->clearCb == NULL) + pNode->clearCb = pHead->clearCb; + + pHead->pPrev = pNode; + pNode->pNext = pHead; + pNode->pPrev = NULL; + return pNode; +} + +xlist_t* XList_InsertTail(xlist_t *pList, xlist_t *pNode) +{ + if (pList == NULL || pNode == NULL) return NULL; + xlist_t *pTail = XList_GetTail(pList); + + if (pNode->pCbCtx == NULL) + pNode->pCbCtx = pTail->pCbCtx; + + if (pNode->clearCb == NULL) + pNode->clearCb = pTail->clearCb; + + pTail->pNext = pNode; + pNode->pPrev = pTail; + pNode->pNext = NULL; + return pNode; +} + +xlist_t* XList_PushNext(xlist_t *pList, void *pData, size_t nSize) +{ + if (pList == NULL) return NULL; + xlist_t *pNode = XList_New(pData, nSize, pList->clearCb, pList->pCbCtx); + return pNode != NULL ? XList_InsertNext(pList, pNode) : NULL; +} + +xlist_t* XList_PushFront(xlist_t *pList, void *pData, size_t nSize) +{ + if (pList == NULL) return NULL; + xlist_t *pNode = XList_New(pData, nSize, pList->clearCb, pList->pCbCtx); + return pNode != NULL ? XList_InsertHead(pList, pNode) : NULL; +} + +xlist_t* XList_PushBack(xlist_t *pList, void *pData, size_t nSize) +{ + if (pList == NULL) return NULL; + xlist_t *pNode = XList_New(pData, nSize, pList->clearCb, pList->pCbCtx); + return pNode != NULL ? XList_InsertTail(pList, pNode) : NULL; +} + +xlist_t* XList_Search(xlist_t *pList, void *pUserPtr, xlist_comparator_t compare) +{ + if (pList == NULL || compare == NULL) return NULL; + xlist_t *pHead = XList_GetHead(pList); + + while (pHead != NULL) + { + int nRet = compare(pUserPtr, pHead); + if (nRet > 0) return pHead; + else if (nRet < 0) break; + pHead = pHead->pNext; + } + + return NULL; +} + +xlist_t* XList_Remove(xlist_t *pList, void *pUserPtr, xlist_comparator_t compare) +{ + xlist_t *pNode = XList_Search(pList, pUserPtr, compare); + return (pNode != NULL) ? XList_Unlink(pNode) : NULL; +} diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..ad4e52b --- /dev/null +++ b/src/list.h @@ -0,0 +1,64 @@ +/*! + * @file libxutils/src/list.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of linked list + */ + +#ifndef __XUTILS_XLIST_H__ +#define __XUTILS_XLIST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef void(*xlist_cb_t)(void *pCbCtx, void *pData); + +typedef struct xlist { + xlist_cb_t clearCb; + struct xlist* pNext; + struct xlist* pPrev; + void* pCbCtx; + void* pData; + size_t nSize; + uint8_t nAlloc; +} xlist_t; + +typedef int(*xlist_comparator_t)(void *pUserPtr, xlist_t *pNode); + +xlist_t *XList_New(void *pData, size_t nSize, xlist_cb_t clearCb, void *pCtx); +void XList_Init(xlist_t *pList, void *pData, size_t nSize, xlist_cb_t clearCb, void *pCtx); +void XList_Clear(xlist_t *pList); +void XList_Free(xlist_t *pList); + +xlist_t* XList_Unlink(xlist_t *pList); +xlist_t* XList_RemoveHead(xlist_t *pList); +xlist_t* XList_RemoveTail(xlist_t *pList); + +xlist_t* XList_GetHead(xlist_t *pList); +xlist_t* XList_GetTail(xlist_t *pList); + +xlist_t* XList_InsertNext(xlist_t *pList, xlist_t *pNode); +xlist_t* XList_InsertHead(xlist_t *pList, xlist_t *pNode); +xlist_t* XList_InsertTail(xlist_t *pList, xlist_t *pNode); + +xlist_t* XList_PushNext(xlist_t *pList, void *pData, size_t nSize); +xlist_t* XList_PushFront(xlist_t *pList, void *pData, size_t nSize); +xlist_t* XList_PushBack(xlist_t *pList, void *pData, size_t nSize); + +xlist_t* XList_Search(xlist_t *pList, void *pUserPtr, xlist_comparator_t compare); +xlist_t* XList_Remove(xlist_t *pList, void *pUserPtr, xlist_comparator_t compare); + +xlist_t* XList_MakeRing(xlist_t *pList); +uint8_t XList_IsRing(xlist_t *pList); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XLIST_H__ */ \ No newline at end of file diff --git a/src/sock.c b/src/sock.c new file mode 100644 index 0000000..b32fc11 --- /dev/null +++ b/src/sock.c @@ -0,0 +1,1505 @@ +/*! + * @file libxutils/src/sock.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Cross-plaform socket operations such as + * create, bind, connect, listen, select and etc. + */ + +#ifdef _XUTILS_USE_GNU +#define _GNU_SOURCE +#endif + +#include "sock.h" +#include "xstr.h" +#include "sync.h" +#include "xtype.h" + +/* + S.K. >> Note: + Disable deprecated warnings for gethostbyaddr() function. + Library already have safer implementation of getaddrinfo() + but also supporting old implementation for legacy devices. +*/ +#ifdef _WIN32 +#pragma warning(disable : 4996) +#endif + +#define XSOCK_MIN(a,b) (((a)<(b))?(a):(b)) +static XATOMIC g_nSSLInit = 0; + +xsock_inaddr_t* XSock_InAddr(xsock_t *pSock) { return &pSock->inAddr; } +xsock_status_t XSock_Status(const xsock_t *pSock) { return pSock->eStatus; } +xsock_type_t XSock_GetType(const xsock_t *pSock) { return pSock->eType; } + +XSOCKET XSock_GetFD(const xsock_t *pSock) { return pSock->nFD; } +xbool_t XSock_IsSSL(const xsock_t *pSock) { return (xbool_t)pSock->nSSL; } +xbool_t XSock_IsNB(const xsock_t *pSock) { return (xbool_t)pSock->nNB; } + +uint32_t XSock_GetNetAddr(const xsock_t *pSock) { return pSock->nAddr; } +uint16_t XSock_GetPort(const xsock_t *pSock) { return pSock->nPort; } +size_t XSock_GetFDMax(const xsock_t *pSock) { return pSock->nFdMax; } +int XSock_GetSockType(const xsock_t *pSock) { return pSock->nType; } +int XSock_GetProto(const xsock_t *pSock) { return pSock->nProto; } + +xbool_t XSockType_IsSSL(xsock_type_t eType) +{ + if (eType == XSOCK_SSL_PREFERED_CLIENT || + eType == XSOCK_SSL_PREFERED_SERVER || + eType == XSOCK_SSLV2_SERVER || + eType == XSOCK_SSLV3_SERVER || + eType == XSOCK_SSLV2_CLIENT || + eType == XSOCK_SSLV3_CLIENT || + eType == XSOCK_SSLV2_PEER || + eType == XSOCK_SSLV3_PEER) + return XTRUE; + + return XFALSE; +} + +#ifdef _XUTILS_USE_SSL +typedef struct XSocketPriv { + xbool_t nShutdown; + void *pSSLCTX; + void *pSSL; +} xsock_priv_t; + +static xsock_priv_t* XSock_AllocPriv() +{ + xsock_priv_t *pPriv = (xsock_priv_t*)malloc(sizeof(xsock_priv_t)); + if (pPriv == NULL) return NULL; + + pPriv->nShutdown = XFALSE; + pPriv->pSSLCTX = NULL; + pPriv->pSSL = NULL; + return pPriv; +} + +static xsock_priv_t* XSock_GetOrAllocPriv(xsock_t *pSock) +{ + if (pSock == NULL) return NULL; + else if (pSock->pPrivate == NULL) + pSock->pPrivate = XSock_AllocPriv(); + return (xsock_priv_t*)pSock->pPrivate; +} + +SSL_CTX* XSock_GetSSLCTX(xsock_t *pSock) +{ + if (pSock == NULL || pSock->pPrivate == NULL) return NULL; + xsock_priv_t *pPriv = (xsock_priv_t*)pSock->pPrivate; + return (SSL_CTX*)pPriv->pSSLCTX; +} + +SSL* XSock_GetSSL(xsock_t *pSock) +{ + if (pSock == NULL || pSock->pPrivate == NULL) return NULL; + xsock_priv_t *pPriv = (xsock_priv_t*)pSock->pPrivate; + return (SSL*)pPriv->pSSL; +} + +static XSOCKET XSock_SetSSLCTX(xsock_t *pSock, SSL_CTX *pSSLCTX) +{ + xsock_priv_t *pPriv = XSock_GetOrAllocPriv(pSock); + if (pPriv == NULL) + { + pSock->eStatus = XSOCK_ERR_ALLOC; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + pPriv->pSSLCTX = pSSLCTX; + pSock->nSSL = XTRUE; + return pSock->nFD; +} + +static XSOCKET XSock_SetSSL(xsock_t *pSock, SSL *pSSL) +{ + xsock_priv_t *pPriv = XSock_GetOrAllocPriv(pSock); + if (pPriv == NULL) + { + pSock->eStatus = XSOCK_ERR_ALLOC; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + pPriv->nShutdown = XTRUE; + pPriv->pSSL = pSSL; + pSock->nSSL = XTRUE; + return pSock->nFD; +} + +static void XSock_SetShutdown(xsock_t *pSock, xbool_t nShutdown) +{ + xsock_priv_t *pPriv = (xsock_priv_t*)pSock->pPrivate; + if (pPriv != NULL) pPriv->nShutdown = nShutdown; +} + +static xsock_type_t XSock_GetPrefredSSL(xsock_type_t eType) +{ + if (eType == XSOCK_SSL_PREFERED_CLIENT) + { +#ifdef SSLv3_client_method + return XSOCK_SSLV3_CLIENT; +#else + return XSOCK_SSLV2_CLIENT; +#endif + } + else if (eType == XSOCK_SSL_PREFERED_SERVER) + { +#ifdef SSLv3_server_method + return XSOCK_SSLV3_SERVER; +#else + return XSOCK_SSLV2_SERVER; +#endif + } + + return eType; +} + +static const SSL_METHOD* XSock_GetSSLMethod(xsock_t *pSock) +{ + switch (pSock->eType) + { + case XSOCK_SSLV2_CLIENT: +#ifdef SSLv23_client_method + return SSLv23_client_method(); +#else + break; +#endif + case XSOCK_SSLV2_SERVER: +#ifdef SSLv23_server_method + return SSLv23_server_method(); +#else + break; +#endif + case XSOCK_SSLV3_CLIENT: +#ifdef SSLv3_client_method + return SSLv3_client_method(); +#else + break; +#endif + case XSOCK_SSLV3_SERVER: +#ifdef SSLv3_server_method + return SSLv3_server_method(); +#else + break; +#endif + default: + break; + } + + return NULL; +} +#endif + +void XSock_InitSSL(void) +{ +#ifdef _XUTILS_USE_SSL + if (XSYNC_ATOMIC_GET(&g_nSSLInit)) return; + +#if OPENSSLVERSION_NUMBER < 0x10100000L + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + + XSYNC_ATOMIC_SET(&g_nSSLInit, 1); +#endif +} + +void XSock_DeinitSSL(void) +{ +#ifdef _XUTILS_USE_SSL + if (!XSYNC_ATOMIC_GET(&g_nSSLInit)) return; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_free_strings(); +#else + EVP_PBE_cleanup(); +#endif + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + + XSYNC_ATOMIC_SET(&g_nSSLInit, 0); +#endif +} + +int XSock_LastSSLError(char *pDst, size_t nSize) +{ + if (pDst == NULL) return XSOCK_NONE; + int nLength = 0; + pDst[0] = XSTR_NUL; + +#ifdef _XUTILS_USE_SSL + BIO *pBIO = BIO_new(BIO_s_mem()); + ERR_print_errors(pBIO); + + char *pErrBuff = NULL; + size_t nErrSize = BIO_get_mem_data(pBIO, &pErrBuff); + if (!nErrSize || pErrBuff == NULL) return 0; + + nLength = nSize < nErrSize ? + nSize - 1 : nErrSize - 1; + + strncpy(pDst, pErrBuff, nLength); + pDst[nLength] = 0; + BIO_free(pBIO); +#else + (void)nSize; +#endif + + return nLength; +} + +int xclosesock(XSOCKET nFd) +{ +#ifdef _WIN32 + return closesocket(nFd); +#else + return close(nFd); +#endif +} + +const char* XSock_GetStatusStr(xsock_status_t eStatus) +{ + switch(eStatus) + { + case XSOCK_ERR_NONE: + return "No error was identified"; + case XSOCK_ERR_BIND: + return "Can not bind the socket"; + case XSOCK_ERR_JOIN: + return "Can not join to the socket"; + case XSOCK_ERR_SEND: + return "Can not send data with the socket"; + case XSOCK_ERR_RECV: + return "Can not receive data from the socket"; + case XSOCK_ERR_READ: + return "Can not read data from the socket"; + case XSOCK_ERR_WRITE: + return "Can not write data fo the socket"; + case XSOCK_ERR_SETFL: + return "Can not set flags to the socket"; + case XSOCK_ERR_GETFL: + return "Can not get flags from the socket"; + case XSOCK_ERR_ACCEPT: + return "Can not accept to the socket"; + case XSOCK_ERR_CONNECT: + return "Can not connect to the socket"; + case XSOCK_ERR_LISTEN: + return "Can not listen to the socket"; + case XSOCK_ERR_SETOPT: + return "Can not set options to the socket"; + case XSOCK_ERR_CREATE: + return "Can not create the socket"; + case XSOCK_ERR_INVALID: + return "Socket is not open"; + case XSOCK_ERR_SUPPORT: + return "Unsupported socket type"; + case XSOCK_ERR_SSLACC: + return "Can not accept SSL connection"; + case XSOCK_ERR_SSLCNT: + return "Can not connect to SSL server"; + case XSOCK_ERR_NOSSL: + return "No SSL (OpenSSL) support"; + case XSOCK_ERR_SSLCTX: + return "Can not create SSL context"; + case XSOCK_ERR_SSLKEY: + return "Can not set SSL key file"; + case XSOCK_ERR_SSLCRT: + return "Can not set SSL sert file"; + case XSOCK_ERR_PKCS12: + return "Failed to load PKCS12 file"; + case XSOCK_ERR_SSLCA: + return "Can not set SSL CA file"; + case XSOCK_ERR_SSLINV: + return "Invalid SSL object or context"; + case XSOCK_ERR_SSLNEW: + return "Failed to create new SSL object"; + case XSOCK_ERR_SSLREAD: + return "Can not read from SSL socket"; + case XSOCK_ERR_SSLWRITE: + return "Can not write to SSL socket"; + case XSOCK_ERR_SSLMET: + return "SSL method is not defined in the SSL library"; + case XSOCK_ERR_SSLERR: + return "SSL_ERROR_SSL ocurred during SSL read or write"; + case XSOCK_ERR_ALLOC: + return "Failed to allocate data for private SSL context"; + case XSOCK_ERR_ADDR: + return "Failed get IP address from hostname"; + case XSOCK_EOF: + return "Received final packet (FIN)"; + default: + break; + } + + return "Undefined error"; +} + +const char* XSock_ErrStr(xsock_t *pSock) +{ + if (pSock == NULL) return XSTR_EMPTY; + return XSock_GetStatusStr(pSock->eStatus); +} + +XSTATUS XSock_IsOpen(xsock_t *pSock) +{ + return pSock->nFD != XSOCK_INVALID ? + XSOCK_SUCCESS : XSOCK_NONE; +} + +XSTATUS XSock_Check(xsock_t *pSock) +{ + if (pSock->nFD == XSOCK_INVALID) + { + if (pSock->eStatus == XSOCK_ERR_NONE) + pSock->eStatus = XSOCK_ERR_INVALID; + return XSOCK_NONE; + } + + pSock->eStatus = XSOCK_ERR_NONE; + return XSOCK_SUCCESS; +} + +XSTATUS XSock_SetType(xsock_t *pSock, xsock_type_t eType) +{ + pSock->eStatus = XSOCK_ERR_NONE; + pSock->eType = eType; + + switch(eType) + { + case XSOCK_SSLV2_PEER: + case XSOCK_SSLV3_PEER: + case XSOCK_SSLV2_CLIENT: + case XSOCK_SSLV3_CLIENT: + case XSOCK_SSLV2_SERVER: + case XSOCK_SSLV3_SERVER: + case XSOCK_TCP_CLIENT: + case XSOCK_TCP_SERVER: + case XSOCK_TCP_PEER: + pSock->nProto = IPPROTO_TCP; + pSock->nType = SOCK_STREAM; + break; + case XSOCK_UDP_CLIENT: + case XSOCK_UDP_BCAST: + case XSOCK_UDP_MCAST: + case XSOCK_UDP_UCAST: + pSock->nProto = IPPROTO_UDP; + pSock->nType = SOCK_DGRAM; + break; + case XSOCK_RAW: + pSock->nProto = IPPROTO_TCP; + pSock->nType = SOCK_RAW; + break; + case XSOCK_UNDEFINED: + default: + pSock->eStatus = XSOCK_ERR_SUPPORT; + pSock->nProto = XSOCK_ERROR; + pSock->nType = XSOCK_ERROR; + return XSOCK_ERROR; + } + + return XSOCK_SUCCESS; +} + +XSTATUS XSock_Init(xsock_t *pSock, xsock_type_t eType, XSOCKET nFD, uint8_t nNB) +{ + pSock->pPrivate = NULL; + pSock->nFdMax = 0; + pSock->nAddr = 0; + pSock->nPort = 0; + pSock->nSSL = 0; + pSock->nFD = nFD; + pSock->nNB = nNB; + + #ifdef _XUTILS_USE_SSL + eType = XSock_GetPrefredSSL(eType); + + if (XSockType_IsSSL(eType)) + { + pSock->pPrivate = XSock_AllocPriv(); + if (pSock->pPrivate == NULL) + { + pSock->eStatus = XSOCK_ERR_ALLOC; + pSock->nFD = XSOCK_INVALID; + return XSOCK_ERROR; + } + } + #endif + + return XSock_SetType(pSock, eType); +} + +void XSock_Close(xsock_t *pSock) +{ + #ifdef _XUTILS_USE_SSL + if (pSock->pPrivate != NULL) + { + xsock_priv_t *pPriv = (xsock_priv_t*)pSock->pPrivate; + SSL *pSSL = (SSL*)pPriv->pSSL; + + if (pSSL != NULL) + { + if (pPriv->nShutdown) + SSL_shutdown(pSSL); + + SSL_free(pSSL); + } + + SSL_CTX *pSSLCTX = (SSL_CTX*)pPriv->pSSLCTX; + if (pSSLCTX != NULL) SSL_CTX_free(pSSLCTX); + + free(pSock->pPrivate); + pSock->pPrivate = NULL; + } +#endif + + if (pSock->nFD != XSOCK_INVALID) + { + shutdown(pSock->nFD, XSHUT_RDWR); + xclosesock(pSock->nFD); + pSock->nFD = XSOCK_INVALID; + } + + pSock->nSSL = 0; +} + +int XSock_SSLRead(xsock_t *pSock, void *pData, size_t nSize, xbool_t nExact) +{ + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nSize || pData == NULL) return XSOCK_NONE; + +#ifdef _XUTILS_USE_SSL + SSL *pSSL = XSock_GetSSL(pSock); + if (pSSL == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLINV; + XSock_Close(pSock); + return XSOCK_ERROR; + } + + uint8_t *pBuff = (uint8_t*)pData; + int nLeft = (int)nSize; + int nReceived = 0; + + while ((nLeft > 0 && nExact) || !nReceived) + { + int nBytes = SSL_read(pSSL, &pBuff[nReceived], nLeft); + if (nBytes <= 0) + { + int nError = SSL_get_error(pSSL, nBytes); + pSock->eStatus = XSOCK_ERR_SSLREAD; + + if (nError == SSL_ERROR_WANT_READ) continue; + else if (nError == SSL_ERROR_ZERO_RETURN) + { + XSock_SetShutdown(pSock, XFALSE); + pSock->eStatus = XSOCK_EOF; + } + else if (nError == SSL_ERROR_SYSCALL) + { + XSock_SetShutdown(pSock, XFALSE); + if (!nBytes) pSock->eStatus = XSOCK_EOF; + } + else if (nError == SSL_ERROR_SSL) + { + XSock_SetShutdown(pSock, XFALSE); + pSock->eStatus = XSOCK_ERR_SSLERR; + } + + if (pSock->eStatus != XSOCK_EOF) + nReceived = XSOCK_ERROR; + + XSock_Close(pSock); + return nReceived; + } + + nReceived += nBytes; + nLeft -= nBytes; + } + + return nReceived; +#else + (void)nExact; + pSock->eStatus = XSOCK_ERR_NOSSL; + XSock_Close(pSock); + return XSOCK_ERROR; +#endif +} + +int XSock_SSLWrite(xsock_t *pSock, const void *pData, size_t nLength) +{ + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nLength || pData == NULL) return XSOCK_NONE; + +#ifdef _XUTILS_USE_SSL + SSL *pSSL = XSock_GetSSL(pSock); + if (pSSL == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLINV; + XSock_Close(pSock); + return XSOCK_ERROR; + } + + uint8_t *pBuff = (uint8_t*)pData; + ssize_t nLeft = nLength; + size_t nSent = 0; + + while (nLeft > 0) + { + int nBytes = SSL_write(pSSL, &pBuff[nSent], nLeft); + if (nBytes <= 0) + { + int nError = SSL_get_error(pSSL, nBytes); + if (nError == SSL_ERROR_WANT_WRITE) continue; + else if (nError == SSL_ERROR_SSL || + nError == SSL_ERROR_SYSCALL) + XSock_SetShutdown(pSock, XFALSE); + + pSock->eStatus = XSOCK_ERR_SSLWRITE; + XSock_Close(pSock); + return nBytes; + } + + nSent += nBytes; + nLeft -= nBytes; + } + + return nSent; +#else + pSock->eStatus = XSOCK_ERR_NOSSL; + XSock_Close(pSock); + return XSOCK_ERROR; +#endif +} + +int XSock_RecvChunk(xsock_t *pSock, void* pData, size_t nSize) +{ + if (pSock->nSSL) return XSock_SSLRead(pSock, pData, nSize, XTRUE); + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nSize || pData == NULL) return XSOCK_NONE; + + uint8_t* pBuff = (uint8_t*)pData; + int nReceived = 0; + + while((size_t)nReceived < nSize) + { + int nChunk = XSOCK_MIN((int)nSize - nReceived, XSOCK_CHUNK_MAX); + int nRecvSize = recv(pSock->nFD, &pBuff[nReceived], nChunk, XMSG_NOSIGNAL); + + if (nRecvSize <= 0) + { + pSock->eStatus = XSOCK_EOF; + XSock_Close(pSock); + + if (nRecvSize < 0) + { + pSock->eStatus = XSOCK_ERR_RECV; + nReceived = XSOCK_ERROR; + } + + return nReceived; + } + + nReceived += nRecvSize; + } + + return nReceived; +} + +int XSock_Recv(xsock_t *pSock, void* pData, size_t nSize) +{ + if (pSock->nSSL) return XSock_SSLRead(pSock, pData, nSize, XFALSE); + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nSize || pData == NULL) return XSOCK_NONE; + + int nRecvSize = 0; + struct sockaddr_in client; + xsocklen_t slen = sizeof(client); + + if (pSock->nType != SOCK_DGRAM) nRecvSize = recv(pSock->nFD, pData, (int)nSize, XMSG_NOSIGNAL); + else nRecvSize = recvfrom(pSock->nFD, pData, (int)nSize, 0, (struct sockaddr*)&client, &slen); + + if (nRecvSize <= 0) + { + if (!nRecvSize) pSock->eStatus = XSOCK_EOF; + else pSock->eStatus = XSOCK_ERR_RECV; + XSock_Close(pSock); + } + + return nRecvSize; +} + +int XSock_SendChunk(xsock_t *pSock, void *pData, size_t nLength) +{ + if (pSock->nSSL) return XSock_SSLWrite(pSock, pData, nLength); + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nLength || pData == NULL) return XSOCK_NONE; + + uint8_t* pBuff =(uint8_t*)pData; + int nDone = 0; + + while((size_t)nDone < nLength) + { + int nChunk = XSOCK_MIN((int)nLength - nDone, XSOCK_CHUNK_MAX); + int nSent = send(pSock->nFD, &pBuff[nDone], nChunk, XMSG_NOSIGNAL); + + if (nSent <= 0) + { + pSock->eStatus = XSOCK_ERR_SEND; + XSock_Close(pSock); + return nSent; + } + + nDone += nSent; + } + + return nDone; +} + +int XSock_Send(xsock_t *pSock, const void *pData, size_t nLength) +{ + if (pSock->nSSL) return XSock_SSLWrite(pSock, pData, nLength); + if (!XSock_Check(pSock)) return XSOCK_ERROR; + if (!nLength || pData == NULL) return XSOCK_NONE; + int nSent = 0; + + if (pSock->nType != SOCK_DGRAM) nSent = send(pSock->nFD, pData, (int)nLength, XMSG_NOSIGNAL); + else nSent = sendto(pSock->nFD, pData, (int)nLength, XMSG_NOSIGNAL, (struct sockaddr*)&pSock->inAddr, sizeof(pSock->inAddr)); + + if (nSent <= 0) + { + pSock->eStatus = XSOCK_ERR_SEND; + XSock_Close(pSock); + } + + return nSent; +} + +int XSock_Read(xsock_t *pSock, void *pData, size_t nSize) +{ + if (pSock->nSSL) return XSock_SSLRead(pSock, pData, nSize, XFALSE); + else if (!XSock_Check(pSock)) return XSOCK_ERROR; + else if (!nSize || pData == NULL) return XSOCK_NONE; + int nReadSize = 0; + +#ifdef _WIN32 + (void)nReadSize; + return XSock_Recv(pSock, pData, nSize); +#elif EINTR + do nReadSize = read(pSock->nFD, pData, nSize); + while (nReadSize < 0 && errno == EINTR); +#else + nReadSize = read(pSock->nFD, pData, nSize); +#endif + + if (nReadSize <= 0) + { + if (!nReadSize) pSock->eStatus = XSOCK_EOF; + else pSock->eStatus = XSOCK_ERR_READ; + XSock_Close(pSock); + } + + return nReadSize; +} + +int XSock_Write(xsock_t *pSock, const void *pData, size_t nLength) +{ + if (pSock->nSSL) return XSock_SSLWrite(pSock, pData, nLength); + else if (!XSock_Check(pSock)) return XSOCK_INVALID; + else if (!nLength || pData == NULL) return XSOCK_NONE; + int nBytes = 0; + +#ifdef _WIN32 + nBytes = XSock_Send(pSock, pData, nLength); +#else + nBytes = write(pSock->nFD, pData, nLength); + if (nBytes <= 0) + { + pSock->eStatus = XSOCK_ERR_WRITE; + XSock_Close(pSock); + } +#endif + + return nBytes; +} + +int XSock_WriteBuff(xsock_t *pSock, xbyte_buffer_t *pBuffer) +{ + if (pBuffer == NULL) return XSOCK_NONE; + return XSock_Write(pSock, pBuffer->pData, pBuffer->nUsed); +} + +int XSock_SendBuff(xsock_t *pSock, xbyte_buffer_t *pBuffer) +{ + if (pBuffer == NULL) return XSOCK_NONE; + return XSock_Send(pSock, pBuffer->pData, pBuffer->nUsed); +} + +XSOCKET XSock_Accept(xsock_t *pSock, xsock_t *pNewSock) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + xsocklen_t len = sizeof(xsocklen_t); + + xsock_type_t eType = XSock_IsSSL(pSock) ? + (pSock->eType == XSOCK_SSLV2_SERVER ? + XSOCK_SSLV2_PEER : XSOCK_SSLV3_PEER) : pSock->eType; + + if (XSock_Init(pNewSock, eType, XSOCK_INVALID, XFALSE) < 0) return XSOCK_INVALID; + pNewSock->nFD = accept(pSock->nFD, (struct sockaddr*)&pNewSock->inAddr, &len); + + if (pNewSock->nFD == XSOCK_INVALID) + { + pSock->eStatus = XSOCK_ERR_ACCEPT; + XSock_Close(pNewSock); + return XSOCK_INVALID; + } + +#ifdef _XUTILS_USE_SSL + SSL_CTX* pSSLCtx = XSock_GetSSLCTX(pSock); + if (pSock->nSSL && pSSLCtx != NULL) + { + SSL *pSSL = SSL_new(pSSLCtx); + if (pSSL == NULL) + { + XSock_Close(pNewSock); + pSock->eStatus = XSOCK_ERR_SSLNEW; + return XSOCK_INVALID; + } + + SSL_set_accept_state(pSSL); + SSL_set_fd(pSSL, pNewSock->nFD); + + if (SSL_accept(pSSL) <= 0) + { + if (pSSL != NULL) SSL_free(pSSL); + pSock->eStatus = XSOCK_ERR_SSLACC; + + XSock_Close(pNewSock); + return XSOCK_INVALID; + } + + return XSock_SetSSL(pNewSock, pSSL); + } +#endif + + return pNewSock->nFD; +} + +XSOCKET XSock_AcceptNB(xsock_t *pSock) +{ +#ifdef _XUTILS_USE_GNU + if (!XSock_Check(pSock)) return XSOCK_INVALID; + xsocklen_t len = sizeof(struct sockaddr); + pSock->nNB = 1; + + XSOCKET nFD = accept4(pSock->nFD, (struct sockaddr *) &pSock->inAddr, &len, pSock->nNB); + if (nFD < 0) + { + pSock->eStatus = XSOCK_ERR_ACCEPT; + pSock->nFD = XSOCK_INVALID; + pSock->nNB = 0; + } + + return nFD; +#endif + + (void)pSock; + return XSOCK_INVALID; +} + +XSTATUS XSock_MsgPeek(xsock_t *pSock) +{ + if (!XSock_Check(pSock)) return XSOCK_ERROR; + unsigned char buf; + int nFlags = MSG_PEEK | XMSG_DONTWAIT; + int nByte = recv(pSock->nFD, &buf, 1, nFlags); + return nByte < 0 ? XSOCK_NONE : XSOCK_SUCCESS; +} + +uint32_t XSock_NetAddr(const char *pAddr) +{ + if (pAddr == NULL) return htonl(INADDR_ANY); + struct in_addr addr; + int nStatus = inet_pton(AF_INET, pAddr, &addr); + return (nStatus <= 0) ? 0 : (uint32_t)addr.s_addr; +} + +size_t XSock_IPStr(const uint32_t nAddr, char *pStr, size_t nSize) +{ + return xstrncpyf(pStr, nSize, "%d.%d.%d.%d", + (int)((nAddr & 0x000000FF)), + (int)((nAddr & 0x0000FF00)>>8), + (int)((nAddr & 0x00FF0000)>>16), + (int)((nAddr & 0xFF000000)>>24)); +} + +size_t XSock_SinAddr(const struct in_addr inAddr, char *pAddr, size_t nSize) +{ + return XSock_IPStr(inAddr.s_addr, pAddr, nSize); +} + +size_t XSock_IPAddr(xsock_t *pSock, char *pAddr, size_t nSize) +{ + struct sockaddr_in *pInAddr = &pSock->inAddr; + return XSock_SinAddr(pInAddr->sin_addr, pAddr, nSize); +} + +XSTATUS XSock_AddrInfo(xsock_addr_t *pAddr, xsock_family_t eFam, const char *pHost) +{ + struct addrinfo hints, *rp, *res = NULL; + int nRetVal = XSOCK_ERROR; + void *ptr = NULL; + + memset(&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + hints.ai_flags |= AI_CANONNAME; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if (getaddrinfo(pHost, NULL, &hints, &res)) return nRetVal; + xstrncpy(pAddr->sHost, sizeof(pAddr->sHost), pHost); + pAddr->eFamily = XF_UNDEF; + + for (rp = res; rp != NULL; rp = rp->ai_next) + { + inet_ntop(res->ai_family, res->ai_addr->sa_data, pAddr->sAddr, sizeof(pAddr->sAddr)); + if (xstrused(pAddr->sAddr)) nRetVal = XSOCK_NONE; + + if (eFam == XF_IPV4 && rp->ai_family == AF_INET) + { + ptr = &((struct sockaddr_in*) res->ai_addr)->sin_addr; + pAddr->eFamily = XF_IPV4; + } +#ifndef _WIN32 + else if (eFam == XF_IPV6 && rp->ai_family == AF_INET6) + { + ptr = &((struct sockaddr_in6*)res->ai_addr)->sin6_addr; + pAddr->eFamily = XF_IPV6; + } +#endif + + if (ptr != NULL) + { + if (inet_ntop(res->ai_family, ptr, pAddr->sAddr, sizeof(pAddr->sAddr)) == NULL) + { + freeaddrinfo(res); + return nRetVal; + } + + xstrncpy(pAddr->sName, sizeof(pAddr->sName), res->ai_canonname); + pAddr->nAddr = XSock_NetAddr(pAddr->sAddr); + pAddr->nPort = 0; + + nRetVal = XSOCK_SUCCESS; + break; + } + } + + freeaddrinfo(res); + return nRetVal; +} + +void XSock_InitAddr(xsock_addr_t *pAddr) +{ + pAddr->sHost[0] = XSTR_NUL; + pAddr->sName[0] = XSTR_NUL; + pAddr->sAddr[0] = XSTR_NUL; + pAddr->nAddr = XSTDNON; + pAddr->nPort = XSTDNON; + pAddr->eFamily = XF_UNDEF; +} + +XSTATUS XSock_GetAddr(xsock_addr_t *pAddr, const char *pHost) +{ + XSock_InitAddr(pAddr); + if (pHost == NULL) return XSOCK_ERROR; + + char sHost[XSOCK_INFO_MAX + XSOCK_ADDR_MAX]; + xstrncpy(sHost, sizeof(sHost), pHost); + + char *savePtr = NULL; + char *ptr = xstrtok(sHost, ":", &savePtr); + if (ptr == NULL) return XSOCK_ERROR; + + int nStatus = XSock_AddrInfo(pAddr, XF_IPV4, ptr); + if (nStatus <= 0) return XSOCK_ERROR; + + ptr = xstrtok(NULL, ":", &savePtr); + if (ptr != NULL) pAddr->nPort = (uint16_t)atoi(ptr); + return pAddr->nPort ? XSOCK_SUCCESS : XSOCK_NONE; +} + +XSTATUS XSock_Addr(xsock_addr_t *pInfo, struct sockaddr_in *pAddr, size_t nSize) +{ + XSock_InitAddr(pInfo); + pInfo->eFamily = XF_IPV4; + + struct hostent *pHostInfo = gethostbyaddr((char*)&pAddr->sin_addr.s_addr, (int)nSize, AF_INET); + if (pHostInfo != NULL) xstrncpy(pInfo->sName, sizeof(pInfo->sName), pHostInfo->h_name); + + XSock_IPStr(pAddr->sin_addr.s_addr, pInfo->sAddr, sizeof(pInfo->sAddr)); + return (pHostInfo != NULL) ? XSOCK_SUCCESS : XSOCK_NONE; +} + +XSOCKET XSock_NonBlock(xsock_t *pSock, xbool_t nNonBlock) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + +#ifdef _WIN32 + unsigned long nOpt = (unsigned long)nNonBlock; + int nRes = ioctlsocket(pSock->nFD, FIONBIO, &nOpt); + if (nRes != NO_ERROR) + { + pSock->eStatus = XSOCK_ERR_SETFL; + XSock_Close(pSock); + return XSOCK_INVALID; + } +#else + /* Get flags */ + int fl = fcntl(pSock->nFD, F_GETFL); + if (fl < 0) + { + pSock->eStatus = XSOCK_ERR_GETFL; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (nNonBlock) + { + /* Set flag */ + fl = fcntl(pSock->nFD, F_SETFL, fl | O_NONBLOCK); + if (fl < 0) + { + pSock->eStatus = XSOCK_ERR_SETFL; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } + else + { + fl = fcntl(pSock->nFD, F_SETFL, fl & (~O_NONBLOCK)); + if (fl < 0) + { + pSock->eStatus = XSOCK_ERR_SETFL; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } +#endif + + pSock->nNB = nNonBlock; + return pSock->nFD; +} + +XSOCKET XSock_TimeOutR(xsock_t *pSock, int nSec, int nUsec) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + struct timeval tmout; + tmout.tv_sec = nSec; + tmout.tv_usec = nUsec; + +#ifdef _WIN32 + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tmout, sizeof(tmout)) < 0) +#else + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_RCVTIMEO, (struct timeval*)&tmout, sizeof(tmout)) < 0) +#endif + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_TimeOutS(xsock_t *pSock, int nSec, int nUsec) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + struct timeval tmout; + tmout.tv_sec = nSec; + tmout.tv_usec = nUsec; + +#ifdef _WIN32 + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tmout, sizeof(tmout)) < 0) +#else + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_SNDTIMEO, (struct timeval*)&tmout, sizeof(tmout)) < 0) +#endif + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_ReuseAddr(xsock_t *pSock, xbool_t nEnabled) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + unsigned int nOpt = (unsigned int)nEnabled; + + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_REUSEADDR, (char*)&nOpt, sizeof(nOpt)) < 0) + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_Linger(xsock_t *pSock, int nSec) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + struct linger lopt; + lopt.l_linger = nSec; + lopt.l_onoff = 1; + +#ifdef _WIN32 + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_LINGER, (const char*)&lopt, sizeof(lopt)) < 0) +#else + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_LINGER, (struct linger*)&lopt, sizeof(lopt)) < 0) +#endif + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_Oobinline(xsock_t *pSock, xbool_t nEnabled) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + unsigned int nOpt = (unsigned int)nEnabled; + + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_OOBINLINE, (char*)&nOpt, sizeof nOpt) < 0) + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_NoDelay(xsock_t *pSock, xbool_t nEnabled) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + unsigned int nOpt = (unsigned int)nEnabled; + + if (setsockopt(pSock->nFD, IPPROTO_TCP, TCP_NODELAY, (char*)&nOpt, sizeof(nOpt)) < 0) + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + } + + return pSock->nFD; +} + +XSOCKET XSock_Bind(xsock_t *pSock) +{ + if (bind(pSock->nFD, (struct sockaddr*)&(pSock->inAddr), sizeof(pSock->inAddr)) < 0) + { + pSock->eStatus = XSOCK_ERR_BIND; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + return pSock->nFD; +} + +XSOCKET XSock_AddMembership(xsock_t* pSock, const char* pGroup) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + + struct ip_mreq mreq; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + mreq.imr_multiaddr.s_addr = XSock_NetAddr(pGroup); + + /* Join to multicast group */ +#ifdef _WIN32 + if (setsockopt(pSock->nFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0) +#else + if (setsockopt(pSock->nFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (struct ip_mreq*)&mreq, sizeof(mreq)) < 0) +#endif + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + return pSock->nFD; +} + +XSTATUS XSock_LoadPKCS12(xsocket_ssl_cert_t *pCert, const char *p12Path, const char *p12Pass) +{ + pCert->nStatus = 0; +#ifdef _XUTILS_USE_SSL + FILE *p12File = fopen(p12Path, "rb"); + if (p12File == NULL) return XSOCK_ERROR; + + PKCS12 *p12 = d2i_PKCS12_fp(p12File, NULL); + fclose(p12File); + if (p12 == NULL) return XSOCK_ERROR; + + STACK_OF(X509) *pCa = NULL; + EVP_PKEY *pKey = NULL; + X509 *pXCert = NULL; + + if (!PKCS12_parse(p12, p12Pass, &pKey, &pXCert, &pCa)) + { + PKCS12_free(p12); + return XSOCK_ERROR; + } + + pCert->pCert = pXCert; + pCert->pKey = pKey; + pCert->pCa = pCa; + + PKCS12_free(p12); + pCert->nStatus = 1; + return XSOCK_SUCCESS; +#else + (void)p12Path; + (void)p12Pass; +#endif + + return XSOCK_NONE; +} + +XSOCKET XSock_SetSSLCert(xsock_t *pSock, xsock_cert_t *pCert) +{ +#ifdef _XUTILS_USE_SSL + SSL_CTX *pSSLCtx = XSock_GetSSLCTX(pSock); + if (pSSLCtx == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLINV; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX_set_ecdh_auto(pSSLCtx, 1); + if (pCert->nVerifyFlags > 0) SSL_CTX_set_verify(pSSLCtx, pCert->nVerifyFlags, NULL); + + if (pCert->pCaPath != NULL) + { + if (SSL_CTX_load_verify_locations(pSSLCtx, pCert->pCaPath, NULL) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLCA; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX_set_client_CA_list(pSSLCtx, SSL_load_client_CA_file(pCert->pCaPath)); + } + + if (pCert->p12Path != NULL) + { + xsocket_ssl_cert_t sslCert; + if (!XSock_LoadPKCS12(&sslCert, pCert->p12Path, pCert->p12Pass)) + { + pSock->eStatus = XSOCK_ERR_PKCS12; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + EVP_PKEY *pKey = (EVP_PKEY *)sslCert.pKey; + X509 *pCert = (X509*)sslCert.pCert; + + if (sslCert.pCert != NULL && SSL_CTX_use_certificate(pSSLCtx, pCert) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLCRT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (sslCert.pKey != NULL && SSL_CTX_use_PrivateKey(pSSLCtx, pKey) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLKEY; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } + else + { + if (pCert->pCertPath != NULL && SSL_CTX_use_certificate_file(pSSLCtx, pCert->pCertPath, SSL_FILETYPE_PEM) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLCRT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (pCert->pKeyPath != NULL && SSL_CTX_use_PrivateKey_file(pSSLCtx, pCert->pKeyPath, SSL_FILETYPE_PEM) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLKEY; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (pCert->pCaPath != NULL && SSL_CTX_use_certificate_chain_file(pSSLCtx, pCert->pCaPath) <= 0) + { + pSock->eStatus = XSOCK_ERR_SSLCA; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } + + return pSock->nFD; +#else + (void)pCert; +#endif + + pSock->eStatus = XSOCK_ERR_NOSSL; + XSock_Close(pSock); + return XSOCK_INVALID; +} + +XSOCKET XSock_InitSSLServer(xsock_t *pSock) +{ +#ifdef _XUTILS_USE_SSL + const SSL_METHOD *pMethod = XSock_GetSSLMethod(pSock); + if (pMethod == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLMET; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX *pSSLCtx = SSL_CTX_new(pMethod); + if (pSSLCtx == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLCTX; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX_set_verify(pSSLCtx, SSL_VERIFY_NONE, NULL); + return XSock_SetSSLCTX(pSock, pSSLCtx); +#endif + + pSock->eStatus = XSOCK_ERR_NOSSL; + XSock_Close(pSock); + return XSOCK_INVALID; +} + +XSOCKET XSock_InitSSLClient(xsock_t *pSock) +{ +#ifdef _XUTILS_USE_SSL + const SSL_METHOD *pMethod = XSock_GetSSLMethod(pSock); + if (pMethod == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLMET; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX *pSSLCtx = SSL_CTX_new(pMethod); + if (pSSLCtx == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLCTX; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_CTX_set_verify(pSSLCtx, SSL_VERIFY_NONE, NULL); + SSL *pSSL = SSL_new(pSSLCtx); + + if (pSSL == NULL) + { + pSock->eStatus = XSOCK_ERR_SSLNEW; + SSL_CTX_free(pSSLCtx); + XSock_Close(pSock); + return XSOCK_INVALID; + } + + SSL_set_connect_state(pSSL); + SSL_set_fd(pSSL, pSock->nFD); + + if (SSL_connect(pSSL) < 0) + { + if (pSSL != NULL) SSL_free(pSSL); + pSock->eStatus = XSOCK_ERR_SSLCNT; + + SSL_CTX_free(pSSLCtx); + XSock_Close(pSock); + return XSOCK_INVALID; + } + + XSock_SetSSLCTX(pSock, pSSLCtx); + XSock_SetSSL(pSock, pSSL); + return pSock->nFD; +#endif + + pSock->eStatus = XSOCK_ERR_NOSSL; + XSock_Close(pSock); + return XSOCK_INVALID; +} + +static XSOCKET XSock_SetupTCP(xsock_t *pSock) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + if (pSock->eType == XSOCK_SSLV2_SERVER || + pSock->eType == XSOCK_SSLV3_SERVER || + pSock->eType == XSOCK_TCP_SERVER) + { + if (XSock_Bind(pSock) == XSOCK_INVALID) return XSOCK_INVALID; + + if (listen(pSock->nFD, (int)pSock->nFdMax) < 0) + { + pSock->eStatus = XSOCK_ERR_LISTEN; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (pSock->eType == XSOCK_SSLV2_SERVER || + pSock->eType == XSOCK_SSLV3_SERVER) + XSock_InitSSLServer(pSock); + } + else if (pSock->eType == XSOCK_SSLV2_CLIENT || + pSock->eType == XSOCK_SSLV3_CLIENT || + pSock->eType == XSOCK_TCP_CLIENT) + { + if (connect(pSock->nFD, (struct sockaddr *)&pSock->inAddr, sizeof(pSock->inAddr)) < 0) + { + pSock->eStatus = XSOCK_ERR_CONNECT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + + if (pSock->eType == XSOCK_SSLV2_CLIENT || + pSock->eType == XSOCK_SSLV3_CLIENT) + XSock_InitSSLClient(pSock); + } + + return pSock->nFD; +} + +static XSOCKET XSock_SetupUDP(xsock_t *pSock) +{ + if (!XSock_Check(pSock)) return XSOCK_INVALID; + int nEnableFlag = 1; + + if (pSock->eType == XSOCK_UDP_BCAST) + { + if (setsockopt(pSock->nFD, SOL_SOCKET, SO_BROADCAST, (char*)&nEnableFlag, sizeof nEnableFlag) < 0) + { + pSock->eStatus = XSOCK_ERR_SETOPT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } + else if (pSock->eType == XSOCK_UDP_CLIENT) + { + if (connect(pSock->nFD, (struct sockaddr *)&pSock->inAddr, sizeof(pSock->inAddr)) < 0) + { + pSock->eStatus = XSOCK_ERR_CONNECT; + XSock_Close(pSock); + return XSOCK_INVALID; + } + } + else if (pSock->eType == XSOCK_UDP_MCAST) + { + if (XSock_ReuseAddr(pSock, 1) == XSOCK_INVALID) return XSOCK_INVALID; + else if (XSock_Bind(pSock) == XSOCK_INVALID) return XSOCK_INVALID; + else if (XSock_AddMembership(pSock, NULL) == XSOCK_INVALID) return XSOCK_INVALID; + } + + return pSock->nFD; +} + +XSOCKET XSock_CreateRAW(xsock_t *pSock) +{ + XSock_Init(pSock, XSOCK_RAW, XSOCK_INVALID, 0); + pSock->nFD = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (pSock->nFD == XSOCK_INVALID) pSock->eStatus = XSOCK_ERR_CREATE; + return pSock->nFD; +} + +XSOCKET XSock_CreateAdv(xsock_t *pSock, xsock_type_t eType, size_t nFdMax, const char *pAddr, uint16_t nPort) +{ + int nStatus = XSock_Init(pSock, eType, XSOCK_INVALID, 0); + if (nStatus == XSOCK_ERROR) return XSOCK_INVALID; + + if (pSock->eType != XSOCK_RAW) + { + pSock->nFdMax = XSTD_FIRSTOF(nFdMax, XSOCK_FD_MAX); + pSock->nAddr = XSock_NetAddr(pAddr); + pSock->nPort = nPort; + + pSock->inAddr.sin_addr.s_addr = pSock->nAddr; + pSock->inAddr.sin_port = htons(pSock->nPort); + pSock->inAddr.sin_family = AF_INET; + } + + pSock->nFD = socket(AF_INET, pSock->nType | FD_CLOEXEC, pSock->nProto); + if (pSock->nFD == XSOCK_INVALID) + { + pSock->eStatus = XSOCK_ERR_CREATE; + return XSOCK_INVALID; + } + + if (pSock->nType == SOCK_STREAM) XSock_SetupTCP(pSock); + else if (pSock->nType == SOCK_DGRAM) XSock_SetupUDP(pSock); + + return pSock->nFD; +} + +XSOCKET XSock_Create(xsock_t *pSock, xsock_type_t eType, const char *pAddr, uint16_t nPort) +{ + return XSock_CreateAdv(pSock, eType, 0, pAddr, nPort); +} + +XSOCKET XSock_Open(xsock_t *pSock, xsock_type_t eType, xsock_addr_t *pAddr) +{ + if (pAddr->sAddr[0] == XSTR_NUL || !pAddr->nPort) + { + pSock->eStatus = XSOCK_ERR_CREATE; + pSock->nFD = XSOCK_INVALID; + return XSOCK_INVALID; + } + + return XSock_Create(pSock, eType, pAddr->sAddr, pAddr->nPort); +} + +XSOCKET XSock_Setup(xsock_t *pSock, xsock_type_t eType, const char *pAddr) +{ + xsock_addr_t addrInfo; + + if (XSock_GetAddr(&addrInfo, pAddr) <= 0) + { + pSock->eStatus = XSOCK_ERR_ADDR; + pSock->nFD = XSOCK_INVALID; + return XSOCK_INVALID; + } + + return XSock_Open(pSock, eType, &addrInfo); +} + +xsock_t* XSock_Alloc(xsock_type_t eType, const char *pAddr, uint16_t nPort) +{ + xsock_t *pSock = (xsock_t*)malloc(sizeof(xsock_t)); + if (!pSock) return NULL; + + XSock_Create(pSock, eType, pAddr, nPort); + return pSock; +} + +xsock_t* XSock_New(xsock_type_t eType, xsock_addr_t *pAddr) +{ + if (strlen(pAddr->sAddr) <= 0 || pAddr->nPort == 0) return NULL; + return XSock_Alloc(eType, pAddr->sAddr, pAddr->nPort); +} + +void XSock_Free(xsock_t *pSock) +{ + if (pSock != NULL) + { + XSock_Close(pSock); + free(pSock); + } +} diff --git a/src/sock.h b/src/sock.h new file mode 100644 index 0000000..60ddc4f --- /dev/null +++ b/src/sock.h @@ -0,0 +1,267 @@ +/*! + * @file libxutils/src/sock.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Cross-plaform socket operations such as + * create, bind, connect, listen, select and etc. + */ + +#ifndef __XUTILS_XSOCK_H__ +#define __XUTILS_XSOCK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "xbuf.h" +#include "xtype.h" + +#ifdef _XUTILS_USE_SSL +#include +#include +#include +#include +#include +#include +#include +#endif + +/* MacOS Compatibility */ +#ifdef DARWIN +#include +#define SOCK_NONBLOCK O_NONBLOCK +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +#ifdef _WIN32 +typedef SOCKET XSOCKET; +#define XSHUT_RDWR SD_BOTH +#define XSOCK_INVALID INVALID_SOCKET +#define XMSG_NOSIGNAL 0 +#define XMSG_DONTWAIT 0 +#else +typedef int XSOCKET; +#define XSHUT_RDWR SHUT_RDWR +#define XMSG_NOSIGNAL MSG_NOSIGNAL +#define XMSG_DONTWAIT MSG_DONTWAIT +#define XSOCK_INVALID -1 +#endif + +#define XSOCK_SUCCESS XSTDOK +#define XSOCK_ERROR XSTDERR +#define XSOCK_NONE XSTDNON + +/* Limits */ +#define XSOCK_CHUNK_MAX 1024 * 32 +#define XSOCK_RX_MAX 1024 * 8 +#define XSOCK_FD_MAX 120000 +#define XSOCK_INFO_MAX 256 +#define XSOCK_ADDR_MAX 128 + +typedef struct sockaddr_in xsock_inaddr_t; + +/* Socket errors */ +typedef enum { + XSOCK_ERR_NONE = (uint8_t)0, + XSOCK_ERR_ALLOC, + XSOCK_ERR_INVALID, + XSOCK_ERR_SUPPORT, + XSOCK_ERR_CONNECT, + XSOCK_ERR_CREATE, + XSOCK_ERR_ACCEPT, + XSOCK_ERR_LISTEN, + XSOCK_ERR_WRITE, + XSOCK_ERR_READ, + XSOCK_ERR_SEND, + XSOCK_ERR_RECV, + XSOCK_ERR_JOIN, + XSOCK_ERR_BIND, + XSOCK_ERR_ADDR, + XSOCK_ERR_SETFL, + XSOCK_ERR_GETFL, + XSOCK_ERR_SETOPT, + XSOCK_ERR_PKCS12, + XSOCK_ERR_SSLWRITE, + XSOCK_ERR_SSLREAD, + XSOCK_ERR_SSLINV, + XSOCK_ERR_SSLNEW, + XSOCK_ERR_SSLCTX, + XSOCK_ERR_SSLMET, + XSOCK_ERR_SSLCNT, + XSOCK_ERR_SSLACC, + XSOCK_ERR_SSLKEY, + XSOCK_ERR_SSLCRT, + XSOCK_ERR_SSLERR, + XSOCK_ERR_SSLCA, + XSOCK_ERR_NOSSL, + XSOCK_EOF +} xsock_status_t; + +typedef enum { + XF_UNDEF = (uint8_t)0, + XF_IPV4 = 4, + XF_IPV6 = 6 +} xsock_family_t; + +/* Supported socket types */ +typedef enum { + XSOCK_RAW = (uint8_t)0, + XSOCK_TCP_PEER, + XSOCK_TCP_CLIENT, + XSOCK_TCP_SERVER, + + XSOCK_SSL_PREFERED_CLIENT, + XSOCK_SSL_PREFERED_SERVER, + XSOCK_SSLV2_CLIENT, + XSOCK_SSLV2_SERVER, + XSOCK_SSLV3_CLIENT, + XSOCK_SSLV3_SERVER, + XSOCK_SSLV2_PEER, + XSOCK_SSLV3_PEER, + + XSOCK_UDP_CLIENT, + XSOCK_UDP_MCAST, + XSOCK_UDP_BCAST, + XSOCK_UDP_UCAST, + XSOCK_UNDEFINED +} xsock_type_t; + +typedef struct XSocketAddr { + xsock_family_t eFamily; + uint32_t nAddr; + uint16_t nPort; + char sAddr[XSOCK_ADDR_MAX]; + char sHost[XSOCK_INFO_MAX]; + char sName[XSOCK_INFO_MAX]; +} xsock_addr_t; + +typedef struct XSocketSSLCert { + uint8_t nStatus; + void *pCert; + void *pKey; + void *pCa; +} xsocket_ssl_cert_t; + +typedef struct XSocketCert { + const char *pCertPath; + const char *pKeyPath; + const char *pCaPath; + const char *p12Path; + const char *p12Pass; + int nVerifyFlags; +} xsock_cert_t; + +/* XSocket */ +typedef struct XSocket { + xsock_status_t eStatus; + xsock_inaddr_t inAddr; + xsock_type_t eType; + + uint32_t nAddr; + uint16_t nPort; + + size_t nFdMax; + xbool_t nSSL; + xbool_t nNB; + + XSOCKET nFD; + int nProto; + int nType; + + void *pPrivate; +} xsock_t; + +const char* XSock_GetStatusStr(xsock_status_t eStatus); +const char* XSock_ErrStr(xsock_t* pSock); + +#ifdef _XSOCK_USE_SSL +SSL_CTX* XSock_GetSSLCTX(xsock_t *pSock); +SSL* XSock_GetSSL(xsock_t *pSock); +#endif + +xsock_inaddr_t* XSock_GetInAddr(xsock_t *pSock); +xsock_status_t XSock_Status(const xsock_t *pSock); +xsock_type_t XSock_GetType(const xsock_t *pSock); +xbool_t XSockType_IsSSL(xsock_type_t eType); + +uint32_t XSock_GetNetAddr(const xsock_t *pSock); +uint16_t XSock_GetPort(const xsock_t *pSock); +size_t XSock_GetFDMax(const xsock_t *pSock); +int XSock_GetSockType(const xsock_t *pSock); +int XSock_GetProto(const xsock_t *pSock); + +XSOCKET XSock_GetFD(const xsock_t *pSock); +xbool_t XSock_IsSSL(const xsock_t *pSock); +xbool_t XSock_IsNB(const xsock_t *pSock); + +int xclosesock(XSOCKET nFd); +void XSock_Close(xsock_t* pSock); + +XSTATUS XSock_MsgPeek(xsock_t* pSock); +XSTATUS XSock_IsOpen(xsock_t* pSock); +XSTATUS XSock_Check(xsock_t* pSock); +XSTATUS XSock_Init(xsock_t* pSock, xsock_type_t eType, XSOCKET nFD, xbool_t nNB); +XSTATUS XSock_SetType(xsock_t* pSock, xsock_type_t eType); + +void XSock_InitSSL(void); +void XSock_DeinitSSL(void); +int XSock_LastSSLError(char* pDst, size_t nSize); + +XSTATUS XSock_LoadPKCS12(xsocket_ssl_cert_t* pCert, const char* p12Path, const char* p12Pass); +XSOCKET XSock_SetSSLCert(xsock_t* pSock, xsock_cert_t* pCert); +XSOCKET XSock_InitSSLServer(xsock_t* pSock); +XSOCKET XSock_InitSSLClient(xsock_t* pSock); + +int XSock_SSLRead(xsock_t* pSock, void* pData, size_t nSize, xbool_t nExact); +int XSock_SSLWrite(xsock_t* pSock, const void* pData, size_t nLength); + +int XSock_WriteBuff(xsock_t *pSock, xbyte_buffer_t *pBuffer); +int XSock_SendBuff(xsock_t *pSock, xbyte_buffer_t *pBuffer); +int XSock_SendChunk(xsock_t* pSock, void* pData, size_t nLength); +int XSock_RecvChunk(xsock_t* pSock, void* pData, size_t nSize); +int XSock_Send(xsock_t* pSock, const void* pData, size_t nLength); +int XSock_Write(xsock_t* pSock, const void* pData, size_t nLength); +int XSock_Read(xsock_t* pSock, void* pData, size_t nSize); +int XSock_Recv(xsock_t* pSock, void* pData, size_t nSize); + +XSOCKET XSock_Accept(xsock_t* pSock, xsock_t* pNewSock); +XSOCKET XSock_AcceptNB(xsock_t* pSock); + +uint32_t XSock_NetAddr(const char* pAddr); +size_t XSock_SinAddr(const struct in_addr inAddr, char* pAddr, size_t nSize); +size_t XSock_IPAddr(xsock_t* pSock, char* pAddr, size_t nSize); +size_t XSock_IPStr(const uint32_t nAddr, char* pStr, size_t nSize); + +void XSock_InitAddr(xsock_addr_t* pAddr); +XSTATUS XSock_AddrInfo(xsock_addr_t* pAddr, xsock_family_t eFam, const char* pHost); +XSTATUS XSock_GetAddr(xsock_addr_t* pAddr, const char* pHost); +XSTATUS XSock_Addr(xsock_addr_t* pInfo, struct sockaddr_in* pAddr, size_t nSize); + +XSOCKET XSock_AddMembership(xsock_t* pSock, const char* pGroup); +XSOCKET XSock_ReuseAddr(xsock_t* pSock, xbool_t nEnabled); +XSOCKET XSock_Oobinline(xsock_t* pSock, xbool_t nEnabled); +XSOCKET XSock_NonBlock(xsock_t* pSock, xbool_t nNonBlock); +XSOCKET XSock_NoDelay(xsock_t* pSock, xbool_t nEnabled); +XSOCKET XSock_TimeOutR(xsock_t* pSock, int nSec, int nUsec); +XSOCKET XSock_TimeOutS(xsock_t* pSock, int nSec, int nUsec); +XSOCKET XSock_Linger(xsock_t* pSock, int nSec); +XSOCKET XSock_Bind(xsock_t *pSock); + +XSOCKET XSock_CreateAdv(xsock_t* pSock, xsock_type_t eType, size_t nFdMax, const char* pAddr, uint16_t nPort); +XSOCKET XSock_Create(xsock_t* pSock, xsock_type_t eType, const char* pAddr, uint16_t nPort); +XSOCKET XSock_Open(xsock_t* pSock, xsock_type_t eType, xsock_addr_t* pAddr); +XSOCKET XSock_Setup(xsock_t* pSock, xsock_type_t eType, const char* pAddr); +XSOCKET XSock_CreateRAW(xsock_t* pSock); + +xsock_t* XSock_Alloc(xsock_type_t eType, const char* pAddr, uint16_t nPort); +xsock_t* XSock_New(xsock_type_t eType, xsock_addr_t* pAddr); +void XSock_Free(xsock_t* pSock); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XSOCK_H__ */ diff --git a/src/sync.c b/src/sync.c new file mode 100644 index 0000000..0c72fdb --- /dev/null +++ b/src/sync.c @@ -0,0 +1,243 @@ +/*! + * @file libxutils/src/sync.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the various cross-platform + * sync functionality with the POSIX mutex and SynchAPI + * critical section, atomic builtins, rwlocks, and etc. + */ + +#include "sync.h" + +#if !defined(_WIN32) && !defined(PTHREAD_MUTEX_RECURSIVE) +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#endif + +void xusleep(uint32_t nUsecs) +{ +#ifdef _WIN32 + DWORD nMilliSec = 0; + if (nUsecs < 1000) nMilliSec = 1; + else nMilliSec = nUsecs / 1000; + Sleep(nMilliSec); +#else + usleep(nUsecs); +#endif +} + +void XSync_Init(xsync_mutex_t *pSync) +{ +#ifndef _WIN32 + pthread_mutexattr_t mutexAttr; + if (pthread_mutexattr_init(&mutexAttr) || + pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE) || + pthread_mutex_init(&pSync->mutex, &mutexAttr) || + pthread_mutexattr_destroy(&mutexAttr)) + { + fprintf(stderr, "<%s:%d> %s: Can not initialize mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + InitializeCriticalSection(&pSync->mutex); +#endif + + pSync->bEnabled = XTRUE; +} + +void XSync_Destroy(xsync_mutex_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if(pthread_mutex_destroy(&pSync->mutex)) + { + fprintf(stderr, "<%s:%d> %s: Can not deinitialize mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + DeleteCriticalSection(&pSync->mutex); +#endif + } + + pSync->bEnabled = XFALSE; +} + +void XSync_Lock(xsync_mutex_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_mutex_lock(&pSync->mutex)) + { + fprintf(stderr, "<%s:%d> %s: Can not lock mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + EnterCriticalSection(&pSync->mutex); +#endif + } +} + +void XSync_Unlock(xsync_mutex_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_mutex_unlock(&pSync->mutex)) + { + fprintf(stderr, "<%s:%d> %s: Can not unlock mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + LeaveCriticalSection(&pSync->mutex); +#endif + } +} + +void XRWSync_Init(xsync_rw_t *pSync) +{ +#ifndef _WIN32 + if (pthread_rwlock_init(&pSync->rwLock, NULL)) + { + fprintf(stderr, "<%s:%d> %s: Can not init rwlock: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + InitializeSRWLock(&pSync->rwLock); + pSync->bExclusive = XFALSE; +#endif + + pSync->bEnabled = XTRUE; +} + +void XRWSync_ReadLock(xsync_rw_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_rwlock_rdlock(&pSync->rwLock)) + { + fprintf(stderr, "<%s:%d> %s: Can not read lock rwlock: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + AcquireSRWLockShared(&pSync->rwLock); +#endif + } +} + +void XRWSync_WriteLock(xsync_rw_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_rwlock_wrlock(&pSync->rwLock)) + { + fprintf(stderr, "<%s:%d> %s: Can not write lock rwlock: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + AcquireSRWLockExclusive(&pSync->rwLock); + pSync->bExclusive = XTRUE; +#endif + } +} + +void XRWSync_Unlock(xsync_rw_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_rwlock_unlock(&pSync->rwLock)) + { + fprintf(stderr, "<%s:%d> %s: Can not unlock rwlock: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + if (pSync->bExclusive) + { + ReleaseSRWLockExclusive(&pSync->rwLock); + pSync->bExclusive = XFALSE; + } + else ReleaseSRWLockShared(&pSync->rwLock); +#endif + } +} + +void XRWSync_Destroy(xsync_rw_t *pSync) +{ + if (pSync->bEnabled) + { +#ifndef _WIN32 + if (pthread_rwlock_destroy(&pSync->rwLock)) + { + fprintf(stderr, "<%s:%d> %s: Can not destroy rwlock: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +#else + (void)pSync; +#endif + } + + pSync->bEnabled = XFALSE; +} + +void XSyncBar_Bar(xsync_bar_t *pBar) +{ + XSYNC_ATOMIC_SET(&pBar->nBar, 1); + XSYNC_ATOMIC_SET(&pBar->nAck, 0); +} + +void XSyncBar_Ack(xsync_bar_t *pBar) +{ + XSYNC_ATOMIC_SET(&pBar->nAck, 1); +} + +void XSyncBar_Reset(xsync_bar_t *pBar) +{ + XSYNC_ATOMIC_SET(&pBar->nBar, 0); + XSYNC_ATOMIC_SET(&pBar->nAck, 0); +} + +uint8_t XSyncBar_CheckBar(xsync_bar_t *pBar) +{ + return XSYNC_ATOMIC_GET(&pBar->nBar) ? XSTDOK : XSTDNON; +} + +uint8_t XSyncBar_CheckAck(xsync_bar_t *pBar) +{ + return XSYNC_ATOMIC_GET(&pBar->nAck) ? XSTDOK : XSTDNON; +} + +uint32_t XSyncBar_WaitAck(xsync_bar_t *pBar, uint32_t nSleepUsec) +{ + uint32_t nUsecs = 0; + + while (!XSyncBar_CheckAck(pBar)) + { + if (nSleepUsec) xusleep(nSleepUsec); + nUsecs += nSleepUsec; + } + + return nUsecs; +} diff --git a/src/sync.h b/src/sync.h new file mode 100644 index 0000000..42c8505 --- /dev/null +++ b/src/sync.h @@ -0,0 +1,82 @@ +/*! + * @file libxutils/src/sync.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the various cross-platform + * sync functionality with the POSIX mutex and SynchAPI + * critical section, rwlocks, atomic built-ins, and etc. + */ + +#ifndef __XUTILS_XSYNC_H__ +#define __XUTILS_XSYNC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "xtype.h" + +typedef struct XSyncMutex { +#ifdef _WIN32 + CRITICAL_SECTION mutex; +#else + pthread_mutex_t mutex; +#endif + xbool_t bEnabled; +} xsync_mutex_t; + +typedef struct XSyncRW { +#ifdef _WIN32 + xbool_t bExclusive; + SRWLOCK rwLock; +#else + pthread_rwlock_t rwLock; +#endif + xbool_t bEnabled; +} xsync_rw_t; + +typedef struct XSyncBar { + xatomic_t nBar; + xatomic_t nAck; +} xsync_bar_t; + +#ifdef _WIN32 +#define XSYNC_ATOMIC_ADD(dst,val) InterlockedExchangeAdd(dst, val) +#define XSYNC_ATOMIC_SUB(dst,val) InterlockedExchangeSubtract(dst, dst) +#define XSYNC_ATOMIC_SET(dst,val) InterlockedExchange(dst, val) +#define XSYNC_ATOMIC_GET(dst) InterlockedExchangeAdd(dst, 0) +#else +#define XSYNC_ATOMIC_ADD(dst,val) __sync_add_and_fetch(dst, val) +#define XSYNC_ATOMIC_SUB(dst,val) __sync_sub_and_fetch(dst, dst) +#define XSYNC_ATOMIC_SET(dst,val) __sync_lock_test_and_set(dst, val) +#define XSYNC_ATOMIC_GET(dst) __sync_add_and_fetch(dst, 0) +#endif + +void xusleep(uint32_t nUsecs); + +void XSync_Init(xsync_mutex_t *pSync); +void XSync_Destroy(xsync_mutex_t *pSync); +void XSync_Lock(xsync_mutex_t *pSync); +void XSync_Unlock(xsync_mutex_t *pSync); + +void XRWSync_Init(xsync_rw_t *pSync); +void XRWSync_ReadLock(xsync_rw_t *pSync); +void XRWSync_WriteLock(xsync_rw_t *pSync); +void XRWSync_Unlock(xsync_rw_t *pSync); +void XRWSync_Destroy(xsync_rw_t *pSync); + +void XSyncBar_Bar(xsync_bar_t *pBar); +void XSyncBar_Ack(xsync_bar_t *pBar); +void XSyncBar_Reset(xsync_bar_t *pBar); +uint8_t XSyncBar_CheckBar(xsync_bar_t *pBar); +uint8_t XSyncBar_CheckAck(xsync_bar_t *pBar); +uint32_t XSyncBar_WaitAck(xsync_bar_t *pBar, uint32_t nSleepUsec); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XSYNC_H__ */ diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..9459aa6 --- /dev/null +++ b/src/thread.c @@ -0,0 +1,216 @@ +/*! + * @file libxutils/src/thread.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the POSIX thread functionality + */ + +#include "xstd.h" +#include "sync.h" +#include "xcpu.h" +#include "xtype.h" +#include "thread.h" + +#ifdef _WIN32 +static DWORD WINAPI XThread_WinThread(void* pArg) +{ + xthread_t *pThread = (xthread_t*)pArg; + pThread->functionCb(pThread->pArgument); + return 0; +} +#endif + +void XThread_Init(xthread_t *pThread) +{ + pThread->nStackSize = XTHREAD_STACK_SIZE; + pThread->functionCb = NULL; + pThread->pArgument = NULL; + pThread->nDetached = 0; + pThread->nStatus = 0; +} + +int XThread_Run(xthread_t *pThread) +{ + if (pThread->functionCb == NULL) return XSTDNON; + pThread->nStatus = XTHREAD_FAIL; + +#ifdef _WIN32 + pThread->threadId = CreateThread(NULL, pThread->nStackSize, XThread_WinThread, pThread, 0, NULL); + if (pThread->threadId == NULL) return XSTDERR; + if (pThread->nDetached) CloseHandle(pThread->threadId); +#else + pthread_attr_t attr; + if (pthread_attr_init(&attr) || + pthread_attr_setstacksize(&attr, pThread->nStackSize)) + { + fprintf(stderr, "<%s:%d> %s: Can not initialize pthread attribute: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + pthread_attr_destroy(&attr); + return XSTDERR; + } + + if (pThread->nDetached) + { + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + { + fprintf(stderr, "<%s:%d> %s Can not set detache state to the pthread attribute: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + pthread_attr_destroy(&attr); + return XSTDERR; + } + } + + if (pthread_create(&pThread->threadId, &attr, pThread->functionCb, pThread->pArgument)) + { + fprintf(stderr, "<%s:%d> %s Can not create pthread: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + pthread_attr_destroy(&attr); + return XSTDERR; + } + + pthread_attr_destroy(&attr); +#endif + + pThread->nStatus = XTHREAD_SUCCESS; + return XSTDOK; +} + +int XThread_Create(xthread_t *pThread, xthread_cb_t func, void *pArg, uint8_t nDtch) +{ + XThread_Init(pThread); + pThread->functionCb = func; + pThread->pArgument = pArg; + pThread->nDetached = nDtch; + return XThread_Run(pThread); +} + +void* XThread_Join(xthread_t *pThread) +{ + if (pThread->nDetached) return NULL; + void* pReturnValue = NULL; + int nStatus = 0; + +#ifdef _WIN32 + WaitForSingleObject(pThread->threadId, INFINITE); + CloseHandle(pThread->threadId); + pThread->nDetached = 1; + nStatus = 1; +#else + pthread_t nTid = pThread->threadId; + nStatus = pthread_join(nTid, &pReturnValue); +#endif + + return nStatus ? NULL : pReturnValue; +} + +void *XTask_WorkerThread(void *pContext) +{ + xtask_t *pTask = (xtask_t*)pContext; + XSYNC_ATOMIC_SET(&pTask->nStatus, XTASK_STAT_ACTIVE); + + int nCurrentCPUID = XTASK_EMPTY_SET; + xbool_t bIsPaused = XFALSE; + xatomic_t nAction = 0; + + while((nAction = XSYNC_ATOMIC_GET(&pTask->nAction)) != XTASK_CTRL_STOP) + { + xatomic_t nInterval = XSYNC_ATOMIC_GET(&pTask->nIntervalU); + + if (nAction == XTASK_CTRL_PAUSE) + { + if (!bIsPaused) + { + XSYNC_ATOMIC_SET(&pTask->nStatus, XTASK_STAT_PAUSED); + nInterval = nInterval ? nInterval : XTASK_SLEEP_USEC; + bIsPaused = XTRUE; + } + + xusleep((uint32_t)nInterval); + continue; + } + + if (bIsPaused) + { + XSYNC_ATOMIC_SET(&pTask->nStatus, XTASK_STAT_ACTIVE); + bIsPaused = XFALSE; + } + + int nCPUID = XSYNC_ATOMIC_GET(&pTask->nCPUID); + if (nCPUID != nCurrentCPUID) + { + nCurrentCPUID = nCPUID; + int nCPUS[1] = { nCPUID }; + XCPU_SetAffinity(nCPUS, 1, XCPU_CALLER_PID); + } + + if (pTask->callback(pTask->pContext) < 0) break; + if (nInterval) xusleep((uint32_t)nInterval); + } + + XSYNC_ATOMIC_SET(&pTask->nStatus, XTASK_STAT_STOPPED); + return NULL; +} + +int XTask_Start(xtask_t *pTask, xtask_cb_t callback, void *pContext, uint32_t nIntervalU) +{ + if (callback == NULL) return 0; + pTask->callback = callback; + pTask->pContext = pContext; + + XSYNC_ATOMIC_SET(&pTask->nCPUID, XTASK_EMPTY_SET); + XSYNC_ATOMIC_SET(&pTask->nAction, XTASK_CTRL_RELEASE); + XSYNC_ATOMIC_SET(&pTask->nIntervalU, nIntervalU); + + int nStatus = XThread_Create(&pTask->taskTd, XTask_WorkerThread, pTask, 1); + XSYNC_ATOMIC_SET(&pTask->nStatus, nStatus == XSTDOK ? XTASK_STAT_CREATED : XTASK_STAT_FAIL); + + return nStatus; +} + +uint32_t XTask_Wait(xtask_t *pTask, int nEvent, int nIntervalU) +{ + uint32_t nCheckCount = 0; + + while (XSYNC_ATOMIC_GET(&pTask->nStatus) != nEvent) + { + if (nIntervalU < 0) break; + else if (!nIntervalU) continue; + + xusleep((uint32_t)nIntervalU); + nCheckCount++; + } + + return nCheckCount * nIntervalU; +} + +uint32_t XTask_Hold(xtask_t *pTask, int nIntervalU) +{ + XSYNC_ATOMIC_SET(&pTask->nAction, XTASK_CTRL_PAUSE); + return XTask_Wait(pTask, XTASK_STAT_PAUSED, nIntervalU); +} + +uint32_t XTask_Release(xtask_t *pTask, int nIntervalU) +{ + XSYNC_ATOMIC_SET(&pTask->nAction, XTASK_CTRL_RELEASE); + return XTask_Wait(pTask, XTASK_STAT_ACTIVE, nIntervalU); +} + +uint32_t XTask_Stop(xtask_t *pTask, int nIntervalU) +{ + XSYNC_ATOMIC_SET(&pTask->nAction, XTASK_CTRL_STOP); + return XTask_Wait(pTask, XTASK_STAT_STOPPED, nIntervalU); +} + +uint32_t XTask_AssignCPU(xtask_t *pTask, int nCPU, int nIntervalU) +{ + uint32_t nWaitTime = 0; + nWaitTime = XTask_Hold(pTask, nIntervalU); + XSYNC_ATOMIC_SET(&pTask->nCPUID, nCPU); + nWaitTime += XTask_Release(pTask, nIntervalU); + return nWaitTime; +} \ No newline at end of file diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000..133b66c --- /dev/null +++ b/src/thread.h @@ -0,0 +1,80 @@ +/*! + * @file libxutils/src/thread.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the POSIX thread functionality + */ + +#ifndef __XUTILS_XTHREAD_H__ +#define __XUTILS_XTHREAD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "sync.h" +#include "xtype.h" + +#define XTHREAD_STACK_SIZE 409600 +#define XTHREAD_SUCCESS XSTDOK +#define XTHREAD_FAIL XSTDERR + +#define XTASK_SLEEP_USEC 10000 +#define XTASK_EMPTY_SET -1 +#define XTASK_STAT_FAIL 0 +#define XTASK_STAT_IDLE 1 +#define XTASK_STAT_CREATED 3 +#define XTASK_STAT_ACTIVE 4 +#define XTASK_STAT_PAUSED 5 +#define XTASK_STAT_STOPPED 6 +#define XTASK_CTRL_RELEASE 7 +#define XTASK_CTRL_PAUSE 8 +#define XTASK_CTRL_STOP 9 + +typedef void*(*xthread_cb_t)(void*); +typedef int(*xtask_cb_t)(void*); + +typedef struct XThread { + xthread_cb_t functionCb; + void* pArgument; + +#ifdef _WIN32 + HANDLE threadId; +#else + pthread_t threadId; +#endif + + uint32_t nStackSize; + uint8_t nDetached; + int8_t nStatus; +} xthread_t; + +void XThread_Init(xthread_t *pThread); +void* XThread_Join(xthread_t *pThread); +int XThread_Run(xthread_t *pThread); +int XThread_Create(xthread_t *pThread, xthread_cb_t func, void *pArg, uint8_t nDtch); + +typedef struct XTask { + void* pContext; + xtask_cb_t callback; + xthread_t taskTd; + xatomic_t nIntervalU; + xatomic_t nAction; + xatomic_t nStatus; + xatomic_t nCPUID; +} xtask_t; + +int XTask_Start(xtask_t *pTask, xtask_cb_t callback, void *pContext, uint32_t nIntervalU); +uint32_t XTask_Stop(xtask_t *pTask, int nIntervalU); +uint32_t XTask_Hold(xtask_t *pTask, int nIntervalU); +uint32_t XTask_Release(xtask_t *pTask, int nIntervalU); +uint32_t XTask_AssignCPU(xtask_t *pTask, int nCPU, int nIntervalU); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XTHREAD_H__ */ \ No newline at end of file diff --git a/src/xaes.c b/src/xaes.c new file mode 100644 index 0000000..d79f1fb --- /dev/null +++ b/src/xaes.c @@ -0,0 +1,711 @@ +/*! + * @file libxutils/src/xaes.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of Advanced Encryption Standard + * based on FIPS-197 implementation by Christophe Devine. + */ + +#include "xstd.h" +#include "xtype.h" +#include "xaes.h" + +#define XAES_GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ((uint32_t)(b)[(i) ] << 24) \ + | ((uint32_t)(b)[(i) + 1] << 16) \ + | ((uint32_t)(b)[(i) + 2] << 8) \ + | ((uint32_t)(b)[(i) + 3] ); \ +} + +#define XAES_PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i)] = (uint8_t)((n) >> 24); \ + (b)[(i) + 1] = (uint8_t)((n) >> 16); \ + (b)[(i) + 2] = (uint8_t)((n) >> 8); \ + (b)[(i) + 3] = (uint8_t)((n) ); \ +} + +static const uint8_t fwdSBox[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* Forward tables */ +#define XAES_FT \ +\ + X(C6,63,63,A5), X(F8,7C,7C,84), X(EE,77,77,99), X(F6,7B,7B,8D), \ + X(FF,F2,F2,0D), X(D6,6B,6B,BD), X(DE,6F,6F,B1), X(91,C5,C5,54), \ + X(60,30,30,50), X(02,01,01,03), X(CE,67,67,A9), X(56,2B,2B,7D), \ + X(E7,FE,FE,19), X(B5,D7,D7,62), X(4D,AB,AB,E6), X(EC,76,76,9A), \ + X(8F,CA,CA,45), X(1F,82,82,9D), X(89,C9,C9,40), X(FA,7D,7D,87), \ + X(EF,FA,FA,15), X(B2,59,59,EB), X(8E,47,47,C9), X(FB,F0,F0,0B), \ + X(41,AD,AD,EC), X(B3,D4,D4,67), X(5F,A2,A2,FD), X(45,AF,AF,EA), \ + X(23,9C,9C,BF), X(53,A4,A4,F7), X(E4,72,72,96), X(9B,C0,C0,5B), \ + X(75,B7,B7,C2), X(E1,FD,FD,1C), X(3D,93,93,AE), X(4C,26,26,6A), \ + X(6C,36,36,5A), X(7E,3F,3F,41), X(F5,F7,F7,02), X(83,CC,CC,4F), \ + X(68,34,34,5C), X(51,A5,A5,F4), X(D1,E5,E5,34), X(F9,F1,F1,08), \ + X(E2,71,71,93), X(AB,D8,D8,73), X(62,31,31,53), X(2A,15,15,3F), \ + X(08,04,04,0C), X(95,C7,C7,52), X(46,23,23,65), X(9D,C3,C3,5E), \ + X(30,18,18,28), X(37,96,96,A1), X(0A,05,05,0F), X(2F,9A,9A,B5), \ + X(0E,07,07,09), X(24,12,12,36), X(1B,80,80,9B), X(DF,E2,E2,3D), \ + X(CD,EB,EB,26), X(4E,27,27,69), X(7F,B2,B2,CD), X(EA,75,75,9F), \ + X(12,09,09,1B), X(1D,83,83,9E), X(58,2C,2C,74), X(34,1A,1A,2E), \ + X(36,1B,1B,2D), X(DC,6E,6E,B2), X(B4,5A,5A,EE), X(5B,A0,A0,FB), \ + X(A4,52,52,F6), X(76,3B,3B,4D), X(B7,D6,D6,61), X(7D,B3,B3,CE), \ + X(52,29,29,7B), X(DD,E3,E3,3E), X(5E,2F,2F,71), X(13,84,84,97), \ + X(A6,53,53,F5), X(B9,D1,D1,68), X(00,00,00,00), X(C1,ED,ED,2C), \ + X(40,20,20,60), X(E3,FC,FC,1F), X(79,B1,B1,C8), X(B6,5B,5B,ED), \ + X(D4,6A,6A,BE), X(8D,CB,CB,46), X(67,BE,BE,D9), X(72,39,39,4B), \ + X(94,4A,4A,DE), X(98,4C,4C,D4), X(B0,58,58,E8), X(85,CF,CF,4A), \ + X(BB,D0,D0,6B), X(C5,EF,EF,2A), X(4F,AA,AA,E5), X(ED,FB,FB,16), \ + X(86,43,43,C5), X(9A,4D,4D,D7), X(66,33,33,55), X(11,85,85,94), \ + X(8A,45,45,CF), X(E9,F9,F9,10), X(04,02,02,06), X(FE,7F,7F,81), \ + X(A0,50,50,F0), X(78,3C,3C,44), X(25,9F,9F,BA), X(4B,A8,A8,E3), \ + X(A2,51,51,F3), X(5D,A3,A3,FE), X(80,40,40,C0), X(05,8F,8F,8A), \ + X(3F,92,92,AD), X(21,9D,9D,BC), X(70,38,38,48), X(F1,F5,F5,04), \ + X(63,BC,BC,DF), X(77,B6,B6,C1), X(AF,DA,DA,75), X(42,21,21,63), \ + X(20,10,10,30), X(E5,FF,FF,1A), X(FD,F3,F3,0E), X(BF,D2,D2,6D), \ + X(81,CD,CD,4C), X(18,0C,0C,14), X(26,13,13,35), X(C3,EC,EC,2F), \ + X(BE,5F,5F,E1), X(35,97,97,A2), X(88,44,44,CC), X(2E,17,17,39), \ + X(93,C4,C4,57), X(55,A7,A7,F2), X(FC,7E,7E,82), X(7A,3D,3D,47), \ + X(C8,64,64,AC), X(BA,5D,5D,E7), X(32,19,19,2B), X(E6,73,73,95), \ + X(C0,60,60,A0), X(19,81,81,98), X(9E,4F,4F,D1), X(A3,DC,DC,7F), \ + X(44,22,22,66), X(54,2A,2A,7E), X(3B,90,90,AB), X(0B,88,88,83), \ + X(8C,46,46,CA), X(C7,EE,EE,29), X(6B,B8,B8,D3), X(28,14,14,3C), \ + X(A7,DE,DE,79), X(BC,5E,5E,E2), X(16,0B,0B,1D), X(AD,DB,DB,76), \ + X(DB,E0,E0,3B), X(64,32,32,56), X(74,3A,3A,4E), X(14,0A,0A,1E), \ + X(92,49,49,DB), X(0C,06,06,0A), X(48,24,24,6C), X(B8,5C,5C,E4), \ + X(9F,C2,C2,5D), X(BD,D3,D3,6E), X(43,AC,AC,EF), X(C4,62,62,A6), \ + X(39,91,91,A8), X(31,95,95,A4), X(D3,E4,E4,37), X(F2,79,79,8B), \ + X(D5,E7,E7,32), X(8B,C8,C8,43), X(6E,37,37,59), X(DA,6D,6D,B7), \ + X(01,8D,8D,8C), X(B1,D5,D5,64), X(9C,4E,4E,D2), X(49,A9,A9,E0), \ + X(D8,6C,6C,B4), X(AC,56,56,FA), X(F3,F4,F4,07), X(CF,EA,EA,25), \ + X(CA,65,65,AF), X(F4,7A,7A,8E), X(47,AE,AE,E9), X(10,08,08,18), \ + X(6F,BA,BA,D5), X(F0,78,78,88), X(4A,25,25,6F), X(5C,2E,2E,72), \ + X(38,1C,1C,24), X(57,A6,A6,F1), X(73,B4,B4,C7), X(97,C6,C6,51), \ + X(CB,E8,E8,23), X(A1,DD,DD,7C), X(E8,74,74,9C), X(3E,1F,1F,21), \ + X(96,4B,4B,DD), X(61,BD,BD,DC), X(0D,8B,8B,86), X(0F,8A,8A,85), \ + X(E0,70,70,90), X(7C,3E,3E,42), X(71,B5,B5,C4), X(CC,66,66,AA), \ + X(90,48,48,D8), X(06,03,03,05), X(F7,F6,F6,01), X(1C,0E,0E,12), \ + X(C2,61,61,A3), X(6A,35,35,5F), X(AE,57,57,F9), X(69,B9,B9,D0), \ + X(17,86,86,91), X(99,C1,C1,58), X(3A,1D,1D,27), X(27,9E,9E,B9), \ + X(D9,E1,E1,38), X(EB,F8,F8,13), X(2B,98,98,B3), X(22,11,11,33), \ + X(D2,69,69,BB), X(A9,D9,D9,70), X(07,8E,8E,89), X(33,94,94,A7), \ + X(2D,9B,9B,B6), X(3C,1E,1E,22), X(15,87,87,92), X(C9,E9,E9,20), \ + X(87,CE,CE,49), X(AA,55,55,FF), X(50,28,28,78), X(A5,DF,DF,7A), \ + X(03,8C,8C,8F), X(59,A1,A1,F8), X(09,89,89,80), X(1A,0D,0D,17), \ + X(65,BF,BF,DA), X(D7,E6,E6,31), X(84,42,42,C6), X(D0,68,68,B8), \ + X(82,41,41,C3), X(29,99,99,B0), X(5A,2D,2D,77), X(1E,0F,0F,11), \ + X(7B,B0,B0,CB), X(A8,54,54,FC), X(6D,BB,BB,D6), X(2C,16,16,3A) + +#define X(a,b,c,d) 0x##a##b##c##d +static const uint32_t XAES_FT0[256] = { XAES_FT }; +#undef X + +#define X(a,b,c,d) 0x##d##a##b##c +static const uint32_t XAES_FT1[256] = { XAES_FT }; +#undef X + +#define X(a,b,c,d) 0x##c##d##a##b +static const uint32_t XAES_FT2[256] = { XAES_FT }; +#undef X + +#define X(a,b,c,d) 0x##b##c##d##a +static const uint32_t XAES_FT3[256] = { XAES_FT }; +#undef X + +#undef FT + +/* Reverse S-box */ +static const uint8_t rvSBox[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* Reverse tables */ +#define XAES_RT \ +\ + X(51,F4,A7,50), X(7E,41,65,53), X(1A,17,A4,C3), X(3A,27,5E,96), \ + X(3B,AB,6B,CB), X(1F,9D,45,F1), X(AC,FA,58,AB), X(4B,E3,03,93), \ + X(20,30,FA,55), X(AD,76,6D,F6), X(88,CC,76,91), X(F5,02,4C,25), \ + X(4F,E5,D7,FC), X(C5,2A,CB,D7), X(26,35,44,80), X(B5,62,A3,8F), \ + X(DE,B1,5A,49), X(25,BA,1B,67), X(45,EA,0E,98), X(5D,FE,C0,E1), \ + X(C3,2F,75,02), X(81,4C,F0,12), X(8D,46,97,A3), X(6B,D3,F9,C6), \ + X(03,8F,5F,E7), X(15,92,9C,95), X(BF,6D,7A,EB), X(95,52,59,DA), \ + X(D4,BE,83,2D), X(58,74,21,D3), X(49,E0,69,29), X(8E,C9,C8,44), \ + X(75,C2,89,6A), X(F4,8E,79,78), X(99,58,3E,6B), X(27,B9,71,DD), \ + X(BE,E1,4F,B6), X(F0,88,AD,17), X(C9,20,AC,66), X(7D,CE,3A,B4), \ + X(63,DF,4A,18), X(E5,1A,31,82), X(97,51,33,60), X(62,53,7F,45), \ + X(B1,64,77,E0), X(BB,6B,AE,84), X(FE,81,A0,1C), X(F9,08,2B,94), \ + X(70,48,68,58), X(8F,45,FD,19), X(94,DE,6C,87), X(52,7B,F8,B7), \ + X(AB,73,D3,23), X(72,4B,02,E2), X(E3,1F,8F,57), X(66,55,AB,2A), \ + X(B2,EB,28,07), X(2F,B5,C2,03), X(86,C5,7B,9A), X(D3,37,08,A5), \ + X(30,28,87,F2), X(23,BF,A5,B2), X(02,03,6A,BA), X(ED,16,82,5C), \ + X(8A,CF,1C,2B), X(A7,79,B4,92), X(F3,07,F2,F0), X(4E,69,E2,A1), \ + X(65,DA,F4,CD), X(06,05,BE,D5), X(D1,34,62,1F), X(C4,A6,FE,8A), \ + X(34,2E,53,9D), X(A2,F3,55,A0), X(05,8A,E1,32), X(A4,F6,EB,75), \ + X(0B,83,EC,39), X(40,60,EF,AA), X(5E,71,9F,06), X(BD,6E,10,51), \ + X(3E,21,8A,F9), X(96,DD,06,3D), X(DD,3E,05,AE), X(4D,E6,BD,46), \ + X(91,54,8D,B5), X(71,C4,5D,05), X(04,06,D4,6F), X(60,50,15,FF), \ + X(19,98,FB,24), X(D6,BD,E9,97), X(89,40,43,CC), X(67,D9,9E,77), \ + X(B0,E8,42,BD), X(07,89,8B,88), X(E7,19,5B,38), X(79,C8,EE,DB), \ + X(A1,7C,0A,47), X(7C,42,0F,E9), X(F8,84,1E,C9), X(00,00,00,00), \ + X(09,80,86,83), X(32,2B,ED,48), X(1E,11,70,AC), X(6C,5A,72,4E), \ + X(FD,0E,FF,FB), X(0F,85,38,56), X(3D,AE,D5,1E), X(36,2D,39,27), \ + X(0A,0F,D9,64), X(68,5C,A6,21), X(9B,5B,54,D1), X(24,36,2E,3A), \ + X(0C,0A,67,B1), X(93,57,E7,0F), X(B4,EE,96,D2), X(1B,9B,91,9E), \ + X(80,C0,C5,4F), X(61,DC,20,A2), X(5A,77,4B,69), X(1C,12,1A,16), \ + X(E2,93,BA,0A), X(C0,A0,2A,E5), X(3C,22,E0,43), X(12,1B,17,1D), \ + X(0E,09,0D,0B), X(F2,8B,C7,AD), X(2D,B6,A8,B9), X(14,1E,A9,C8), \ + X(57,F1,19,85), X(AF,75,07,4C), X(EE,99,DD,BB), X(A3,7F,60,FD), \ + X(F7,01,26,9F), X(5C,72,F5,BC), X(44,66,3B,C5), X(5B,FB,7E,34), \ + X(8B,43,29,76), X(CB,23,C6,DC), X(B6,ED,FC,68), X(B8,E4,F1,63), \ + X(D7,31,DC,CA), X(42,63,85,10), X(13,97,22,40), X(84,C6,11,20), \ + X(85,4A,24,7D), X(D2,BB,3D,F8), X(AE,F9,32,11), X(C7,29,A1,6D), \ + X(1D,9E,2F,4B), X(DC,B2,30,F3), X(0D,86,52,EC), X(77,C1,E3,D0), \ + X(2B,B3,16,6C), X(A9,70,B9,99), X(11,94,48,FA), X(47,E9,64,22), \ + X(A8,FC,8C,C4), X(A0,F0,3F,1A), X(56,7D,2C,D8), X(22,33,90,EF), \ + X(87,49,4E,C7), X(D9,38,D1,C1), X(8C,CA,A2,FE), X(98,D4,0B,36), \ + X(A6,F5,81,CF), X(A5,7A,DE,28), X(DA,B7,8E,26), X(3F,AD,BF,A4), \ + X(2C,3A,9D,E4), X(50,78,92,0D), X(6A,5F,CC,9B), X(54,7E,46,62), \ + X(F6,8D,13,C2), X(90,D8,B8,E8), X(2E,39,F7,5E), X(82,C3,AF,F5), \ + X(9F,5D,80,BE), X(69,D0,93,7C), X(6F,D5,2D,A9), X(CF,25,12,B3), \ + X(C8,AC,99,3B), X(10,18,7D,A7), X(E8,9C,63,6E), X(DB,3B,BB,7B), \ + X(CD,26,78,09), X(6E,59,18,F4), X(EC,9A,B7,01), X(83,4F,9A,A8), \ + X(E6,95,6E,65), X(AA,FF,E6,7E), X(21,BC,CF,08), X(EF,15,E8,E6), \ + X(BA,E7,9B,D9), X(4A,6F,36,CE), X(EA,9F,09,D4), X(29,B0,7C,D6), \ + X(31,A4,B2,AF), X(2A,3F,23,31), X(C6,A5,94,30), X(35,A2,66,C0), \ + X(74,4E,BC,37), X(FC,82,CA,A6), X(E0,90,D0,B0), X(33,A7,D8,15), \ + X(F1,04,98,4A), X(41,EC,DA,F7), X(7F,CD,50,0E), X(17,91,F6,2F), \ + X(76,4D,D6,8D), X(43,EF,B0,4D), X(CC,AA,4D,54), X(E4,96,04,DF), \ + X(9E,D1,B5,E3), X(4C,6A,88,1B), X(C1,2C,1F,B8), X(46,65,51,7F), \ + X(9D,5E,EA,04), X(01,8C,35,5D), X(FA,87,74,73), X(FB,0B,41,2E), \ + X(B3,67,1D,5A), X(92,DB,D2,52), X(E9,10,56,33), X(6D,D6,47,13), \ + X(9A,D7,61,8C), X(37,A1,0C,7A), X(59,F8,14,8E), X(EB,13,3C,89), \ + X(CE,A9,27,EE), X(B7,61,C9,35), X(E1,1C,E5,ED), X(7A,47,B1,3C), \ + X(9C,D2,DF,59), X(55,F2,73,3F), X(18,14,CE,79), X(73,C7,37,BF), \ + X(53,F7,CD,EA), X(5F,FD,AA,5B), X(DF,3D,6F,14), X(78,44,DB,86), \ + X(CA,AF,F3,81), X(B9,68,C4,3E), X(38,24,34,2C), X(C2,A3,40,5F), \ + X(16,1D,C3,72), X(BC,E2,25,0C), X(28,3C,49,8B), X(FF,0D,95,41), \ + X(39,A8,01,71), X(08,0C,B3,DE), X(D8,B4,E4,9C), X(64,56,C1,90), \ + X(7B,CB,84,61), X(D5,32,B6,70), X(48,6C,5C,74), X(D0,B8,57,42) + +#define X(a,b,c,d) 0x##a##b##c##d +static const uint32_t XAES_RT0[256] = { XAES_RT }; +#undef X + +#define X(a,b,c,d) 0x##d##a##b##c +static const uint32_t XAES_RT1[256] = { XAES_RT }; +#undef X + +#define X(a,b,c,d) 0x##c##d##a##b +static const uint32_t XAES_RT2[256] = { XAES_RT }; +#undef X + +#define X(a,b,c,d) 0x##b##c##d##a +static const uint32_t XAES_RT3[256] = { XAES_RT }; +#undef X + +#undef XAES_RT + +static const uint32_t XAES_RoundConsts[10] = +{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000 +}; + +/* Decryption key schedule tables */ +static uint32_t XAES_KT0[256]; +static uint32_t XAES_KT1[256]; +static uint32_t XAES_KT2[256]; +static uint32_t XAES_KT3[256]; +static xbool_t g_bKTInit = XFALSE; + +void XAES_SetKey(xaes_context_t *pCtx, const uint8_t *pKey, size_t nSize, const uint8_t *pIV) +{ + if (pIV == NULL) memset((void*)pCtx->IV, 0, sizeof(pCtx->IV)); + else memcpy((void*)pCtx->IV, (void*)pIV, sizeof(pCtx->IV)); + + switch (nSize) + { + case 128: pCtx->nRounds = 10; break; + case 192: pCtx->nRounds = 12; break; + case 256: pCtx->nRounds = 14; break; + default : return; + } + + uint32_t *RK = pCtx->encKeys; + int i; + + for (i = 0; i < (nSize >> 5); i++) + XAES_GET_UINT32_BE(RK[i], pKey, i << 2); + + switch (pCtx->nRounds) + { + case 10: + for (i = 0; i < 10; i++, RK += 4) + { + RK[4] = RK[0] ^ XAES_RoundConsts[i] ^ + (fwdSBox[(uint8_t)(RK[3] >> 16)] << 24) ^ + (fwdSBox[(uint8_t)(RK[3] >> 8)] << 16) ^ + (fwdSBox[(uint8_t)(RK[3] )] << 8) ^ + (fwdSBox[(uint8_t)(RK[3] >> 24)] ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + case 12: + for (i = 0; i < 8; i++, RK += 6) + { + RK[6] = RK[0] ^ XAES_RoundConsts[i] ^ + (fwdSBox[(uint8_t)(RK[5] >> 16)] << 24) ^ + (fwdSBox[(uint8_t)(RK[5] >> 8)] << 16) ^ + (fwdSBox[(uint8_t)(RK[5] )] << 8) ^ + (fwdSBox[(uint8_t)(RK[5] >> 24)] ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + case 14: + + for (i = 0; i < 7; i++, RK += 8) + { + RK[8] = RK[0] ^ XAES_RoundConsts[i] ^ + (fwdSBox[(uint8_t)(RK[7] >> 16)] << 24) ^ + (fwdSBox[(uint8_t)(RK[7] >> 8)] << 16) ^ + (fwdSBox[(uint8_t)(RK[7] )] << 8) ^ + (fwdSBox[(uint8_t)(RK[7] >> 24)] ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + (fwdSBox[(uint8_t)(RK[11] >> 24)] << 24) ^ + (fwdSBox[(uint8_t)(RK[11] >> 16)] << 16) ^ + (fwdSBox[(uint8_t)(RK[11] >> 8)] << 8) ^ + (fwdSBox[(uint8_t)(RK[11] )] ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + default: + break; + } + + /* Setup decryption round keys */ + if (!g_bKTInit) + { + for (i = 0; i < 256; i++) + { + XAES_KT0[i] = XAES_RT0[fwdSBox[i]]; + XAES_KT1[i] = XAES_RT1[fwdSBox[i]]; + XAES_KT2[i] = XAES_RT2[fwdSBox[i]]; + XAES_KT3[i] = XAES_RT3[fwdSBox[i]]; + } + + g_bKTInit = XTRUE; + } + + uint32_t *SK = pCtx->decKeys; + *SK++ = *RK++; + *SK++ = *RK++; + *SK++ = *RK++; + *SK++ = *RK++; + + for (i = 1; i < pCtx->nRounds; i++) + { + RK -= 8; + + *SK++ = XAES_KT0[(uint8_t)(*RK >> 24)] ^ + XAES_KT1[(uint8_t)(*RK >> 16)] ^ + XAES_KT2[(uint8_t)(*RK >> 8)] ^ + XAES_KT3[(uint8_t)(*RK )]; RK++; + + *SK++ = XAES_KT0[(uint8_t)(*RK >> 24)] ^ + XAES_KT1[(uint8_t)(*RK >> 16)] ^ + XAES_KT2[(uint8_t)(*RK >> 8)] ^ + XAES_KT3[(uint8_t)(*RK )]; RK++; + + *SK++ = XAES_KT0[(uint8_t)(*RK >> 24)] ^ + XAES_KT1[(uint8_t)(*RK >> 16)] ^ + XAES_KT2[(uint8_t)(*RK >> 8)] ^ + XAES_KT3[(uint8_t)(*RK )]; RK++; + + *SK++ = XAES_KT0[(uint8_t)(*RK >> 24)] ^ + XAES_KT1[(uint8_t)(*RK >> 16)] ^ + XAES_KT2[(uint8_t)(*RK >> 8)] ^ + XAES_KT3[(uint8_t)(*RK )]; RK++; + } + + RK -= 8; + *SK++ = *RK++; + *SK++ = *RK++; + *SK++ = *RK++; + *SK++ = *RK++; +} + +void XAES_EncryptBlock(xaes_context_t *pCtx, uint8_t output[XAES_BLOCK_SIZE], const uint8_t input[XAES_BLOCK_SIZE]) +{ + uint32_t X0, X1, X2, X3, Y0, Y1, Y2, Y3; + uint32_t *RK = pCtx->encKeys; + + XAES_GET_UINT32_BE(X0, input, 0); X0 ^= RK[0]; + XAES_GET_UINT32_BE(X1, input, 4); X1 ^= RK[1]; + XAES_GET_UINT32_BE(X2, input, 8); X2 ^= RK[2]; + XAES_GET_UINT32_BE(X3, input, 12); X3 ^= RK[3]; + +#define XAES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + RK += 4; \ + \ + X0 = RK[0] ^ XAES_FT0[(uint8_t)(Y0 >> 24)] ^ \ + XAES_FT1[(uint8_t)(Y1 >> 16)] ^ \ + XAES_FT2[(uint8_t)(Y2 >> 8)] ^ \ + XAES_FT3[(uint8_t)(Y3 )]; \ + \ + X1 = RK[1] ^ XAES_FT0[(uint8_t)(Y1 >> 24)] ^ \ + XAES_FT1[(uint8_t)(Y2 >> 16)] ^ \ + XAES_FT2[(uint8_t)(Y3 >> 8)] ^ \ + XAES_FT3[(uint8_t)(Y0 )]; \ + \ + X2 = RK[2] ^ XAES_FT0[(uint8_t)(Y2 >> 24)] ^ \ + XAES_FT1[(uint8_t)(Y3 >> 16)] ^ \ + XAES_FT2[(uint8_t)(Y0 >> 8)] ^ \ + XAES_FT3[(uint8_t)(Y1 )]; \ + \ + X3 = RK[3] ^ XAES_FT0[(uint8_t)(Y3 >> 24)] ^ \ + XAES_FT1[(uint8_t)(Y0 >> 16)] ^ \ + XAES_FT2[(uint8_t)(Y1 >> 8)] ^ \ + XAES_FT3[(uint8_t)(Y2 )]; \ +} + + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + + if (pCtx->nRounds > 10) + { + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + } + + if (pCtx->nRounds > 12) + { + XAES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + } + + RK += 4; + + X0 = RK[0] ^ (fwdSBox[(uint8_t)(Y0 >> 24)] << 24) ^ + (fwdSBox[(uint8_t)(Y1 >> 16)] << 16) ^ + (fwdSBox[(uint8_t)(Y2 >> 8)] << 8) ^ + (fwdSBox[(uint8_t)(Y3 )] ); + + X1 = RK[1] ^ (fwdSBox[(uint8_t)(Y1 >> 24)] << 24) ^ + (fwdSBox[(uint8_t)(Y2 >> 16)] << 16) ^ + (fwdSBox[(uint8_t)(Y3 >> 8)] << 8) ^ + (fwdSBox[(uint8_t)(Y0 )] ); + + X2 = RK[2] ^ (fwdSBox[(uint8_t)(Y2 >> 24)] << 24) ^ + (fwdSBox[(uint8_t)(Y3 >> 16)] << 16) ^ + (fwdSBox[(uint8_t)(Y0 >> 8)] << 8) ^ + (fwdSBox[(uint8_t)(Y1 )] ); + + X3 = RK[3] ^ (fwdSBox[(uint8_t)(Y3 >> 24)] << 24) ^ + (fwdSBox[(uint8_t)(Y0 >> 16)] << 16) ^ + (fwdSBox[(uint8_t)(Y1 >> 8)] << 8) ^ + (fwdSBox[(uint8_t)(Y2 )] ); + + XAES_PUT_UINT32_BE(X0, output, 0); + XAES_PUT_UINT32_BE(X1, output, 4); + XAES_PUT_UINT32_BE(X2, output, 8); + XAES_PUT_UINT32_BE(X3, output, 12); +} + +void XAES_DecryptBlock(xaes_context_t *pCtx, uint8_t output[XAES_BLOCK_SIZE], const uint8_t input[XAES_BLOCK_SIZE]) +{ + uint32_t X0, X1, X2, X3, Y0, Y1, Y2, Y3; + uint32_t *RK = pCtx->decKeys; + + XAES_GET_UINT32_BE(X0, input, 0); X0 ^= RK[0]; + XAES_GET_UINT32_BE(X1, input, 4); X1 ^= RK[1]; + XAES_GET_UINT32_BE(X2, input, 8); X2 ^= RK[2]; + XAES_GET_UINT32_BE(X3, input, 12); X3 ^= RK[3]; + +#define XAES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + RK += 4; \ + \ + X0 = RK[0] ^ XAES_RT0[(uint8_t)(Y0 >> 24)] ^ \ + XAES_RT1[(uint8_t)(Y3 >> 16)] ^ \ + XAES_RT2[(uint8_t)(Y2 >> 8)] ^ \ + XAES_RT3[(uint8_t)(Y1 )]; \ + \ + X1 = RK[1] ^ XAES_RT0[(uint8_t)(Y1 >> 24)] ^ \ + XAES_RT1[(uint8_t)(Y0 >> 16)] ^ \ + XAES_RT2[(uint8_t)(Y3 >> 8)] ^ \ + XAES_RT3[(uint8_t)(Y2 )]; \ + \ + X2 = RK[2] ^ XAES_RT0[(uint8_t)(Y2 >> 24)] ^ \ + XAES_RT1[(uint8_t)(Y1 >> 16)] ^ \ + XAES_RT2[(uint8_t)(Y0 >> 8)] ^ \ + XAES_RT3[(uint8_t)(Y3 )]; \ + \ + X3 = RK[3] ^ XAES_RT0[(uint8_t)(Y3 >> 24)] ^ \ + XAES_RT1[(uint8_t)(Y2 >> 16)] ^ \ + XAES_RT2[(uint8_t)(Y1 >> 8)] ^ \ + XAES_RT3[(uint8_t)(Y0 )]; \ +} + + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + + if (pCtx->nRounds > 10) + { + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + } + + if (pCtx->nRounds > 12) + { + XAES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + XAES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + } + + RK += 4; + X0 = RK[0] ^ (rvSBox[(uint8_t)(Y0 >> 24)] << 24) ^ + (rvSBox[(uint8_t)(Y3 >> 16)] << 16) ^ + (rvSBox[(uint8_t)(Y2 >> 8)] << 8) ^ + (rvSBox[(uint8_t)(Y1 )] ); + + X1 = RK[1] ^ (rvSBox[(uint8_t)(Y1 >> 24)] << 24) ^ + (rvSBox[(uint8_t)(Y0 >> 16)] << 16) ^ + (rvSBox[(uint8_t)(Y3 >> 8)] << 8) ^ + (rvSBox[(uint8_t)(Y2 )] ); + + X2 = RK[2] ^ (rvSBox[(uint8_t)(Y2 >> 24)] << 24) ^ + (rvSBox[(uint8_t)(Y1 >> 16)] << 16) ^ + (rvSBox[(uint8_t)(Y0 >> 8)] << 8) ^ + (rvSBox[(uint8_t)(Y3 )] ); + + X3 = RK[3] ^ (rvSBox[(uint8_t)(Y3 >> 24)] << 24) ^ + (rvSBox[(uint8_t)(Y2 >> 16)] << 16) ^ + (rvSBox[(uint8_t)(Y1 >> 8)] << 8) ^ + (rvSBox[(uint8_t)(Y0 )] ); + + XAES_PUT_UINT32_BE(X0, output, 0); + XAES_PUT_UINT32_BE(X1, output, 4); + XAES_PUT_UINT32_BE(X2, output, 8); + XAES_PUT_UINT32_BE(X3, output, 12); +} + +uint8_t* XAES_Encrypt(xaes_context_t *pCtx, const uint8_t *pInput, size_t *pLength) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + uint8_t *pInputPtr = NULL; + size_t nDataLength = 0; + + uint8_t iv[XAES_BLOCK_SIZE]; + memcpy(iv, pCtx->IV, sizeof(iv)); + + if (*pLength % XAES_BLOCK_SIZE) + { + size_t nCount = *pLength / XAES_BLOCK_SIZE; + size_t nPart = *pLength - nCount * XAES_BLOCK_SIZE; + nDataLength = *pLength + (XAES_BLOCK_SIZE - nPart); + } + + if (nDataLength) + { + pInputPtr = (uint8_t*)malloc(nDataLength + 1); + if (pInputPtr == NULL) return NULL; + + memcpy(pInputPtr, pInput, *pLength); + memset(pInputPtr + *pLength, 0, nDataLength - *pLength); + } + + const uint8_t *pData = pInputPtr != NULL ? pInputPtr : pInput; + nDataLength = nDataLength ? nDataLength : *pLength; + + uint8_t *pOutput = (uint8_t*)malloc(nDataLength + 1); + if (pOutput == NULL) + { + free(pInputPtr); + return NULL; + } + + uint8_t *pOffset = pOutput; + size_t nDataLeft = nDataLength; + + while (nDataLeft > 0) + { + uint8_t i; + for (i = 0; i < XAES_BLOCK_SIZE; i++) + pOffset[i] = pData[i] ^ iv[i]; + + XAES_EncryptBlock(pCtx, pOffset, pOffset); + memcpy(iv, pOffset, XAES_BLOCK_SIZE); + + nDataLeft -= XAES_BLOCK_SIZE; + pOffset += XAES_BLOCK_SIZE; + pData += XAES_BLOCK_SIZE; + } + + *pLength = nDataLength; + pOutput[*pLength] = '\0'; + + free(pInputPtr); + return pOutput; +} + +uint8_t* XAES_Decrypt(xaes_context_t *pCtx, const uint8_t *pInput, size_t *pLength) +{ + if (pInput == NULL || pLength == NULL || !(*pLength)) return NULL; + uint8_t *pInputPtr = NULL; + size_t nDataLength = 0; + + uint8_t iv[XAES_BLOCK_SIZE]; + memcpy(iv, pCtx->IV, sizeof(iv)); + + if (*pLength % XAES_BLOCK_SIZE) + { + size_t nCount = *pLength / XAES_BLOCK_SIZE; + size_t nPart = *pLength - nCount * XAES_BLOCK_SIZE; + nDataLength = *pLength + (XAES_BLOCK_SIZE - nPart); + } + + if (nDataLength) + { + pInputPtr = (uint8_t*)malloc(nDataLength + 1); + if (pInputPtr == NULL) return NULL; + + memcpy(pInputPtr, pInput, *pLength); + memset(pInputPtr + *pLength, 0, nDataLength - *pLength); + } + + const uint8_t *pData = pInputPtr != NULL ? pInputPtr : pInput; + nDataLength = nDataLength ? nDataLength : *pLength; + + uint8_t *pOutput = (uint8_t*)malloc(nDataLength + 1); + if (pOutput == NULL) + { + free(pInputPtr); + return NULL; + } + + uint8_t *pOffset = pOutput; + size_t nDataLeft = nDataLength; + + while (nDataLeft > 0) + { + uint8_t i, ivTmp[XAES_BLOCK_SIZE]; + memcpy(ivTmp, pData, XAES_BLOCK_SIZE); + XAES_DecryptBlock(pCtx, pOffset, pData); + + for (i = 0; i < XAES_BLOCK_SIZE; i++) + pOffset[i] = pOffset[i] ^ iv[i]; + + memcpy(iv, ivTmp, XAES_BLOCK_SIZE); + nDataLeft -= XAES_BLOCK_SIZE; + pOffset += XAES_BLOCK_SIZE; + pData += XAES_BLOCK_SIZE; + } + + pOutput[nDataLength] = '\0'; + *pLength = nDataLength; + + while (nDataLength && pOutput[nDataLength] == '\0') nDataLength--; + if (*pLength != nDataLength) *pLength = nDataLength + 1; + + free(pInputPtr); + return pOutput; +} \ No newline at end of file diff --git a/src/xaes.h b/src/xaes.h new file mode 100644 index 0000000..754acaf --- /dev/null +++ b/src/xaes.h @@ -0,0 +1,42 @@ +/*! + * @file libxutils/src/xaes.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of Advanced Encryption Standard + * based on FIPS-197 implementation by Christophe Devine. + */ + +#ifndef __XUTILS_AES_H__ +#define __XUTILS_AES_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define XAES_BLOCK_SIZE 16 +#define XAES_RKEY_SIZE 64 + +typedef struct AESContext { + uint32_t encKeys[XAES_RKEY_SIZE]; /* Dncryption round keys */ + uint32_t decKeys[XAES_RKEY_SIZE]; /* Decryption round keys */ + uint8_t IV[XAES_BLOCK_SIZE]; /* Initialization vector */ + size_t nRounds; /* Number of rounds */ +} xaes_context_t; + +void XAES_SetKey(xaes_context_t *pCtx, const uint8_t *pKey, size_t nSize, const uint8_t *pIV); +void XAES_EncryptBlock(xaes_context_t *pCtx, uint8_t output[XAES_BLOCK_SIZE], const uint8_t input[XAES_BLOCK_SIZE]); +void XAES_DecryptBlock(xaes_context_t *pCtx, uint8_t output[XAES_BLOCK_SIZE], const uint8_t input[XAES_BLOCK_SIZE]); + +uint8_t* XAES_Encrypt(xaes_context_t *pCtx, const uint8_t *pInput, size_t *pLength); +uint8_t* XAES_Decrypt(xaes_context_t *pCtx, const uint8_t *pInput, size_t *pLength); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_AES_H__ */ diff --git a/src/xbuf.c b/src/xbuf.c new file mode 100644 index 0000000..1dff875 --- /dev/null +++ b/src/xbuf.c @@ -0,0 +1,504 @@ +/*! + * @file libxutils/src/xbuf.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Dynamically allocated byte and data buffers + */ + +#include "xstd.h" +#include "xstr.h" +#include "xbuf.h" + +uint8_t *XByteData_Dup(const uint8_t *pBuff, size_t nLength) +{ + if (pBuff == NULL || !nLength) return NULL; + + uint8_t *pData = (uint8_t*)malloc(nLength + 1); + if (pData == NULL) return NULL; + + memcpy(pData, pBuff, nLength); + pData[nLength] = XSTR_NUL; + + return pData; +} + +int XByteBuffer_Resize(xbyte_buffer_t *pBuffer, size_t nSize) +{ + if (pBuffer->pData == NULL && nSize) + { + pBuffer->pData = (uint8_t*)malloc(nSize); + if (pBuffer->pData == NULL) + { + pBuffer->nStatus = XSTDERR; + return pBuffer->nStatus; + } + + pBuffer->nSize = nSize; + pBuffer->nUsed = 0; + return (int)nSize; + } + + if (!pBuffer->nSize) + { + if (pBuffer->nUsed >= nSize) + return (int)pBuffer->nUsed; + + pBuffer->nStatus = XSTDERR; + return pBuffer->nStatus; + } + + if (!nSize) return pBuffer->nUsed ? (int)pBuffer->nUsed : XSTDERR; + pBuffer->nUsed = (pBuffer->nUsed >= nSize) ? (nSize - 1) : pBuffer->nUsed; + + uint8_t* pOldData = pBuffer->pData; + pBuffer->pData = (uint8_t*)realloc(pBuffer->pData, nSize); + + if (pBuffer->pData == NULL) + { + pBuffer->pData = pOldData; + pBuffer->nStatus = XSTDERR; + return pBuffer->nStatus; + } + + if (pBuffer->pData != NULL && pBuffer->nUsed < nSize) + pBuffer->pData[pBuffer->nUsed] = XSTR_NUL; + + pBuffer->nSize = nSize; + return (int)pBuffer->nSize; +} + +int XByteBuffer_Terminate(xbyte_buffer_t *pBuffer, size_t nPosit) +{ + if (pBuffer->pData == NULL || !pBuffer->nUsed) return XSTDERR; + size_t nTerminatePosit = XSTD_MIN(pBuffer->nUsed, nPosit); + pBuffer->pData[nTerminatePosit] = XSTR_NUL; + pBuffer->nUsed = nTerminatePosit; + return XSTDOK; +} + +int XByteBuffer_Reserve(xbyte_buffer_t *pBuffer, size_t nSize) +{ + if (pBuffer->nStatus < 0) return pBuffer->nStatus; + size_t nNewSize = pBuffer->nUsed + nSize; + if (nNewSize <= pBuffer->nSize) return (int)pBuffer->nSize; + else if (pBuffer->nFast) nNewSize *= 2; + return XByteBuffer_Resize(pBuffer, nNewSize); +} + +xbyte_buffer_t* XByteBuffer_New(size_t nSize, int nFastAlloc) +{ + xbyte_buffer_t *pBuffer = (xbyte_buffer_t*)malloc(sizeof(xbyte_buffer_t)); + if (pBuffer == NULL) return NULL; + + if (XByteBuffer_Init(pBuffer, nSize, nFastAlloc) < 0) + { + free(pBuffer); + return NULL; + } + + pBuffer->nAlloc = 1; + return pBuffer; +} + +int XByteBuffer_Init(xbyte_buffer_t *pBuffer, size_t nSize, int nFastAlloc) +{ + pBuffer->nStatus = XSTDOK; + pBuffer->nAlloc = XSTDNON; + pBuffer->nFast = nFastAlloc; + pBuffer->nSize = 0; + pBuffer->nUsed = 0; + pBuffer->pData = NULL; + return XByteBuffer_Reserve(pBuffer, nSize); +} + +void XByteBuffer_Clear(xbyte_buffer_t *pBuffer) +{ + if (pBuffer == NULL) return; + + if (pBuffer->pData != NULL && + pBuffer->nSize > 0) + free(pBuffer->pData); + + pBuffer->nStatus = 0; + pBuffer->nSize = 0; + pBuffer->nUsed = 0; + pBuffer->pData = NULL; + + if (pBuffer->nAlloc) + free(pBuffer); +} + +void XByteBuffer_Reset(xbyte_buffer_t *pBuffer) +{ + if (pBuffer->pData == NULL) + pBuffer->nSize = 0; + + pBuffer->nStatus = 0; + pBuffer->nUsed = 0; +} + +int XByteBuffer_Set(xbyte_buffer_t *pBuffer, uint8_t *pData, size_t nUsed) +{ + pBuffer->nStatus = 1; + pBuffer->nAlloc = 0; + pBuffer->nFast = 0; + pBuffer->nSize = 0; + pBuffer->nUsed = nUsed; + pBuffer->pData = pData; + + if (pBuffer->pData != NULL) + pBuffer->pData[nUsed] = '\0'; + + return (int)pBuffer->nUsed; +} + +int XByteBuffer_Add(xbyte_buffer_t *pBuffer, const uint8_t *pData, size_t nSize) +{ + if (pData == NULL || !nSize) return XSTDNON; + else if (XByteBuffer_Reserve(pBuffer, nSize + 1) <= 0) return XSTDERR; + memcpy(&pBuffer->pData[pBuffer->nUsed], pData, nSize); + pBuffer->nUsed += nSize; + pBuffer->pData[pBuffer->nUsed] = '\0'; + return (int)pBuffer->nUsed; +} + +int XByteBuffer_AddStr(xbyte_buffer_t *pBuffer, xstring_t *pStr) +{ + const uint8_t *pData = (const uint8_t *)pStr->pData; + return XByteBuffer_Add(pBuffer, pData, pStr->nLength); +} + +int XByteBuffer_AddByte(xbyte_buffer_t *pBuffer, uint8_t nByte) +{ + if (XByteBuffer_Reserve(pBuffer, 2) <= 0) return XSTDERR; + pBuffer->pData[pBuffer->nUsed++] = nByte; + pBuffer->pData[pBuffer->nUsed] = '\0'; + return (int)pBuffer->nUsed; +} + +int XByteBuffer_AddFmt(xbyte_buffer_t *pBuffer, const char *pFmt, ...) +{ + size_t nBytes = 0; + va_list args; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nBytes); + va_end(args); + + if (pDest == NULL) + { + pBuffer->nStatus = XSTDERR; + return XSTDERR; + } + + int nStatus = XByteBuffer_Add(pBuffer, (uint8_t*)pDest, nBytes); + free(pDest); + + return nStatus; +} + +int XByteBuffer_NullTerm(xbyte_buffer_t *pBuffer) +{ + if (XByteBuffer_Reserve(pBuffer, 1) <= 0) return XSTDERR; + pBuffer->pData[pBuffer->nUsed] = '\0'; + return (int)pBuffer->nUsed; +} + +int XByteBuffer_AddBuff(xbyte_buffer_t *pBuffer, xbyte_buffer_t *pSrc) +{ + if (pSrc->pData == NULL || !pSrc->nUsed) return XSTDERR; + XByteBuffer_Add(pBuffer, pSrc->pData, pSrc->nUsed); + return pBuffer->nStatus; +} + +int XByteBuffer_Insert(xbyte_buffer_t *pBuffer, size_t nPosit, const uint8_t *pData, size_t nSize) +{ + if (nPosit >= pBuffer->nUsed) return XByteBuffer_Add(pBuffer, pData, nSize); + else if (XByteBuffer_Reserve(pBuffer, nSize + 1) <= 0) return XSTDERR; + + uint8_t *pOffset = &pBuffer->pData[nPosit]; + size_t nTailSize = pBuffer->nUsed - nPosit; + + memmove(pOffset + nSize, pOffset, nTailSize); + memcpy(&pBuffer->pData[nPosit], pData, nSize); + + pBuffer->nUsed += nSize; + pBuffer->pData[pBuffer->nUsed] = '\0'; + return (int)pBuffer->nUsed; +} + +int XByteBuffer_Remove(xbyte_buffer_t *pBuffer, size_t nPosit, size_t nSize) +{ + if (!nSize || nPosit >= pBuffer->nUsed) return 0; + nSize = ((nPosit + nSize) > pBuffer->nUsed) ? + pBuffer->nUsed - nPosit : nSize; + + size_t nTailOffset = nPosit + nSize; + if (nTailOffset >= pBuffer->nUsed) + { + pBuffer->nUsed = nPosit; + return (int)pBuffer->nUsed; + } + + size_t nTailSize = pBuffer->nUsed - nTailOffset; + const uint8_t *pTail = &pBuffer->pData[nTailOffset]; + memmove(&pBuffer->pData[nPosit], pTail, nTailSize); + + pBuffer->nUsed -= nSize; + pBuffer->pData[pBuffer->nUsed] = '\0'; + return (int)nSize; +} + +int XByteBuffer_Delete(xbyte_buffer_t *pBuffer, size_t nPosit, size_t nSize) +{ + XByteBuffer_Remove(pBuffer, nPosit, nSize); + XByteBuffer_Resize(pBuffer, pBuffer->nUsed + 1); + pBuffer->pData[pBuffer->nUsed] = '\0'; + return pBuffer->nStatus; +} + +int XByteBuffer_Advance(xbyte_buffer_t *pBuffer, size_t nSize) +{ + XByteBuffer_Delete(pBuffer, 0, nSize); + return (int)pBuffer->nUsed; +} + +int XDataBuffer_Init(xdata_buffer_t *pBuffer, size_t nSize, int nFixed) +{ + pBuffer->pData = (void**)calloc(nSize, sizeof(void*)); + if (pBuffer->pData == NULL) + { + pBuffer->nSize = 0; + return XSTDERR; + } + + unsigned int i; + for (i = 0; i < nSize; i++) + pBuffer->pData[i] = NULL; + + pBuffer->nStatus = 0; + pBuffer->clearCb = NULL; + pBuffer->nFixed = nFixed; + pBuffer->nSize = nSize; + pBuffer->nUsed = 0; + return (int)nSize; +} + +int XDataBuffer_Realloc(xdata_buffer_t *pBuffer) +{ + if (pBuffer->nFixed) return (int)pBuffer->nSize; + void* pOldData = pBuffer->pData; + void *pData = NULL; + size_t nSize = 0; + + if (pBuffer->nUsed == pBuffer->nSize) + { + nSize = pBuffer->nSize * 2; + pData = realloc(pBuffer->pData, sizeof(void*) * nSize); + } + else if (pBuffer->nUsed > 0 && ((float)pBuffer->nUsed / (float)pBuffer->nSize) < 0.25) + { + nSize = pBuffer->nSize / 2; + pData = realloc(pBuffer->pData, sizeof(void*) * nSize); + } + else return (int)pBuffer->nSize; + + if (pData != NULL) + { + pBuffer->pData = (void**)pData; + pBuffer->nSize = nSize; + + size_t i; + for (i = pBuffer->nUsed; i < nSize; i++) + pBuffer->pData[i] = NULL; + + return (int)pBuffer->nSize; + } + + pBuffer->pData = pOldData; + return XSTDERR; +} + +void XDataBuffer_Clear(xdata_buffer_t *pBuffer) +{ + unsigned int i; + for (i = 0; i < pBuffer->nSize; i++) + { + if (pBuffer->clearCb != NULL) + pBuffer->clearCb(pBuffer->pData[i]); + pBuffer->pData[i] = NULL; + } + pBuffer->nUsed = 0; +} + +void XDataBuffer_Destroy(xdata_buffer_t *pBuffer) +{ + XDataBuffer_Clear(pBuffer); + if (pBuffer->pData != NULL) + { + free(pBuffer->pData); + pBuffer->pData = NULL; + } +} + +int XDataBuffer_Add(xdata_buffer_t *pBuffer, void *pData) +{ + if (pBuffer->nUsed >= pBuffer->nSize) return XSTDERR; + pBuffer->pData[pBuffer->nUsed++] = pData; + int nStat = XDataBuffer_Realloc(pBuffer); + return nStat > 0 ? (int)pBuffer->nUsed-1 : -2; +} + +void* XDataBuffer_Set(xdata_buffer_t *pBuffer, unsigned int nIndex, void *pData) +{ + void *pOldData = NULL; + if (nIndex < pBuffer->nSize || nIndex > 0) + { + pOldData = pBuffer->pData[nIndex]; + pBuffer->pData[nIndex] = pData; + pBuffer->nUsed += pOldData ? 0 : 1; + } + + return pOldData; +} + +void* XDataBuffer_Get(xdata_buffer_t *pBuffer, unsigned int nIndex) +{ + if (nIndex >= pBuffer->nUsed || !nIndex) return NULL; + return pBuffer->pData[nIndex]; +} + +void* XDataBuffer_Pop(xdata_buffer_t *pBuffer, unsigned int nIndex) +{ + void *pRetVal = XDataBuffer_Get(pBuffer, nIndex); + if (pRetVal == NULL) return NULL; + size_t i; + + for (i = (size_t)nIndex; i < pBuffer->nUsed; i++) + { + if ((i + 1) >= pBuffer->nUsed) break; + pBuffer->pData[i] = pBuffer->pData[i+1]; + } + + pBuffer->pData[--pBuffer->nUsed] = NULL; + XDataBuffer_Realloc(pBuffer); + return pRetVal; +} + +int XRingBuffer_Init(xring_buffer_t *pBuffer, size_t nSize) +{ + pBuffer->nSize = nSize; + pBuffer->nFront = 0; + pBuffer->nBack = 0; + pBuffer->nUsed = 0; + unsigned int i; + + pBuffer->pData = (xbyte_buffer_t**)calloc(nSize, sizeof(xbyte_buffer_t*)); + if (pBuffer->pData == NULL) return 0; + + for (i = 0; i < pBuffer->nSize; i++) + { + pBuffer->pData[i] = (xbyte_buffer_t*)malloc(sizeof(xbyte_buffer_t)); + if (pBuffer->pData[i]) XByteBuffer_Init(pBuffer->pData[i], 0, 0); + } + + return (int)pBuffer->nSize; +} + +void XRingBuffer_Reset(xring_buffer_t *pBuffer) +{ + unsigned long i; + for (i = 0; i < pBuffer->nSize; i++) + { + if (pBuffer->pData[i] != NULL) + { + XByteBuffer_Clear(pBuffer->pData[i]); + free(pBuffer->pData[i]); + pBuffer->pData[i] = NULL; + } + } + + pBuffer->nFront = 0; + pBuffer->nBack = 0; + pBuffer->nUsed = 0; +} + +void XRingBuffer_Destroy(xring_buffer_t *pBuffer) +{ + XRingBuffer_Reset(pBuffer); + free(pBuffer->pData); + pBuffer->pData = NULL; + pBuffer->nSize = 0; +} + +void XRingBuffer_Update(xring_buffer_t *pBuffer, int nAdd) +{ + if (nAdd) pBuffer->nBack++; + else pBuffer->nFront++; + + if ((size_t)pBuffer->nFront >= pBuffer->nSize) pBuffer->nFront = 0; + if ((size_t)pBuffer->nBack >= pBuffer->nSize) pBuffer->nBack = 0; + + pBuffer->nUsed = (pBuffer->nBack > pBuffer->nFront) ? + (pBuffer->nBack - pBuffer->nFront) : + (pBuffer->nSize - (pBuffer->nFront - pBuffer->nBack)); +} + +void XRingBuffer_Advance(xring_buffer_t *pBuffer) +{ + if (!pBuffer->nUsed || !pBuffer->nSize) return; + xbyte_buffer_t *pBuffData = pBuffer->pData[pBuffer->nFront]; + if (pBuffData != NULL) XByteBuffer_Clear(pBuffData); + XRingBuffer_Update(pBuffer, 0); +} + +int XRingBuffer_AddData(xring_buffer_t *pBuffer, const uint8_t* pData, size_t nSize) +{ + if (pBuffer->nUsed >= pBuffer->nSize) return 0; + xbyte_buffer_t *pBuffData = pBuffer->pData[pBuffer->nBack]; + + if (pBuffData == NULL) + { + pBuffData = malloc(sizeof(xbyte_buffer_t)); + if (pBuffData == NULL) return 0; + } + + int nStatus = XByteBuffer_Add(pBuffData, pData, nSize); + if (nStatus > 0) XRingBuffer_Update(pBuffer, 1); + return nStatus; +} + +int XRingBuffer_AddDataAdv(xring_buffer_t *pBuffer, const uint8_t* pData, size_t nSize) +{ + if (pBuffer->nUsed >= pBuffer->nSize) XRingBuffer_Advance(pBuffer); + return XRingBuffer_AddData(pBuffer, pData, nSize); +} + +int XRingBuffer_GetData(xring_buffer_t *pBuffer, uint8_t** pData, size_t* pSize) +{ + if (pBuffer->nUsed >= pBuffer->nSize) return 0; + xbyte_buffer_t *pBuffData = pBuffer->pData[pBuffer->nFront]; + if (pBuffData == NULL) return 0; + *pData = pBuffData->pData; + *pSize = pBuffData->nSize; + return 1; +} + +int XRingBuffer_Pop(xring_buffer_t *pBuffer, uint8_t* pData, size_t nSize) +{ + size_t nCopySize = 0; + if (pBuffer->nUsed >= pBuffer->nSize) return (int)nCopySize; + xbyte_buffer_t *pBuffData = pBuffer->pData[pBuffer->nFront]; + + if (pBuffData != NULL) + { + nCopySize = (nSize > pBuffData->nSize) ? pBuffData->nSize : nSize; + memcpy(pData, pBuffData->pData, nCopySize); + XByteBuffer_Clear(pBuffData); + } + + XRingBuffer_Update(pBuffer, 0); + return (int)nCopySize; +} diff --git a/src/xbuf.h b/src/xbuf.h new file mode 100644 index 0000000..1ba1a14 --- /dev/null +++ b/src/xbuf.h @@ -0,0 +1,92 @@ +/*! + * @file libxutils/src/xbuf.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Dynamically allocated byte and data buffers + */ + +#ifndef __XUTILS_BUFFER_H__ +#define __XUTILS_BUFFER_H__ + +#include +#include +#include +#include "xstr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void(*xdata_clear_cb_t)(void *pData); + +typedef struct XByteBuffer { + uint8_t *pData; + size_t nSize; + size_t nUsed; + uint16_t nAlloc; + uint16_t nFast; + int nStatus; +} xbyte_buffer_t; + +uint8_t *XByteData_Dup(const uint8_t *pBuff, size_t nSize); +xbyte_buffer_t* XByteBuffer_New(size_t nSize, int nFastAlloc); +void XByteBuffer_Clear(xbyte_buffer_t *pBuffer); +void XByteBuffer_Reset(xbyte_buffer_t *pBuffer); +int XByteBuffer_Terminate(xbyte_buffer_t *pBuffer, size_t nPosit); +int XByteBuffer_Resize(xbyte_buffer_t *pBuffer, size_t nSize); +int XByteBuffer_Reserve(xbyte_buffer_t *pBuffer, size_t nSize); +int XByteBuffer_Init(xbyte_buffer_t *pBuffer, size_t nSize, int nFastAlloc); +int XByteBuffer_Set(xbyte_buffer_t *pBuffer, uint8_t *pData, size_t nUsed); +int XByteBuffer_Add(xbyte_buffer_t *pBuffer, const uint8_t *pData, size_t nSize); +int XByteBuffer_AddByte(xbyte_buffer_t *pBuffer, uint8_t nByte); +int XByteBuffer_AddStr(xbyte_buffer_t *pBuffer, xstring_t *pStr); +int XByteBuffer_AddFmt(xbyte_buffer_t *pBuffer, const char *pFmt, ...); +int XByteBuffer_AddBuff(xbyte_buffer_t *pBuffer, xbyte_buffer_t *pSrc); +int XByteBuffer_Insert(xbyte_buffer_t *pBuffer, size_t nPosit, const uint8_t *pData, size_t nSize); +int XByteBuffer_Remove(xbyte_buffer_t *pBuffer, size_t nPosit, size_t nSize); +int XByteBuffer_Delete(xbyte_buffer_t *pBuffer, size_t nPosit, size_t nSize); +int XByteBuffer_Advance(xbyte_buffer_t *pBuffer, size_t nSize); +int XByteBuffer_NullTerm(xbyte_buffer_t *pBuffer); + +typedef struct XDataBuffer { + xdata_clear_cb_t clearCb; + void **pData; + size_t nSize; + size_t nUsed; + int nStatus; + int nFixed; +} xdata_buffer_t; + +void XDataBuffer_Destroy(xdata_buffer_t *pBuffer); +void XDataBuffer_Clear(xdata_buffer_t *pBuffer); +int XDataBuffer_Init(xdata_buffer_t *pBuffer, size_t nSize, int nFixed); +int XDataBuffer_Add(xdata_buffer_t *pBuffer, void *pData); +void* XDataBuffer_Set(xdata_buffer_t *pBuffer, unsigned int nIndex, void *pData); +void* XDataBuffer_Get(xdata_buffer_t *pBuffer, unsigned int nIndex); +void* XDataBuffer_Pop(xdata_buffer_t *pBuffer, unsigned int nIndex); + +typedef struct XRingBuffer { + xbyte_buffer_t **pData; + size_t nUsed; + size_t nSize; + size_t nFront; + size_t nBack; +} xring_buffer_t; + +int XRingBuffer_Init(xring_buffer_t *pBuffer, size_t nSize); +void XRingBuffer_Reset(xring_buffer_t *pBuffer); +void XRingBuffer_Destroy(xring_buffer_t *pBuffer); +void XRingBuffer_Update(xring_buffer_t *pBuffer, int nAdd); +void XRingBuffer_Advance(xring_buffer_t *pBuffer); +int XRingBuffer_AddData(xring_buffer_t *pBuffer, const uint8_t* pData, size_t nSize); +int XRingBuffer_AddDataAdv(xring_buffer_t *pBuffer, const uint8_t* pData, size_t nSize); +int XRingBuffer_GetData(xring_buffer_t *pBuffer, uint8_t** pData, size_t* pSize); +int XRingBuffer_Pop(xring_buffer_t *pBuffer, uint8_t* pData, size_t nSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_BUFFER_H__ */ \ No newline at end of file diff --git a/src/xcli.c b/src/xcli.c new file mode 100644 index 0000000..582e5f5 --- /dev/null +++ b/src/xcli.c @@ -0,0 +1,547 @@ +/*! + * @file libxutils/src/xcli.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of CLI window operarions. + */ + +#include "xstd.h" +#include "xcli.h" +#include "xtime.h" + +#ifdef __linux__ +#include +#endif + +#define XBAR_FRAME_BYTES 3 +#define XCLI_PERCENT_MAX 4 + +int XCLI_GetPass(const char *pText, char *pPass, size_t nSize) +{ + size_t nLength = XSTDNON; +#ifdef __linux__ + struct termios oflags, nflags; + tcgetattr(fileno(stdin), &oflags); + + nflags = oflags; + nflags.c_lflag &= ~ECHO; + nflags.c_lflag |= ECHONL; + + if (pText != NULL) printf("%s", pText); + if (tcsetattr(fileno(stdin), TCSANOW, &nflags)) return XSTDERR; + fgets(pPass, nSize, stdin); + if (tcsetattr(fileno(stdin), TCSANOW, &oflags)) return XSTDERR; + + nLength = strlen(pPass); + pPass[nLength - 1] = 0; +#endif + return (int)nLength; +} + +XSTATUS XCLI_GetWindowSize(xcli_size_t *pCli) +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + csbi.srWindow.Right = csbi.srWindow.Left = 0; + csbi.srWindow.Top = csbi.srWindow.Bottom = 0; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + pWin->nWinColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; + pCtpWinx->nWinRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; +#else + struct winsize size; + size.ws_col = size.ws_row = 0; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + pCli->nWinColumns = size.ws_col; + pCli->nWinRows = size.ws_row; +#endif + + return (pCli->nWinColumns && pCli->nWinRows) ? XSTDOK : XSTDERR; +} + +void XWindow_Init(xcli_wind_t *pWin) +{ + XArray_Init(&pWin->lineArray, 0, 0); + pWin->eType = XCLI_RENDER_FRAME; + pWin->frameSize.nWinColumns = 0; + pWin->frameSize.nWinRows = 0; +} + +XSTATUS XWindow_UpdateSize(xcli_wind_t *pWin) +{ + XSTATUS nStatus = XCLI_GetWindowSize(&pWin->frameSize); + if (nStatus != XSTDERR) pWin->frameSize.nWinRows--; + return nStatus; +} + +XSTATUS XWindow_AddLine(xcli_wind_t *pWin, char *pLine, size_t nLength) +{ + xcli_size_t *pSize = &pWin->frameSize; + xarray_t *pLines = &pWin->lineArray; + + if (XWindow_UpdateSize(pWin) == XSTDERR) return XSTDERR; + if (pLines->nUsed >= pSize->nWinRows) return XSTDNON; + + if (XArray_AddData(pLines, pLine, nLength) < 0) + { + XArray_Clear(pLines); + return XSTDERR; + } + + return XSTDOK; +} + +XSTATUS XWindow_AddLineFmt(xcli_wind_t *pWin, const char *pFmt, ...) +{ + xcli_size_t *pSize = &pWin->frameSize; + xarray_t *pLines = &pWin->lineArray; + + if (XWindow_UpdateSize(pWin) == XSTDERR) return XSTDERR; + if (pLines->nUsed >= pSize->nWinRows) return XSTDNON; + + size_t nLength = 0; + va_list args; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nLength); + va_end(args); + + XSTATUS nStatus = XSTDERR; + if (pDest == NULL) return nStatus; + + if (XArray_PushData(pLines, pDest, nLength) < 0) + { + XArray_Clear(pLines); + free(pDest); + return XSTDERR; + } + + return XSTDOK; +} + +XSTATUS XWindow_AddEmptyLine(xcli_wind_t *pWin) +{ + if (XWindow_UpdateSize(pWin) == XSTDERR) return XSTDERR; + size_t nColumns = pWin->frameSize.nWinColumns; + char emptyLine[XLINE_MAX]; + + size_t i, nSpaces = XSTD_MIN(nColumns, sizeof(emptyLine) - 1); + for (i = 0; i < nSpaces; i++) emptyLine[i] = XSTR_SPACE_CHAR; + + emptyLine[i] = XSTR_NUL; + return XWindow_AddLine(pWin, emptyLine, i); +} + +XSTATUS XWindow_AddAligned(xcli_wind_t *pWin, const char *pInput, const char *pFmt, uint8_t nAlign) +{ + if (XWindow_UpdateSize(pWin) == XSTDERR) return XSTDERR; + size_t nColumns = pWin->frameSize.nWinColumns; + + size_t nInputLen = strlen(pInput); + if (!nInputLen) return XSTDERR; + + char sAfterBuf[XLINE_MAX]; + char sPreBuf[XLINE_MAX]; + size_t nSpaces = 0; + + if (nAlign == XCLI_CENTER) nSpaces = (nColumns - nInputLen) / 2; + else if (nAlign == XCLI_RIGHT) nSpaces = nColumns - nInputLen; + xstrfill(sPreBuf, sizeof(sPreBuf), nSpaces, XSTR_SPACE_CHAR); + + if (nAlign == XCLI_RIGHT) nSpaces = 0; + else if (nAlign == XCLI_CENTER && nColumns % 2) nSpaces++; + else if (nAlign == XCLI_LEFT) nSpaces = nColumns - nInputLen; + xstrfill(sAfterBuf, sizeof(sAfterBuf), nSpaces, XSTR_SPACE_CHAR); + + if (pFmt == NULL) return XWindow_AddLineFmt(pWin, "%s%s%s", sPreBuf, pInput, sAfterBuf); + return XWindow_AddLineFmt(pWin, "%s%s%s%s%s", pFmt, sPreBuf, pInput, sAfterBuf, XSTR_FMT_RESET); +} + +void XWindow_ClearScreen() +{ +#if !defined(_WIN32) && !defined(_WIN64) + system("clear"); +#else + system("cls"); +#endif +} + +XSTATUS XWindow_RenderLine(xcli_wind_t *pWin, xbyte_buffer_t *pLine, xarray_data_t *pArrData) +{ + if (XWindow_UpdateSize(pWin) == XSTDERR) return XSTDERR; + size_t nChars = 0, nMaxSize = pWin->frameSize.nWinColumns; + + XByteBuffer_Set(pLine, (uint8_t*)pArrData->pData, pArrData->nSize); + pLine->nSize = pArrData->nSize; + pArrData->nSize = 0; + + const char *pRawLine = (const char*)pLine->pData; + size_t nExtra = xstrextra(pRawLine, pLine->nUsed, nMaxSize, &nChars, NULL); + + if (nChars < nMaxSize) + { + size_t i, nAppendSize = nMaxSize - nChars; + if (XByteBuffer_Reserve(pLine, nAppendSize) <= 0) + { + XByteBuffer_Clear(pLine); + return XSTDERR; + } + + for (i = 0; i < nAppendSize; i++) + { + if (XByteBuffer_AddByte(pLine, XSTR_SPACE_CHAR) <= 0) + { + XByteBuffer_Clear(pLine); + return XSTDERR; + } + } + } + + if (XByteBuffer_Resize(pLine, nMaxSize + nExtra + 1) <= 0 || + XByteBuffer_AddFmt(pLine, "%s", XSTR_FMT_RESET) <= 0) + { + XByteBuffer_Clear(pLine); + return XSTDERR; + } + + pArrData->pData = pLine->pData; + pArrData->nSize = pLine->nSize; + + return XSTDOK; +} + +XSTATUS XWindow_GetFrame(xcli_wind_t *pWin, xbyte_buffer_t *pFrame) +{ + if (pWin == NULL || pFrame == NULL) return XSTDERR; + XByteBuffer_Init(pFrame, XSTDNON, XSTDNON); + + while (pWin->lineArray.nUsed < pWin->frameSize.nWinRows) + if (XWindow_AddEmptyLine(pWin) < 0) return XSTDERR; + + xcli_size_t *pSize = &pWin->frameSize; + xarray_t *pLines = &pWin->lineArray; + size_t i, nRows = XSTD_MIN(pSize->nWinRows, pLines->nUsed); + + for (i = 0; i < nRows; i++) + { + xarray_data_t *pData = XArray_Get(pLines, i); + if (pData == NULL) continue; + + xbyte_buffer_t lineBuff; + if (XWindow_RenderLine(pWin, &lineBuff, pData) < 0) + { + XByteBuffer_Clear(pFrame); + XArray_Clear(pLines); + return XSTDERR; + } + + if (XByteBuffer_AddBuff(pFrame, &lineBuff) < 0) + { + XByteBuffer_Clear(pFrame); + XArray_Clear(pLines); + return XSTDERR; + } + } + + return XSTDOK; +} + +XSTATUS XWindow_Display(xcli_wind_t *pWin) +{ + XSTATUS nStatus = XSTDNON; + + if (pWin->eType == XCLI_LINE_BY_LINE) + { + XWindow_ClearScreen(); + xarray_t *pLines = &pWin->lineArray; + size_t i, nWinRows = pWin->frameSize.nWinRows; + size_t nRows = XSTD_MIN(nWinRows, pLines->nUsed); + + for (i = 0; i < nRows; i++) + { + xarray_data_t *pData = XArray_Get(pLines, i); + if (pData == NULL) continue; + + xbyte_buffer_t lineBuff; + if (XWindow_RenderLine(pWin, &lineBuff, pData) < 0) + { + XArray_Clear(pLines); + return XSTDERR; + } + + printf("%s\n", (char*)lineBuff.pData); + } + + for (i = pLines->nUsed; i < nWinRows; i++) printf("\n"); + + fflush(stdout); + nStatus = XSTDOK; + } + else if (pWin->eType == XCLI_RENDER_FRAME) + { + xbyte_buffer_t frameBuf; + nStatus = XWindow_GetFrame(pWin, &frameBuf); + if (nStatus == XSTDERR) return nStatus; + + XWindow_ClearScreen(); + printf("%s\r", (char*)frameBuf.pData); + fflush(stdout); + + XByteBuffer_Clear(&frameBuf); + nStatus = XSTDOK; + } + + return nStatus; +} + +XSTATUS XWindow_Flush(xcli_wind_t *pWin) +{ + XSTATUS nStatus = XWindow_Display(pWin); + XArray_Clear(&pWin->lineArray); + return nStatus; +} + +void XWindow_Destroy(xcli_wind_t *pWin) +{ + xarray_t *pArr = &pWin->lineArray; + XArray_Destroy(pArr); +} + +///////////////////////////////////////////////////////////////////////// +// Implementation of CLI progress bar +///////////////////////////////////////////////////////////////////////// + +XSTATUS XProgBar_UpdateWindowSize(xcli_bar_t *pCtx) +{ + xcli_size_t *pSize = &pCtx->frameSize; + return XCLI_GetWindowSize(pSize); +} + +void XProgBar_GetDefaults(xcli_bar_t *pCtx) +{ + pCtx->nIntervalU = XCLI_BAR_INTERVAL; + pCtx->nBarLength = 0; + pCtx->nLastTime = 0; + pCtx->nPosition = 0; + pCtx->fPercent = 0.; + pCtx->nBarUsed = 0; + + pCtx->bInPercent = XFALSE; + pCtx->bInSuffix = XFALSE; + pCtx->bReverse = XFALSE; + pCtx->bKeepBar = XFALSE; + + pCtx->cBackCursor = '<'; + pCtx->cCursor = '>'; + pCtx->cLoader = '='; + pCtx->cEmpty = ' '; + pCtx->cStart = '['; + pCtx->cEnd = ']'; + + xstrnul(pCtx->sPercent); + xstrnul(pCtx->sPrefix); + xstrnul(pCtx->sSuffix); + XProgBar_UpdateWindowSize(pCtx); +} + +void XProgBar_Finish(xcli_bar_t *pCtx) +{ + if (pCtx->bKeepBar) + { + printf("\n"); + return; + } + + char sSpaces[XLINE_MAX]; + xstrnul(sSpaces); + + char sPercent[XCLI_BUF_SIZE]; + if (pCtx->fPercent < 0.) xstrncpy(sPercent, sizeof(sPercent), " N/A "); + else xstrncpyf(sPercent, sizeof(sPercent), "%.1f%%", pCtx->fPercent); + + xstrfill(sSpaces, sizeof(sSpaces), pCtx->nBarLength, XSTR_SPACE_CHAR); + printf("%s%s %s %s\r\n", pCtx->sPrefix, sSpaces, sPercent, pCtx->sSuffix); +} + +void XProgBar_MakeMove(xcli_bar_t *pCtx) +{ + XProgBar_UpdateWindowSize(pCtx); + size_t nColumns = pCtx->frameSize.nWinColumns; + + char sProgress[XLINE_MAX]; + char sSpaces[XLINE_MAX]; + xstrnul(sProgress); + xstrnul(sSpaces); + + size_t nUsedLength = strlen(pCtx->sPrefix) + strlen(pCtx->sSuffix) + 7; + pCtx->nBarLength = (nColumns > nUsedLength) ? (nColumns - nUsedLength) : 0; + size_t i, nLoaderPercent = pCtx->nBarLength / 10; + + if (pCtx->nBarLength) + { + for (i = 0; i < (size_t)pCtx->nPosition; i++) + xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cEmpty); + + if (pCtx->bReverse == XTRUE && pCtx->nPosition < pCtx->nBarLength) + xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cBackCursor); + + for (i = 0; i < nLoaderPercent; i++) + xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cLoader); + + if (pCtx->bReverse == XFALSE && (size_t)pCtx->nPosition < pCtx->nBarLength - nLoaderPercent) + xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cCursor); + + size_t nProgressLen = strlen(sProgress); + if (nProgressLen < pCtx->nBarLength) + { + size_t nUnusedLength = pCtx->nBarLength - nProgressLen; + for (i = 0; i < nUnusedLength; i++) + xstrncat(sSpaces, sizeof(sSpaces), "%c", pCtx->cEmpty); + } + + uint32_t nNowTime = 0; + if (pCtx->nIntervalU) nNowTime = XTime_GetUsec(); + + if (!pCtx->nIntervalU || (!pCtx->nLastTime || (nNowTime - pCtx->nLastTime) >= pCtx->nIntervalU)) + { + if (!pCtx->bReverse) + { + if (++pCtx->nPosition > pCtx->nBarLength - nLoaderPercent - 1) + { + pCtx->nPosition = pCtx->nBarLength - nLoaderPercent - 1; + pCtx->bReverse = XTRUE; + } + } + else + { + if (--pCtx->nPosition < 0) + { + pCtx->bReverse = XFALSE; + pCtx->nPosition = 0; + } + } + + if (pCtx->nIntervalU) pCtx->nLastTime = nNowTime; + } + } + + printf("%s%s%c%s%s%s%s%c%s N/A %s\r", + pCtx->sPrefix, XSTR_FMT_BOLD, pCtx->cStart, XSTR_FMT_RESET, sProgress, + sSpaces, XSTR_FMT_BOLD, pCtx->cEnd, XSTR_FMT_RESET, pCtx->sSuffix); + fflush(stdout); +} + +xbool_t XProgBar_CalculateBounds(xcli_bar_t *pCtx) +{ + if (pCtx->fPercent > 100.) pCtx->fPercent = 100.; + else if (pCtx->fPercent < 0.) pCtx->fPercent = 0.; + + if (pCtx->bInPercent) + xstrncpyf(pCtx->sPercent, sizeof(pCtx->sPercent), "%s%.1f%%%s", + XSTR_FMT_DIM, pCtx->fPercent, XSTR_FMT_RESET); + else + xstrncpyfl(pCtx->sPercent, sizeof(pCtx->sPercent), + XCLI_PERCENT_MAX, XSTR_SPACE_CHAR, "%.1f%%", pCtx->fPercent); + + size_t nColumns = pCtx->frameSize.nWinColumns; + size_t nPreLen = strlen(pCtx->sPrefix); + size_t nSufLen = strlen(pCtx->sSuffix); + size_t nPctLen = strlen(pCtx->sPercent); + + size_t nExtraPctChars = xstrextra(pCtx->sPercent, nPctLen, 0, NULL, NULL); + size_t nExtraChars = xstrextra(pCtx->sPrefix, nPreLen, 0, NULL, NULL); + nExtraChars += xstrextra(pCtx->sSuffix, nSufLen, 0, NULL, NULL); + + xbool_t bHidePercent = (pCtx->bInPercent && pCtx->bInSuffix && nSufLen); + size_t nUsedLength = nPreLen + nSufLen + XBAR_FRAME_BYTES - nExtraChars; + if (!bHidePercent) nUsedLength += nPctLen - nExtraPctChars; + + pCtx->nBarLength = (nColumns > nUsedLength) ? (nColumns - nUsedLength) : 0; + pCtx->nBarUsed = pCtx->nBarLength * (size_t)floor(pCtx->fPercent) / 100; + return bHidePercent; +} + +size_t XProgBar_GetOutputAdv(xcli_bar_t *pCtx, char *pDst, size_t nSize, char *pProgress, xbool_t bHidePct) +{ + size_t nPosit = 0, nChars = 0; + char sProgress[XLINE_MAX]; + char sSpaces[XLINE_MAX]; + + size_t i, nProgLen = 0; + xstrnul(sProgress); + xstrnul(sSpaces); + + if (pProgress != NULL) nProgLen = xstrncpy(sProgress, sizeof(sProgress), pProgress); + if (pCtx->nBarLength) xstrextra(pProgress, nProgLen, pCtx->nBarLength, &nChars, &nPosit); + + sProgress[nPosit] = XSTR_NUL; + xstrncat(sProgress, sizeof(sProgress), "%s", XSTR_FMT_RESET); + + if (pCtx->nBarLength) + { + if (pProgress == NULL) + { + for (i = 0; i < pCtx->nBarUsed; i++) + nChars += xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cLoader); + + if (pCtx->fPercent > 0. && pCtx->fPercent < 100.) + nChars += xstrncat(sProgress, sizeof(sProgress), "%c", pCtx->cCursor); + } + + if (nChars < pCtx->nBarLength) + { + size_t nUnused = pCtx->nBarLength - nChars; + for (i = 0; i < nUnused; i++) xstrncat(sSpaces, sizeof(sSpaces), "%c", pCtx->cEmpty); + } + } + + if (bHidePct) + { + return xstrncpyf(pDst, nSize, "%s%s%c%s%s%s%s%s%c%s", + pCtx->sPrefix, XSTR_FMT_BOLD, pCtx->cStart, XSTR_FMT_RESET, sProgress, + sSpaces, pCtx->sSuffix, XSTR_FMT_BOLD, pCtx->cEnd, XSTR_FMT_RESET); + } + else if (pCtx->bInPercent) + { + return xstrncpyf(pDst, nSize, "%s%s%c%s%s%s%s%s%c%s%s", + pCtx->sPrefix, XSTR_FMT_BOLD, pCtx->cStart, XSTR_FMT_RESET, sProgress, sSpaces, + pCtx->sPercent, XSTR_FMT_BOLD, pCtx->cEnd, XSTR_FMT_RESET, pCtx->sSuffix); + } + else if (pCtx->bInSuffix) + { + return xstrncpyf(pDst, nSize, "%s%s%c%s%s%s%s%s%c%s %s", + pCtx->sPrefix, XSTR_FMT_BOLD, pCtx->cStart, XSTR_FMT_RESET, sProgress, sSpaces, + pCtx->sSuffix, XSTR_FMT_BOLD, pCtx->cEnd, XSTR_FMT_RESET, pCtx->sPercent); + } + + return xstrncpyf(pDst, nSize, "%s%s%c%s%s%s%s%c%s %s%s", + pCtx->sPrefix, XSTR_FMT_BOLD, pCtx->cStart, XSTR_FMT_RESET, sProgress, sSpaces, + XSTR_FMT_BOLD, pCtx->cEnd, XSTR_FMT_RESET, pCtx->sPercent, pCtx->sSuffix); +} + +size_t XProgBar_GetOutput(xcli_bar_t *pCtx, char *pDst, size_t nSize) +{ + xbool_t bPctOverride = XProgBar_CalculateBounds(pCtx); + return XProgBar_GetOutputAdv(pCtx, pDst, nSize, NULL, bPctOverride); +} + +void XProgBar_Update(xcli_bar_t *pCtx) +{ + if (pCtx->fPercent < 0.) + { + XProgBar_MakeMove(pCtx); + return; + } + + char sBuffer[XLINE_MAX]; + XProgBar_UpdateWindowSize(pCtx); + + if (XProgBar_GetOutput(pCtx, sBuffer, sizeof(sBuffer))) + printf("%s\r", sBuffer); + + if (pCtx->fPercent == 100.) + XProgBar_Finish(pCtx); + + fflush(stdout); +} diff --git a/src/xcli.h b/src/xcli.h new file mode 100644 index 0000000..72e7cdb --- /dev/null +++ b/src/xcli.h @@ -0,0 +1,106 @@ +/*! + * @file libxutils/src/xcli.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of CLI window operarions. + */ + +#ifndef __XUTILS_XCLI_H__ +#define __XUTILS_XCLI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xbuf.h" +#include "list.h" +#include "xtype.h" + +#define XCLI_BAR_INTERVAL 100000 +#define XCLI_BUF_SIZE 256 + +#define XCLI_CENTER 0 +#define XCLI_RIGHT 1 +#define XCLI_LEFT 2 + +typedef struct xcli_size_ { + size_t nWinColumns; + size_t nWinRows; +} xcli_size_t; + +int XCLI_GetPass(const char *pText, char *pPass, size_t nSize); +XSTATUS XCLI_GetWindowSize(xcli_size_t *pCli); +size_t XCLI_CountFormat(xcli_size_t *pSize, const char *pLine, size_t nLength, size_t *pPosit); + +typedef enum { + XCLI_FLUSH_SCREEN = 0, + XCLI_RENDER_FRAME, + XCLI_LINE_BY_LINE +} xcli_disp_type_t; + +typedef struct xcli_wind_ { + xcli_disp_type_t eType; + xcli_size_t frameSize; + xarray_t lineArray; +} xcli_wind_t; + +void XWindow_Init(xcli_wind_t *pWin); +void XWindow_Destroy(xcli_wind_t *pWin); + +XSTATUS XWindow_UpdateSize(xcli_wind_t *pWin); +void XWindow_ClearScreen(); + +XSTATUS XWindow_AddAligned(xcli_wind_t *pWin, const char *pInput, const char *pFmt, uint8_t nAlign); +XSTATUS XWindow_AddLineFmt(xcli_wind_t *pWin, const char *pFmt, ...); +XSTATUS XWindow_AddLine(xcli_wind_t *pWin, char *pLine, size_t nLine); +XSTATUS XWindow_AddEmptyLine(xcli_wind_t *pWin); + +XSTATUS XWindow_GetFrame(xcli_wind_t *pWin, xbyte_buffer_t *pFrame); +XSTATUS XWindow_Display(xcli_wind_t *pWin); +XSTATUS XWindow_Flush(xcli_wind_t *pWin); + +typedef struct xcli_bar_ { + xcli_size_t frameSize; + size_t nBarLength; + size_t nBarUsed; + + uint32_t nIntervalU; + uint32_t nLastTime; + + xbool_t bInPercent; + xbool_t bInSuffix; + xbool_t bKeepBar; + xbool_t bReverse; + double fPercent; + int nPosition; + + char cBackCursor; + char cCursor; + char cLoader; + char cEmpty; + char cStart; + char cEnd; + + char sPercent[XCLI_BUF_SIZE]; + char sPrefix[XCLI_BUF_SIZE]; + char sSuffix[XCLI_BUF_SIZE]; +} xcli_bar_t; + +void XProgBar_GetDefaults(xcli_bar_t *pCtx); +XSTATUS XProgBar_UpdateWindowSize(xcli_bar_t *pCtx); + +xbool_t XProgBar_CalculateBounds(xcli_bar_t *pCtx); +size_t XProgBar_GetOutputAdv(xcli_bar_t *pCtx, char *pDst, size_t nSize, char *pProgress, xbool_t bHidePct); +size_t XProgBar_GetOutput(xcli_bar_t *pCtx, char *pDst, size_t nSize); + +void XProgBar_MakeMove(xcli_bar_t *pCtx); +void XProgBar_Update(xcli_bar_t *pCtx); +void XProgBar_Finish(xcli_bar_t *pCtx); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XCLI_H__ */ \ No newline at end of file diff --git a/src/xcpu.c b/src/xcpu.c new file mode 100644 index 0000000..2b61314 --- /dev/null +++ b/src/xcpu.c @@ -0,0 +1,157 @@ +/*! + * @file libxutils/src/xcpu.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of sched_setaffinity. + */ + +#ifdef _XUTILS_USE_GNU +#define _GNU_SOURCE +#endif + +#include "xstd.h" +#include "xcpu.h" +#include "xstr.h" +#include "sync.h" +#include "xfs.h" +#include "xtype.h" + +#define XCPU_INFO_FILE "/proc/cpuinfo" +#define XCPU_KEYWORD "processor" + +static XATOMIC g_nCPUCount = 0; + +int XCPU_GetCount(void) +{ + xatomic_t nCount = XSYNC_ATOMIC_GET(&g_nCPUCount); + if (nCount > 0) return (int)nCount; + +#ifdef _WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + nCount = sysInfo.dwNumberOfProcessors; + XSYNC_ATOMIC_SET(&g_nCPUCount, nCount); +#else + xbyte_buffer_t buffer; + int nFoundPosit = 0; + int nPosit = 0; + + if (XPath_LoadBuffer(XCPU_INFO_FILE, &buffer) <= 0) return XSTDERR; + + do + { + nFoundPosit = xstrnsrc( + (char*)buffer.pData, + buffer.nUsed, + XCPU_KEYWORD, + nPosit + ); + + if (nFoundPosit < 0) break; + nPosit += nFoundPosit + 1; + nCount++; + } + while (nFoundPosit >= 0); + + XSYNC_ATOMIC_SET(&g_nCPUCount, nCount); + XByteBuffer_Clear(&buffer); +#endif + return nCount; +} + +int XCPU_SetAffinity(int *pCPUs, size_t nCount, xpid_t nPID) +{ + if (pCPUs == NULL || !nCount) return XSTDERR; + int nCPUCount = XCPU_GetCount(); + if (nCPUCount <= 0) return XSTDERR; + +#ifdef _XUTILS_USE_GNU + cpu_set_t mask; + CPU_ZERO(&mask); + size_t i; + + for (i = 0; i < nCount; i++) + { + if (pCPUs[i] >= 0 && + pCPUs[i] < nCPUCount) + CPU_SET(pCPUs[i], &mask); + } + + if (nPID == XCPU_CALLER_PID) nPID = syscall(SYS_gettid); + return sched_setaffinity(nPID, sizeof(mask), &mask); +#elif WIN32 + size_t i = 0; + for (i = 0; i < nCount; i++) + { + if (pCPUs[i] >= 0 && pCPUs[i] < nCPUCount) + { + DWORD_PTR nMask = ((DWORD_PTR)1) << pCPUs[i]; + if (!SetThreadAffinityMask(GetCurrentThread(), nMask)) return XSTDERR; + } + } + (void)nPID; + return XSTDOK; +#else + return XSTDERR; +#endif +} + +int XCPU_SetSingle(int nCPU, xpid_t nPID) +{ + int nCPUS[1]; + nCPUS[0] = nCPU; + return XCPU_SetAffinity(nCPUS, 1, nPID); +} + +int XCPU_AddAffinity(int nCPU, xpid_t nPID) +{ +#ifdef _XUTILS_USE_GNU + int nCPUCount = XCPU_GetCount(); + if (nCPU < 0 || nCPU >= nCPUCount) return XSTDERR; + + cpu_set_t mask; + CPU_ZERO(&mask); + + if (nPID == XCPU_CALLER_PID) nPID = syscall(SYS_gettid); + if (sched_getaffinity(nPID, sizeof(mask), &mask) < 0) return XSTDERR; + + if (!CPU_ISSET(nCPU, &mask)) + { + CPU_SET(nCPU, &mask); + return sched_setaffinity(nPID, sizeof(mask), &mask); + } + + return XSTDNON; +#elif WIN32 + return XCPU_SetSingle(nCPU, nPID); +#else + return XSTDERR; +#endif +} + +int XCPU_DelAffinity(int nCPU, xpid_t nPID) +{ +#ifdef _XUTILS_USE_GNU + int nCPUCount = XCPU_GetCount(); + if (nCPU < 0 || nCPU >= nCPUCount) return XSTDERR; + + cpu_set_t mask; + CPU_ZERO(&mask); + + if (nPID == XCPU_CALLER_PID) nPID = syscall(SYS_gettid); + if (sched_getaffinity(nPID, sizeof(mask), &mask) < 0) return XSTDERR; + + if (CPU_ISSET(nCPU, &mask)) + { + CPU_CLR(nCPU, &mask); + return sched_setaffinity(nPID, sizeof(mask), &mask); + } + + return XSTDNON; +#endif + (void)nPID; + (void)nCPU; + return XSTDERR; +} diff --git a/src/xcpu.h b/src/xcpu.h new file mode 100644 index 0000000..b3a8642 --- /dev/null +++ b/src/xcpu.h @@ -0,0 +1,31 @@ +/*! + * @file libxutils/src/xcpu.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of sched_setaffinity. + */ + +#ifndef __XUTILS_XCPU_H__ +#define __XUTILS_XCPU_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtype.h" + +#define XCPU_CALLER_PID -1 + +int XCPU_GetCount(void); +int XCPU_SetSingle(int nCPU, xpid_t nPID); +int XCPU_SetAffinity(int *pCPUs, size_t nCount, xpid_t nPID); +int XCPU_AddAffinity(int nCPU, xpid_t nPID); +int XCPU_DelAffinity(int nCPU, xpid_t nPID); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XCPU_H__ */ \ No newline at end of file diff --git a/src/xfs.c b/src/xfs.c new file mode 100644 index 0000000..acdf0ff --- /dev/null +++ b/src/xfs.c @@ -0,0 +1,1248 @@ +/*! + * @file libxutils/src/xfs.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the NIX/POSIX + * standart file and directory operations. + */ + +#include "xstd.h" +#include "xstr.h" +#include "sync.h" +#include "xfs.h" + +#define XFILE_BUF_SIZE 4096 +#define XFILE_FLAGS_LEN 10 +#define XFILE_DEFAULT_PERM "rw-r--r--" + +int xchmod(const char* pPath, xmode_t nMode) +{ +#ifdef _WIN32 + return _chmod(pPath, nMode); +#else + return chmod(pPath, nMode); +#endif +} + +int xunlink(const char* pPath) +{ +#ifdef _WIN32 + return _unlink(pPath); +#else + return unlink(pPath); +#endif +} + +int xrmdir(const char* pPath) +{ +#ifdef _WIN32 + return _rmdir(pPath); +#else + return rmdir(pPath); +#endif +} + +int xmkdir(const char* pPath, xmode_t nMode) +{ +#ifdef _WIN32 + (void)nMode; + return _mkdir(pPath); +#else + return mkdir(pPath, nMode); +#endif +} + +int xclose(int nFD) +{ +#ifdef _WIN32 + return _close(nFD); +#else + return close(nFD); +#endif +} + +int xstat(const char *pPath, struct stat *pStat) +{ + memset(pStat, 0, sizeof(struct stat)); +#ifdef _WIN32 + if (stat(pPath, pStat) < 0) return XSTDERR; +#else + if (lstat(pPath, pStat) < 0) return XSTDERR; +#endif + return XSTDOK; +} + +int XFile_ParseFlags(const char *pFlags) +{ + size_t i, nLen = strnlen(pFlags, XFILE_FLAGS_LEN); + int nFlags = 0; + + for (i = 0; i < nLen; i++) + { + switch(pFlags[i]) + { +#ifdef O_APPEND + case 'a': { nFlags |= O_APPEND; break; } +#endif +#ifdef O_CREAT + case 'c': { nFlags |= O_CREAT; break; } +#endif +#ifdef O_NDELAY + case 'd': { nFlags |= O_NDELAY; break; } +#endif +#ifdef O_EXCL + case 'e': { nFlags |= O_EXCL; break; } +#endif +#ifdef O_NONBLOCK + case 'n': { nFlags |= O_NONBLOCK; break; } +#endif +#ifdef O_RDONLY + case 'r': { nFlags |= O_RDONLY; break; } +#endif +#ifdef O_TRUNC + case 't': { nFlags |= O_TRUNC; break; } +#endif +#ifdef O_SYNC + case 's': { nFlags |= O_SYNC; break; } +#endif +#ifdef O_WRONLY + case 'w': { nFlags |= O_WRONLY; break; } +#endif +#ifdef O_RDWR + case 'x': { nFlags |= O_RDWR; break; } +#endif + default: break; + } + } + +#if defined(O_RDONLY) && defined(O_WRONLY) && defined(O_RDWR) + if (XFILE_CHECK_FL(nFlags, O_RDONLY) && + XFILE_CHECK_FL(nFlags, O_WRONLY)) + { + nFlags &= ~O_RDONLY; + nFlags &= ~O_WRONLY; + nFlags |= O_RDWR; + } +#endif + + return nFlags; +} + +int XFile_Open(xfile_t *pFile, const char *pPath, const char *pFlags, const char *pPerms) +{ + if (pFile == NULL || pPath == NULL) return XFILE_INVALID; + pFile->nFlags = (pFlags != NULL) ? XFile_ParseFlags(pFlags) : 0; + pFile->nBlockSize = XFILE_BUF_SIZE; + pFile->nSize = 0; + + const char *pPerm = (pPerms != NULL) ? pPerms : XFILE_DEFAULT_PERM; + if (!XPath_PermToMode(pPerm, &pFile->nMode)) return XFILE_INVALID; + +#ifdef _WIN32 + _sopen_s(&pFile->nFD, pPath, pFile->nFlags, _SH_DENYNO, pFile->nMode); +#else + pFile->nFD = open(pPath, pFile->nFlags, pFile->nMode); +#endif + + pFile->nPosit = pFile->nAlloc = 0; + return pFile->nFD; +} + +xfile_t* XFile_New(const char *pPath, const char *pFlags, const char *pPerms) +{ + xfile_t *pFile = (xfile_t*)malloc(sizeof(xfile_t)); + if (pFile == NULL) return NULL; + + if (XFile_Open(pFile, pPath, pFlags, pPerms) < 0) + { + free(pFile); + return NULL; + } + + pFile->nAlloc = 1; + return pFile; +} + +void XFile_Close(xfile_t *pFile) +{ + if (pFile != NULL) + { + if (pFile->nFD >= 0) + { + xclose(pFile->nFD); + pFile->nFD = -1; + } + + pFile->nFlags = 0; + pFile->nPosit = 0; + } +} + +void XFile_Clean(xfile_t *pFile) +{ + if (pFile != NULL) + { + XFile_Close(pFile); + + if (pFile->nAlloc) + free(pFile); + } +} + +size_t XFile_Seek(xfile_t *pFile, uint64_t nPosit, int nOffset) +{ + if (pFile->nFD < 0) return XFILE_INVALID; +#ifdef _WIN32 + return (int)_lseek(pFile->nFD, (long)nPosit, nOffset); +#else + return lseek(pFile->nFD, nPosit, nOffset); +#endif +} + +int XFile_Write(xfile_t *pFile, const void *pBuff, size_t nSize) +{ + if (pFile->nFD < 0) return XFILE_INVALID; +#ifdef _WIN32 + return _write(pFile->nFD, pBuff, (unsigned int)nSize); +#else + return write(pFile->nFD, pBuff, nSize); +#endif +} + +int XFile_Read(xfile_t *pFile, void *pBuff, size_t nSize) +{ + if (pFile->nFD < 0) return XFILE_INVALID; +#ifdef _WIN32 + return _read(pFile->nFD, pBuff, (unsigned int)nSize); +#else + return read(pFile->nFD, pBuff, nSize); +#endif +} + +int XFile_GetStats(xfile_t *pFile) +{ + if (pFile->nFD < 0) return XSTDERR; + + struct stat fileStat; + if (fstat(pFile->nFD, &fileStat) < 0) return XSTDERR; + + pFile->nBlockSize = fileStat.st_blksize ? + fileStat.st_blksize : XFILE_BUF_SIZE; + + pFile->nMode = fileStat.st_mode; + pFile->nSize = fileStat.st_size; + return pFile->nSize ? XSTDOK : XSTDNON; +} + +uint8_t* XFile_Load(xfile_t *pFile, size_t *pSize) +{ + *pSize = 0; + if (XFile_GetStats(pFile) <= 0 || !S_ISREG(pFile->nMode)) return NULL; + + uint8_t *pBuffer = (uint8_t*)malloc(pFile->nSize + 1); + if (pBuffer == NULL) return NULL; + + size_t nOffset = 0; + int nBytes = 0; + + do + { + size_t nFreeSpace = pFile->nSize - nOffset; + size_t nReadSize = pFile->nBlockSize < nFreeSpace ? + pFile->nBlockSize : nFreeSpace; + + nBytes = XFile_Read(pFile, &pBuffer[nOffset], nReadSize); + if (nBytes > 0) nOffset += nBytes; + + } while (nBytes > 0); + + if (!nOffset) + { + free(pBuffer); + return NULL; + } + + pBuffer[nOffset] = '\0'; + *pSize = nOffset; + + return pBuffer; +} + +int XFile_Copy(xfile_t *pIn, xfile_t *pOut) +{ + if (XFile_GetStats(pIn) < 0 || + pOut->nFD < 0) return XSTDERR; + + uint8_t *pBlock = (uint8_t*)malloc(pIn->nBlockSize); + if (pBlock == NULL) return XSTDERR; + + int nTotalBytes = 0; + int nRBytes = 0; + + while ((nRBytes = XFile_Read(pIn, pBlock, pIn->nBlockSize)) > 0) + { + int nWBytes = XFile_Write(pOut, pBlock, nRBytes); + if (nWBytes != nRBytes) break; + nTotalBytes += nWBytes; + } + + free(pBlock); + return nTotalBytes; +} + +int XFile_GetLine(xfile_t *pFile, char* pLine, size_t nSize) +{ + if (pLine != NULL && nSize) pLine[0] = XSTR_NUL; + if (pFile->nFD < 0) return XFILE_INVALID; + + char *pReadBuff = (char*)malloc(nSize); + if (pReadBuff == NULL) return XSTDERR; + + int nAvail = (int)nSize; + int nRead = 0; + char cByte; + + int nBytes = XFile_Read(pFile, &cByte, sizeof(char)); + pReadBuff[nRead] = cByte; + nAvail -= nBytes; + nRead += nBytes; + + while (nBytes > 0 && nRead < nAvail) + { + if (pReadBuff[nRead-nBytes] == '\n') + { + if (pLine != NULL && nSize > 0) + { + pReadBuff[nRead-nBytes] = '\0'; + xstrncpy(pLine, nSize, pReadBuff); + } + + free(pReadBuff); + return XFILE_SUCCESS; + } + + nBytes = XFile_Read(pFile, &cByte, sizeof(char)); + pReadBuff[nRead] = cByte; + nAvail -= nBytes; + nRead += nBytes; + } + + free(pReadBuff); + return XFILE_INVALID; +} + +int XFile_GetLineCount(xfile_t *pFile) +{ + if (XFile_GetStats(pFile) <= 0) return XFILE_INVALID; + int nLineNumber = 0; + + int nRet = XFile_GetLine(pFile, NULL, pFile->nSize); + while (nRet == XFILE_SUCCESS) + { + nLineNumber++; + nRet = XFile_GetLine(pFile, NULL, pFile->nSize); + } + + return nLineNumber; +} + +int XFile_ReadLine(xfile_t *pFile, char* pLine, size_t nSize, int nLineNumber) +{ + if (pFile->nFD < 0) return XFILE_INVALID; + int nReadNumber = 0; + + int nRet = XFile_GetLine(pFile, pLine, nSize); + while (nRet == XFILE_SUCCESS) + { + nReadNumber++; + if (nLineNumber == nReadNumber) return XFILE_SUCCESS; + nRet = XFile_GetLine(pFile, pLine, nSize); + } + + return XFILE_INVALID; +} + +int XPath_Exists(const char *pPath) +{ + struct stat st; + memset(&st, 0, sizeof(struct stat)); + if (stat(pPath, &st) == -1) return 0; + return 1; +} + +char XPath_GetType(xmode_t nMode) +{ +#ifdef S_IFMT + switch (nMode & S_IFMT) + { +#ifdef S_IFREG + case S_IFREG: return '-'; +#endif +#ifdef S_IFBLK + case S_IFBLK: return 'b'; +#endif +#ifdef S_IFCHR + case S_IFCHR: return 'c'; +#endif +#ifdef S_IFDIR + case S_IFDIR: return 'd'; +#endif +#ifdef S_IFIFO + case S_IFIFO: return 'p'; +#endif +#ifdef S_IFLNK + case S_IFLNK: return 'l'; +#endif +#ifdef S_IFSOCK + case S_IFSOCK: return 's'; +#endif + default: break; + } +#endif + return '?'; +} + +xfile_type_t XFile_GetType(xmode_t nMode) +{ + char type = XPath_GetType(nMode); + + switch (type) + { + case '-': + return XF_REGULAR; + case 'b': + return XF_BLOCK_DEVICE; + case 'c': + return XF_CHAR_DEVICE; + case 'd': + return XF_DIRECTORY; + case 'p': + return XF_PIPE; + case 'l': + return XF_SYMLINK; + case 's': + return XF_SOCKET; + default: break; + } + + return 0; // Unknown file format +} + +char XFile_GetTypeChar(xfile_type_t eType) +{ + switch (eType) + { + case XF_REGULAR: + return '-'; + case XF_BLOCK_DEVICE: + return 'b'; + case XF_CHAR_DEVICE: + return 'c'; + case XF_DIRECTORY: + return 'd'; + case XF_PIPE: + return 'p'; + case XF_SYMLINK: + return 'l'; + case XF_SOCKET: + return 's'; + default: break; + } + + return '?'; // Unknown file format +} + +int XPath_Parse(xpath_t *pPath, const char *pPathStr) +{ + pPath->sPath[0] = XSTR_NUL; + pPath->sFile[0] = XSTR_NUL; + + if (!xstrused(pPathStr)) return XSTDERR; + size_t nLength = strlen(pPathStr); + + if (pPathStr[nLength - 1] == '/') + return xstrncpy(pPath->sPath, sizeof(pPath->sPath), pPathStr); + + xarray_t *pArr = xstrsplit(pPathStr, "/"); + if (pArr == NULL) return xstrncpy(pPath->sFile, sizeof(pPath->sFile), pPathStr); + + if (pPathStr[0] == '/') + { + pPath->sPath[0] = '/'; + pPath->sPath[1] = XSTR_NUL; + } + + size_t i, nUsed = XArray_Used(pArr); + size_t nAvail = sizeof(pPath->sPath); + + for (i = 0; i < nUsed; i++) + { + const char *pEntry = (const char*)XArray_GetData(pArr, i); + if (pEntry == NULL) continue; + + if (i + 1 < nUsed) + { + nAvail = xstrncatf(pPath->sPath, nAvail, "%s/", pEntry); + continue; + } + + return xstrncpy(pPath->sFile, sizeof(pPath->sFile), pEntry); + } + + return XSTDNON; +} + +int XPath_PermToMode(const char *pPerm, xmode_t *pMode) +{ + *pMode = 0; + + size_t nPermLen = strnlen(pPerm, XPERM_LEN); + if (nPermLen < XPERM_LEN) return XSTDERR; + +#ifndef _WIN32 + *pMode |= pPerm[0] == 'r' ? S_IRUSR : 0; + *pMode |= pPerm[1] == 'w' ? S_IWUSR : 0; + *pMode |= pPerm[2] == 'x' ? S_IXUSR : 0; + + *pMode |= pPerm[3] == 'r' ? S_IRGRP : 0; + *pMode |= pPerm[4] == 'w' ? S_IWGRP : 0; + *pMode |= pPerm[5] == 'x' ? S_IXGRP : 0; + + *pMode |= pPerm[6] == 'r' ? S_IROTH : 0; + *pMode |= pPerm[7] == 'w' ? S_IWOTH : 0; + *pMode |= pPerm[8] == 'x' ? S_IXOTH : 0; +#else + *pMode |= pPerm[0] == 'r' ? _S_IREAD : 0; + *pMode |= pPerm[1] == 'w' ? _S_IWRITE : 0; +#endif + return XSTDOK; +} + +int XPath_ModeToChmod(char *pOutput, size_t nSize, xmode_t nMode) +{ + int nOwner, nGroup, nOthers; + nOwner = nGroup = nOthers = 0; + +#ifndef _WIN32 + nOwner = (nMode & S_IRUSR) ? 4 : 0; + nOwner += (nMode & S_IWUSR) ? 2 : 0; + nOwner += (nMode & S_IXUSR) ? 1 : 0; + + nGroup = (nMode & S_IRGRP) ? 4 : 0; + nGroup += (nMode & S_IWGRP) ? 2 : 0; + nGroup += (nMode & S_IXGRP) ? 1 : 0; + + nOthers = (nMode & S_IROTH) ? 4 : 0; + nOthers += (nMode & S_IWOTH) ? 2 : 0; + nOthers += (nMode & S_IXOTH) ? 1 : 0; +#else + nOwner = (nMode & _S_IREAD) ? 4 : 0; + nOwner += (nMode & _S_IWRITE) ? 2 : 0; +#endif + + return xstrncpyf(pOutput, nSize, "%d%d%d", nOwner, nGroup, nOthers); +} + +int XPath_ModeToPerm(char *pOutput, size_t nSize, xmode_t nMode) +{ + pOutput[0] = '\0'; + if (nSize < XPERM_LEN + 1) return 0; + +#ifndef _WIN32 + pOutput[0] = (nMode & S_IRUSR) ? 'r' : '-'; + pOutput[1] = (nMode & S_IWUSR) ? 'w' : '-'; + pOutput[2] = (nMode & S_IXUSR) ? 'x' : '-'; + + pOutput[3] = (nMode & S_IRGRP) ? 'r' : '-'; + pOutput[4] = (nMode & S_IWGRP) ? 'w' : '-'; + pOutput[5] = (nMode & S_IXGRP) ? 'x' : '-'; + + pOutput[6] = (nMode & S_IROTH) ? 'r' : '-'; + pOutput[7] = (nMode & S_IWOTH) ? 'w' : '-'; + pOutput[8] = (nMode & S_IXOTH) ? 'x' : '-'; +#else + pOutput[0] = (nMode & _S_IREAD) ? 'r' : '-'; + pOutput[1] = (nMode & _S_IWRITE) ? 'w' : '-'; +#endif + + pOutput[XPERM_LEN] = 0; + return XPERM_LEN; +} + +int XPath_SetPerm(const char *pPath, const char *pPerm) +{ + xmode_t nMode = 0; + if (!XPath_PermToMode(pPerm, &nMode)) return XSTDERR; + return (xchmod(pPath, nMode) < 0) ? XSTDERR : XSTDOK; +} + +int XPath_GetPerm(char *pOutput, size_t nSize, const char *pPath) +{ + struct stat statbuf; + xstat(pPath, &statbuf); + return XPath_ModeToPerm(pOutput, nSize, statbuf.st_mode); +} + +int XPath_CopyFile(const char *pSrc, const char *pDst) +{ + xfile_t srcFile; + + if (XFile_Open(&srcFile, pSrc, NULL, NULL) >= 0) + { + int nRet = XFILE_INVALID; + xfile_t dstFile; + + if (XFile_Open(&dstFile, pDst, "cwt", NULL) >= 0) + { + nRet = XFile_Copy(&srcFile, &dstFile); + XFile_Close(&dstFile); + } + + XFile_Close(&srcFile); + return nRet; + } + + return XFILE_INVALID; +} + +int XPath_Read(const char *pPath, uint8_t *pBuffer, size_t nSize) +{ + xfile_t file; + XFile_Open(&file, pPath, NULL, NULL); + if (file.nFD < 0) return XSTDERR; + + int nBytes = XFile_Read(&file, pBuffer, nSize); + size_t nTermPosit = (nBytes > 0) ? nBytes : 0; + pBuffer[nTermPosit] = '\0'; + + XFile_Close(&file); + return nBytes; +} + +uint8_t* XPath_Load(const char *pPath, size_t* pSize) +{ + xfile_t file; + *pSize = 0; + + XFile_Open(&file, pPath, NULL, NULL); + if (file.nFD < 0) return NULL; + + uint8_t *pData = XFile_Load(&file, pSize); + XFile_Close(&file); + return pData; +} + +size_t XPath_LoadBuffer(const char *pPath, xbyte_buffer_t *pBuffer) +{ + if (pPath == NULL || pBuffer == NULL) return 0; + XByteBuffer_Init(pBuffer, XSTDNON, XFALSE); + size_t nSize = 0; + + uint8_t* pData = XPath_Load(pPath, &nSize); + if (pData != NULL) + { + pBuffer->nSize = nSize + 1; + pBuffer->nUsed = nSize; + pBuffer->pData = pData; + } + + return nSize; +} + +int XPath_Write(const char *pPath, const char *pFlags, const uint8_t *pData, size_t nSize) +{ + if (pPath == NULL || pData == NULL || !nSize) return XSTDERR; + + xfile_t file; + if (XFile_Open(&file, pPath, pFlags, NULL) < 0) return XSTDERR; + + int nLeft = (int)nSize; + int nDone = 0; + + while (nLeft > 0) + { + int nBytes = XFile_Write(&file, &pData[nDone], nSize); + if (nBytes <= 0) + { + XFile_Close(&file); + return nDone; + } + + nDone += nBytes; + nLeft -= nBytes; + } + + XFile_Close(&file); + return nDone; +} + +int XPath_WriteBuffer(const char *pPath, const char *pFlags, xbyte_buffer_t *pBuffer) +{ + if (pPath == NULL || pBuffer == NULL) return XSTDERR; + return XPath_Write(pPath, pFlags, pBuffer->pData, pBuffer->nUsed); +} + +int XDir_Open(xdir_t *pDir, const char *pPath) +{ + pDir->nOpen = XSTDNON; + pDir->pPath = pPath; + pDir->nFirstFile = 0; + pDir->pDirectory = NULL; + pDir->pCurrEntry = NULL; + if (pPath == NULL) return XSTDERR; + +#ifdef _WIN32 + pDir->pDirectory = FindFirstFile(pPath, &pDir->entry); + if (pDir->pDirectory == INVALID_HANDLE_VALUE) return XSTDERR; + pDir->nFirstFile = 1; +#else + pDir->pDirectory = opendir(pPath); + if (pDir->pDirectory == NULL) return XSTDERR; + pDir->pEntry = NULL; +#endif + + pDir->nOpen = 1; + return XSTDOK; +} + +void XDir_Close(xdir_t *pDir) +{ + if (pDir->nOpen && pDir->pDirectory) + { +#ifdef _WIN32 + FindClose(pDir->pDirectory); +#else + closedir(pDir->pDirectory); + pDir->pEntry = NULL; +#endif + pDir->pDirectory = NULL; + pDir->pCurrEntry = NULL; + pDir->nFirstFile = 0; + pDir->nOpen = 0; + } +} + +int XDir_Read(xdir_t *pDir, char *pFile, size_t nSize) +{ + if (!pDir || !pDir->nOpen) return XSTDERR; + +#ifdef _WIN32 + if (pDir->nFirstFile) + { + pDir->nFirstFile = 0; + + if (strcmp(".", pDir->entry.cFileName) && + strcmp("..", pDir->entry.cFileName)) + { + if (pFile != NULL && nSize > 0) + xstrncpy(pFile, nSize, pDir->entry.cFileName); + + pDir->pCurrEntry = pDir->entry.cFileName; + return XSTDOK; + } + } + + while (FindNextFile(pDir->pDirectory, &pDir->entry)) + { + /* Found an entry, but ignore . and .. */ + if (!strcmp(".", pDir->entry.cFileName) || + !strcmp("..", pDir->entry.cFileName)) + continue; + + if (pFile != NULL && nSize > 0) + xstrncpy(pFile, nSize, pDir->entry.cFileName); + + pDir->pCurrEntry = pDir->entry.cFileName; + return XSTDOK; + } +#else + while((pDir->pEntry = readdir(pDir->pDirectory)) != NULL) + { + /* Found an entry, but ignore . and .. */ + if (strcmp(".", pDir->pEntry->d_name) == 0 || + strcmp("..", pDir->pEntry->d_name) == 0) + continue; + + if (pFile != NULL && nSize > 0) + strncpy(pFile, pDir->pEntry->d_name, nSize); + + pDir->pCurrEntry = pDir->pEntry->d_name; + return XSTDOK; + } +#endif + + return XSTDNON; +} + +int XDir_Valid(const char *pPath) +{ + struct stat statbuf = {0}; + int nStatus = stat(pPath, &statbuf); + if (nStatus < 0) return nStatus; + + nStatus = S_ISDIR(statbuf.st_mode); + if (!nStatus) errno = ENOTDIR; + return nStatus; +} + +int XDir_Make(char *pPath, xmode_t mode) +{ + if ((XPath_Exists(pPath) == 0) && + (xmkdir(pPath, mode) < 0) && + (errno != EEXIST)) return 0; + + return 1; +} + +int XDir_Create(const char *pDir, xmode_t nMode) +{ + if (XPath_Exists(pDir)) return 1; + char sDir[XPATH_MAX]; + int nStatus = 0; + + size_t nLen = xstrncpyf(sDir, sizeof(sDir), "%s", pDir); + if (!nLen) return nStatus; + + if (sDir[nLen-1] == '/') sDir[nLen-1] = 0; + char *pOffset = NULL; + + for (pOffset = sDir + 1; *pOffset; pOffset++) + { + if (*pOffset == '/') + { + *pOffset = 0; + nStatus = XDir_Make(sDir, nMode); + if (nStatus <= 0) return nStatus; + *pOffset = '/'; + } + } + + return XDir_Make(sDir, nMode); +} + +int XDir_Unlink(const char *pPath) +{ + struct stat statbuf; + if (!stat(pPath, &statbuf)) + { + return (S_ISDIR(statbuf.st_mode)) ? + XDir_Remove(pPath) : xunlink(pPath); + } + + return XFILE_INVALID; +} + +int XDir_Remove(const char *pPath) +{ + size_t nLength = strlen(pPath); + int nStatus = XFILE_INVALID; + xdir_t dir; + + if (XDir_Open(&dir, pPath) > 0) + { + while (XDir_Read(&dir, NULL, 0) > 0) + { + size_t nSize = nLength + strlen(dir.pCurrEntry) + 2; + char *pNewPath = (char*)malloc(nSize); + + if (pNewPath == NULL) + { + XDir_Close(&dir); + return XFILE_INVALID; + } + + size_t nLen = xstrncpyf(pNewPath, nSize, "%s/%s", pPath, dir.pCurrEntry); + if (nLen > 0) nStatus = XDir_Unlink(pNewPath); + free(pNewPath); + } + + XDir_Close(&dir); + xrmdir(pPath); + } + + return nStatus; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Advanced file search implementation +/////////////////////////////////////////////////////////////////////////////////////////////// + +void XFile_InitEntry(xfile_entry_t *pEntry) +{ + pEntry->sPath[0] = XSTR_NUL; + pEntry->sName[0] = XSTR_NUL; + pEntry->sLink[0] = XSTR_NUL; + pEntry->eType = XF_UNKNOWN; + pEntry->pRealPath = NULL; + pEntry->nLinkCount = 0; + pEntry->nTime = 0; + pEntry->nSize = 0; + pEntry->nGID = 0; + pEntry->nUID = 0; +} + +void XFile_CreateEntry(xfile_entry_t *pEntry, const char *pName, const char *pPath, struct stat *pStat) +{ + XFile_InitEntry(pEntry); + + XPath_ModeToPerm(pEntry->sPerm, sizeof(pEntry->sPerm), pStat->st_mode); + if (pName != NULL) xstrncpy(pEntry->sName, sizeof(pEntry->sName), pName); + if (pPath != NULL) xstrncpy(pEntry->sPath, sizeof(pEntry->sPath), pPath); + + pEntry->eType = XFile_GetType(pStat->st_mode); + pEntry->nLinkCount = pStat->st_nlink; + pEntry->nTime = pStat->st_atime; + pEntry->nSize = pStat->st_size; + pEntry->nGID = pStat->st_gid; + pEntry->nUID = pStat->st_uid; + + if (pEntry->eType == XF_SYMLINK && pName != NULL && pPath != NULL) + { + char sPath[XPATH_MAX]; + xstrncpyf(sPath, sizeof(sPath), "%s%s", pPath, pName); + + int nLen = readlink(sPath, pEntry->sLink, sizeof(pEntry->sLink)-1); + if (nLen < 0) nLen = 0; + + pEntry->sLink[nLen] = XSTR_NUL; + pEntry->pRealPath = realpath(sPath, NULL); + } +} + +xfile_entry_t* XFile_AllocEntry() +{ + xfile_entry_t *pEntry = (xfile_entry_t*)malloc(sizeof(xfile_entry_t)); + if (pEntry == NULL) return NULL; + + XFile_InitEntry(pEntry); + return pEntry; +} + +xfile_entry_t* XFile_NewEntry(const char *pName, const char *pPath, struct stat *pStat) +{ + xfile_entry_t *pEntry = (xfile_entry_t*)malloc(sizeof(xfile_entry_t)); + if (pEntry == NULL) return NULL; + + XFile_CreateEntry(pEntry, pName, pPath, pStat); + return pEntry; +} + +void XFile_FreeEntry(xfile_entry_t *pEntry) +{ + if (pEntry != NULL) + { + if (pEntry->pRealPath != NULL) + free(pEntry->pRealPath); + + free(pEntry); + } +} + +static void XFile_ArrayClearCb(xarray_data_t *pItem) +{ + if (pItem == NULL) return; + free(pItem->pData); +} + +static int XFile_ErrorCallback(xfile_search_t *pSearch, const char *pStr, ...) +{ + if (pSearch == NULL) return XSTDERR; + else if (pSearch->callback == NULL) return XSTDOK; + + char sMessage[XSTR_MID]; + va_list args; + + va_start(args, pStr); + xstrncpyarg(sMessage, sizeof(sMessage), pStr, args); + va_end(args); + + if (pSearch->callback(pSearch, NULL, sMessage) < 0) + { + XSYNC_ATOMIC_SET(pSearch->pInterrupted, 1); + return XSTDERR; + } + + return XSTDOK; +} + +static int XFile_SearchCallback(xfile_search_t *pSearch, xfile_entry_t *pEntry) +{ + if (pSearch == NULL || pEntry == NULL) return XSTDERR; + + int nReturnValue = (pSearch->callback != NULL) ? + pSearch->callback(pSearch, pEntry, NULL) : XSTDOK; + + if (nReturnValue > 0) + { + if (XArray_AddData(&pSearch->fileArray, pEntry, 0) < 0) + { + XFile_ErrorCallback(pSearch, "Failed to append entry: %s%s", pEntry->sPath, pEntry->sName); + return XSTDERR; + } + + return XSTDOK; + } + else if (nReturnValue < 0) + { + XSYNC_ATOMIC_SET(pSearch->pInterrupted, 1); + XFile_FreeEntry(pEntry); + return XSTDERR; + } + + XFile_FreeEntry(pEntry); + return XSTDNON; +} + +static xbool_t XFile_SearchTokens(xarray_t *pTokens, const char *pName, size_t nLength) +{ + size_t nUsed = XArray_Used(pTokens); + if (!nUsed) return XFALSE; + size_t i, nOffset = 0; + + for (i = 0; i < nUsed; i++) + { + const char *pTok = (const char*)XArray_GetData(pTokens, i); + if (xstrused(pTok)) + { + int nPosit = xstrnsrc(pName, nLength, pTok, nOffset); + if (nPosit < 0) return XFALSE; + nOffset += nPosit; + } + } + + return XTRUE; +} + +static xbool_t XFile_SearchMulty(xarray_t *pTokens, const char *pName, size_t nLength) +{ + size_t i, nCount = XArray_Used(pTokens); + xbool_t bFound = XFALSE; + + for (i = 0; i < nCount; i++) + { + xarray_data_t *pArrData = XArray_Get(pTokens, i); + if (pArrData == NULL || pArrData->pData == NULL) continue; + + bFound = pArrData->nKey != XSTDOK ? + !strncmp((const char *)pArrData->pData, pName, nLength) : + XFile_SearchTokens((xarray_t*)pArrData->pData, pName, nLength); + + if (bFound) break; + } + + return bFound; +} + +static xbool_t XFile_SearchName(xfile_search_t *pSearch, const char *pFileName) +{ + size_t nLength = strlen(pFileName); + xbool_t bFound = pSearch->bMulty ? + XFile_SearchMulty(pSearch->pTokens, pFileName, nLength) : + XFile_SearchTokens(pSearch->pTokens, pFileName, nLength); + + return bFound; +} + +static xbool_t XFile_CheckCriteria(xfile_search_t *pSearch, const char *pPath, const char *pName, struct stat *pStat) +{ + if (pSearch->nLinkCount >= 0 && pSearch->nLinkCount != pStat->st_nlink) return XFALSE; + if (pSearch->nFileSize >= 0 && pSearch->nFileSize != pStat->st_size) return XFALSE; + + if (pSearch->nPermissions) + { + char buff[XPERM_MAX]; + XPath_ModeToChmod(buff, sizeof(buff), pStat->st_mode); + if (atoi(buff) != pSearch->nPermissions) return XFALSE; + } + + if (pSearch->nFileTypes) + { + xfile_type_t eType = XFile_GetType(pStat->st_mode); + if (!XFILE_CHECK_FL(pSearch->nFileTypes, eType)) return XFALSE; + } + + if (xstrused(pSearch->pFileName)) + { + char sName[XPATH_MAX]; + if (pSearch->bInsensitive) xstrncase(sName, sizeof(sName), XSTR_LOWER, pName); + const char *pSearchName = pSearch->bInsensitive ? (const char *)sName : pName; + + xbool_t bFound = pSearch->pTokens != NULL ? + XFile_SearchName(pSearch, pSearchName) : + !strcmp(pSearch->pFileName, pSearchName); + + if (!bFound) return XFALSE; + } + + if (xstrused(pSearch->sText)) + { + xfile_type_t eType = XFile_GetType(pStat->st_mode); + if (eType != XF_REGULAR) return XFALSE; + + char sPath[XPATH_MAX]; + xstrncpyf(sPath, sizeof(sPath), "%s%s", pPath, pName); + + xbyte_buffer_t buffer; + XPath_LoadBuffer(sPath, &buffer); + if (buffer.pData == NULL) return XFALSE; + + char *pBuffer = (char*)buffer.pData; + if (pSearch->bInsensitive) xstrcase((char*)pBuffer, XSTR_LOWER); + int nPosit = xstrsrc(pBuffer, pSearch->sText); + + XByteBuffer_Clear(&buffer); + if (nPosit < 0) return XFALSE; + } + + return XTRUE; +} + +void XFile_SearchClearCb(xarray_data_t *pArrData) +{ + if (pArrData == NULL || pArrData->pData == NULL) return; + else if (!pArrData->nKey) free(pArrData->pData); + else XArray_Destroy((xarray_t*)pArrData->pData); +} + +static xarray_t* XFile_TokenizeName(xfile_search_t *pSrcCtx, const char *pFileName) +{ + xarray_t *pTokens = NULL; + + if (xstrsrc(pFileName, ";") >= 0) + pTokens = xstrsplit(pFileName, ";"); + else if (xstrsrc(pFileName, "*") >= 0) + return xstrsplit(pFileName, "*"); + + if (pTokens == NULL) return NULL; + size_t i, nUsed = XArray_Used(pTokens); + pSrcCtx->bMulty = XTRUE; + + for (i = 0; i < nUsed; i++) + { + char *pToken = XArray_GetData(pTokens, i); + if (xstrsrc(pToken, "*") >= 0) + { + xarray_t *pSubTokens = xstrsplit(pToken, "*"); + if (pSubTokens != NULL) + { + xarray_data_t *pArrData = XArray_Get(pTokens, i); + pArrData->pData = pSubTokens; + pArrData->nKey = XSTDOK; + + pTokens->clearCb = XFile_SearchClearCb; + free(pToken); + } + } + } + + return pTokens; +} + +void XFile_SearchInit(xfile_search_t *pSrcCtx, const char *pFileName) +{ + pSrcCtx->bInsensitive = XFALSE; + pSrcCtx->bRecursive = XFALSE; + pSrcCtx->bMulty = XFALSE; + pSrcCtx->callback = NULL; + pSrcCtx->pUserCtx = NULL; + pSrcCtx->nInterrupted = 0; + + pSrcCtx->pInterrupted = &pSrcCtx->nInterrupted; + pSrcCtx->pTokens = XFile_TokenizeName(pSrcCtx, pFileName); + + pSrcCtx->pFileName = pFileName; + pSrcCtx->sText[0] = XSTR_NUL; + pSrcCtx->nPermissions = 0; + pSrcCtx->nFileTypes = 0; + pSrcCtx->nLinkCount = -1; + pSrcCtx->nFileSize = -1; + + XArray_Init(&pSrcCtx->fileArray, XSTDNON, XFALSE); + pSrcCtx->fileArray.clearCb = XFile_ArrayClearCb; +} + +void XFile_SearchDestroy(xfile_search_t *pSrcCtx) +{ + if (pSrcCtx == NULL) return; + XSYNC_ATOMIC_SET(pSrcCtx->pInterrupted, 1); + + if (pSrcCtx->pTokens != NULL) + { + XArray_Destroy(pSrcCtx->pTokens); + pSrcCtx->pTokens = NULL; + } + + pSrcCtx->fileArray.clearCb = XFile_ArrayClearCb; + XArray_Destroy(&pSrcCtx->fileArray); +} + +xfile_entry_t* XFile_GetEntry(xfile_search_t *pSearch, int nIndex) +{ + if (nIndex >= XArray_Used(&pSearch->fileArray)) return NULL; + return (xfile_entry_t*)XArray_GetData(&pSearch->fileArray, nIndex); +} + +int XFile_Search(xfile_search_t *pSearch, const char *pDirectory) +{ + if (pDirectory == NULL) return XSTDERR; + size_t nDirLen = strlen(pDirectory); + + xdir_t dirHandle; + if (XDir_Open(&dirHandle, pDirectory) < 0) + { + XFile_ErrorCallback(pSearch, "Failed to open directory: %s", pDirectory); + return XSYNC_ATOMIC_GET(pSearch->pInterrupted) ? XSTDERR : XSTDOK; + } + + while (XDir_Read(&dirHandle, NULL, 0) > 0 && !XSYNC_ATOMIC_GET(pSearch->pInterrupted)) + { + char sFullPath[XPATH_MAX]; + char sDirPath[XPATH_MAX]; + struct stat statbuf; + + /* Don't add slash twice if directory already contains slash character at the end */ + const char *pSlash = (pDirectory[nDirLen - 1] != '/') ? "/" : XSTR_EMPTY; + const char *pEntryName = dirHandle.pCurrEntry; + + xstrncpyf(sDirPath, sizeof(sDirPath), "%s%s", pDirectory, pSlash); + xstrncpyf(sFullPath, sizeof(sFullPath), "%s%s", sDirPath, pEntryName); + + if (xstat(sFullPath, &statbuf) < 0) + { + XFile_ErrorCallback(pSearch, "Failed to stat file: %s", sFullPath); + if (XSYNC_ATOMIC_GET(pSearch->pInterrupted)) return XSTDERR; + continue; + } + + if (XFile_CheckCriteria(pSearch, sDirPath, pEntryName, &statbuf)) + { + xfile_entry_t *pEntry = XFile_NewEntry(pEntryName, sDirPath, &statbuf); + if (pEntry == NULL) + { + XFile_ErrorCallback(pSearch, "Failed to alloc entry: %s", sFullPath); + XDir_Close(&dirHandle); + return XSTDERR; + } + + if (XFile_SearchCallback(pSearch, pEntry) < 0) + { + XDir_Close(&dirHandle); + return XSTDERR; + } + } + + /* Recursive search */ + if (pSearch->bRecursive && + S_ISDIR(statbuf.st_mode) && + XFile_Search(pSearch, sFullPath) < 0) + { + XDir_Close(&dirHandle); + return XSTDERR; + } + } + + XDir_Close(&dirHandle); + return XSTDOK; +} \ No newline at end of file diff --git a/src/xfs.h b/src/xfs.h new file mode 100644 index 0000000..c9fbbb3 --- /dev/null +++ b/src/xfs.h @@ -0,0 +1,187 @@ +/*! + * @file libxutils/src/xfs.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the NIX/POSIX + * standart file and directory operations. + */ + +#ifndef __XUTILS_XFILE_H__ +#define __XUTILS_XFILE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "xbuf.h" +#include "xtype.h" +#include "array.h" + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#define XFILE_INVALID -1 +#define XFILE_UNSETRC 0 +#define XFILE_SUCCESS 1 + +typedef struct XFile { + uint64_t nPosit; + xmode_t nMode; + size_t nBlockSize; + size_t nSize; + int nAlloc:1; + int nFlags; + int nFD; +} xfile_t; + +typedef struct XDir { + const char* pPath; + char* pCurrEntry; +#ifdef _WIN32 + WIN32_FIND_DATA entry; + HANDLE pDirectory; +#else + struct dirent* pEntry; + DIR* pDirectory; +#endif + uint8_t nFirstFile; + uint8_t nOpen; +} xdir_t; + +typedef struct XPath { + char sPath[XPATH_MAX]; + char sFile[XNAME_MAX]; +} xpath_t; + +#define XFILE_CHECK_FL(c, f) (((c) & (f)) == (f)) + +int xstat(const char *pPath, struct stat *pStat); +int xmkdir(const char* pPath, xmode_t nMode); +int xunlink(const char* pPath); +int xrmdir(const char* pPath); +int xclose(int nFD); + +int XFile_Open(xfile_t *pFile, const char *pPath, const char *pFlags, const char *pPerms); +xfile_t* XFile_New(const char *pPath, const char *pFlags, const char *pPerms); +void XFile_Close(xfile_t *pFile); +void XFile_Clean(xfile_t *pFile); + +size_t XFile_Seek(xfile_t *pFile, uint64_t nPosit, int nOffset); +int XFile_Write(xfile_t *pFile, const void *pBuff, size_t nSize); +int XFile_Read(xfile_t *pFile, void *pBuff, size_t nSize); + +int XFile_GetStats(xfile_t *pFile); +int XFile_Copy(xfile_t *pIn, xfile_t *pOut); +int XFile_GetLine(xfile_t *pFile, char* pLine, size_t nSize); +int XFile_GetLineCount(xfile_t *pFile); +int XFile_ReadLine(xfile_t *pFile, char* pLine, size_t nSize, int nLineNumber); +uint8_t* XFile_Load(xfile_t *pFile, size_t *pSize); + +char XPath_GetType(xmode_t nMode); +int XPath_Exists(const char *pPath); +int XPath_Parse(xpath_t *pPath, const char *pPathStr); +int XPath_SetPerm(const char *pPath, const char *pPerm); +int XPath_GetPerm(char *pOutput, size_t nSize, const char *pPath); +int XPath_PermToMode(const char *pPerm, xmode_t *pMode); +int XPath_ModeToPerm(char *pOutput, size_t nSize, xmode_t nMode); +int XPath_ModeToChmod(char *pOutput, size_t nSize, xmode_t nMode); + +uint8_t* XPath_Load(const char *pPath, size_t* pSize); +int XPath_CopyFile(const char *pSrc, const char *pDst); +int XPath_Read(const char *pPath, uint8_t *pBuffer, size_t nSize); +int XPath_Write(const char *pPath, const char *pFlags, const uint8_t *pData, size_t nSize); +int XPath_WriteBuffer(const char *pPath, const char *pFlags, xbyte_buffer_t *pBuffer); +size_t XPath_LoadBuffer(const char *pPath, xbyte_buffer_t *pBuffer); + +void XDir_Close(xdir_t *pDir); +int XDir_Open(xdir_t *pDir, const char *pPath); +int XDir_Read(xdir_t *pDir, char *pFile, size_t nSize); +int XDir_Create(const char *pDir, xmode_t mode); +int XDir_Unlink(const char *pPath); +int XDir_Valid(const char *pPath); +int XDir_Remove(const char *pPath); + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Advanced file search implementation +/////////////////////////////////////////////////////////////////////////////////////////////// + +typedef enum { + XF_UNKNOWN = 0, + XF_BLOCK_DEVICE = (1 << 0), + XF_CHAR_DEVICE = (1 << 1), + XF_DIRECTORY = (1 << 2), + XF_REGULAR = (1 << 3), + XF_SYMLINK = (1 << 4), + XF_SOCKET = (1 << 5), + XF_PIPE = (1 << 6) +} xfile_type_t; + +typedef struct XFileEntry { + char sPath[XPATH_MAX]; + char sLink[XPATH_MAX]; + char sName[XNAME_MAX]; + char sPerm[XPERM_MAX]; + xfile_type_t eType; + size_t nLinkCount; + uint32_t nGID; + uint32_t nUID; + time_t nTime; + size_t nSize; + char *pRealPath; +} xfile_entry_t; + +void XFile_CreateEntry(xfile_entry_t *pEntry, const char *pName, const char *pPath, struct stat *pStat); +xfile_entry_t* XFile_NewEntry(const char *pName, const char *pPath, struct stat *pStat); +xfile_entry_t* XFile_AllocEntry(); + +void XFile_InitEntry(xfile_entry_t *pEntry); +void XFile_FreeEntry(xfile_entry_t *pEntry); +char XFile_GetTypeChar(xfile_type_t eType); + +typedef struct XFileSearch xfile_search_t; +typedef int(*xfile_search_cb_t)(xfile_search_t *pSearch, xfile_entry_t *pEntry, const char *pMsg); + +typedef struct XFileSearch { + /* Search context */ + xfile_search_cb_t callback; // Search callback + xarray_t fileArray; // Found file array + xbool_t bInsensitive; // Case insensitive search + xbool_t bRecursive; // Recursive search + void *pUserCtx; // User space + + /* Search criteria */ + const char* pFileName; // Needed file name + char sText[XSTR_MID]; // Containing text + xarray_t *pTokens; // Search name tokens + int nPermissions; // Needed file permissions + int nLinkCount; // Needed file link count + int nFileTypes; // Needed file types + int nFileSize; // Needed file size + xbool_t bMulty; // Multy file name search + + xatomic_t *pInterrupted; // Interrupt flag pointer + xatomic_t nInterrupted; // Interrupt flag +} xfile_search_t; + +void XFile_SearchInit(xfile_search_t *pSrcCtx, const char *pFileName); +void XFile_SearchDestroy(xfile_search_t *pSrcCtx); +int XFile_Search(xfile_search_t *pSearch, const char *pDirectory); +xfile_entry_t* XFile_GetEntry(xfile_search_t *pSearch, int nIndex); + +/////////////////////////////////////////////////////////////////////////////////////////////// +// End of advanced file search implementation +/////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif + + +#endif /* __XUTILS_XFILE_H__ */ diff --git a/src/xjson.c b/src/xjson.c new file mode 100644 index 0000000..3f1d1c2 --- /dev/null +++ b/src/xjson.c @@ -0,0 +1,1242 @@ +/*! + * @file libxutils/src/xjson.c + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the lexical analyzer + * and recursive descent parser with JSON grammar + */ + +#ifdef _XUTILS_USE_GNU +#define _GNU_SOURCE +#endif + +#include "xstd.h" +#include "xmap.h" +#include "xstr.h" +#include "array.h" +#include "xjson.h" + +#define XOBJ_INITIAL_SIZE 2 +#define XJSON_IDENT_INC 1 +#define XJSON_IDENT_DEC 0 + +#define XJSON_NUMBER_MAX 64 +#define XJSON_BOOL_MAX 6 +#define XJSON_NULL_MAX 5 + +#define XJSON_ASSERT(condition) \ + if (!condition) return XJSON_FAILURE + +typedef struct xjson_iterator_ { + xjson_writer_t *pWriter; + size_t nCurrent; + size_t nUsed; +} xjson_iterator_t; + +size_t XJSON_GetErrorStr(xjson_t *pJson, char *pOutput, size_t nSize) +{ + switch (pJson->nError) + { + case XJSON_ERR_INVALID: + return xstrncpyf(pOutput, nSize, "Invalid item at posit(%zu)", pJson->nOffset); + case XJSON_ERR_EXITS: + return xstrncpyf(pOutput, nSize, "Duplicate Key at posit(%zu)", pJson->nOffset); + case XJSON_ERR_BOUNDS: + return xstrncpyf(pOutput, nSize, "Unexpected EOF at posit(%zu)", pJson->nOffset); + case XJSON_ERR_ALLOC: + return xstrncpyf(pOutput, nSize, "Can not allocate memory for object at posit(%zu)", pJson->nOffset); + case XJSON_ERR_UNEXPECTED: + return xstrncpyf(pOutput, nSize, "Unexpected symbol '%c' at posit(%zu)", pJson->pData[pJson->nOffset], pJson->nOffset); + case XJSON_ERR_NONE: + default: + break; + } + + return xstrncpyf(pOutput, nSize, "Undeclared error"); +} + +///////////////////////////////////////////////////////////////////////// +// Start of lexical parser + +static int XJSON_UnexpectedToken(xjson_t *pJson) +{ + xjson_token_t *pToken = &pJson->lastToken; + pJson->nError = XJSON_ERR_UNEXPECTED; + pJson->nOffset -= pToken->nLength; + + if (pToken->nType == XJSON_TOKEN_QUOTE) + pJson->nOffset -= 2; + + return XJSON_FAILURE; +} + +static int XJSON_UndoLastToken(xjson_t *pJson) +{ + pJson->nOffset -= pJson->lastToken.nLength; + return XJSON_SUCCESS; +} + +static int XJSON_CheckBounds(xjson_t *pJson) +{ + if (pJson->nOffset >= pJson->nDataSize) + { + pJson->nError = XJSON_ERR_BOUNDS; + return XJSON_FAILURE; + } + + return XJSON_SUCCESS; +} + +static int XJSON_NextChar(xjson_t *pJson, char *pChar) +{ + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + char nCharacter = pJson->pData[pJson->nOffset++]; + + /* Skip space and new line characters */ + while (nCharacter == ' ' || + nCharacter == '\n' || + nCharacter == '\t') + { + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + nCharacter = pJson->pData[pJson->nOffset++]; + } + + *pChar = nCharacter; + return XJSON_SUCCESS; +} + +static int XJSON_ParseDigit(xjson_t *pJson, char nCharacter) +{ + xjson_token_t *pToken = &pJson->lastToken; + pToken->nType = XJSON_TOKEN_INVALID; + size_t nPosition = pJson->nOffset; + uint8_t nPoint = 0; + + if (nCharacter == '-') nCharacter = pJson->pData[pJson->nOffset]; + + while (isdigit(nCharacter) || (nPoint < 2 && nCharacter == '.')) + { + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + nCharacter = pJson->pData[pJson->nOffset++]; + + if (nCharacter == '.' && ++nPoint == 2) + { + pToken->nLength = pJson->nOffset - nPosition; + return XJSON_UnexpectedToken(pJson); + } + } + + pToken->nLength = pJson->nOffset - nPosition; + pToken->nType = nPoint ? XJSON_TOKEN_FLOAT : XJSON_TOKEN_INTEGER; + pToken->pData = &pJson->pData[nPosition - 1]; + pJson->nOffset--; + + return XJSON_SUCCESS; +} + +static int XJSON_ParseQuote(xjson_t *pJson) +{ + xjson_token_t *pToken = &pJson->lastToken; + pToken->nType = XJSON_TOKEN_INVALID; + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + + size_t nStart = pJson->nOffset; + char nCurr = 0, nPrev = 0; + + for (;;) + { + if (nCurr == '"' && nPrev != '\\') break; + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + + nPrev = pJson->pData[pJson->nOffset-1]; + nCurr = pJson->pData[pJson->nOffset++]; + } + + pToken->nLength = pJson->nOffset - nStart - 1; + pToken->pData = &pJson->pData[nStart]; + pToken->nType = XJSON_TOKEN_QUOTE; + + return XJSON_SUCCESS; +} + +static int XJSON_IsAlphabet(char nChar) +{ + return ((nChar >= 'a' && nChar <= 'z') || + (nChar >= 'A' && nChar <= 'Z')) ? + XJSON_SUCCESS : XJSON_FAILURE; +} + +static int XJSON_ParseAlphabet(xjson_t *pJson, char nCharacter) +{ + xjson_token_t *pToken = &pJson->lastToken; + pToken->nType = XJSON_TOKEN_INVALID; + size_t nPosition = pJson->nOffset; + + while (XJSON_IsAlphabet(nCharacter)) + { + XJSON_ASSERT(XJSON_CheckBounds(pJson)); + nCharacter = pJson->pData[pJson->nOffset++]; + } + + pToken->nLength = pJson->nOffset - nPosition; + pToken->pData = &pJson->pData[nPosition - 1]; + pJson->nOffset--; + + if (!strncmp((char*)pToken->pData, "null", 4)) + { + pToken->nType = XJSON_TOKEN_NULL; + return XJSON_SUCCESS; + } + + if (!strncmp((char*)pToken->pData, "true", 4) || + !strncmp((char*)pToken->pData, "false", 5)) + { + pToken->nType = XJSON_TOKEN_BOOL; + return XJSON_SUCCESS; + } + + return XJSON_UnexpectedToken(pJson); +} + +static int XJSON_GetNextToken(xjson_t *pJson) +{ + xjson_token_t *pToken = &pJson->lastToken; + pToken->nType = XJSON_TOKEN_INVALID; + pToken->pData = NULL; + pToken->nLength = 0; + char nChar = 0; + + if (!XJSON_NextChar(pJson, &nChar)) + { + pToken->nType = XJSON_TOKEN_EOF; + return XJSON_FAILURE; // End of input + } + + if (nChar == '-' || isdigit(nChar)) return XJSON_ParseDigit(pJson, nChar); + else if (XJSON_IsAlphabet(nChar)) return XJSON_ParseAlphabet(pJson, nChar); + else if (nChar == '"') return XJSON_ParseQuote(pJson); + + pToken->pData = &pJson->pData[pJson->nOffset-1]; + pToken->nLength = 1; + + switch (nChar) + { + case '\0': case EOF: + pToken->nType = XJSON_TOKEN_EOF; + pToken->pData = NULL; + pToken->nLength = 0; + break; + case '{': + pToken->nType = XJSON_TOKEN_LCURLY; + break; + case '}': + pToken->nType = XJSON_TOKEN_RCURLY; + break; + case '[': + pToken->nType = XJSON_TOKEN_LSQUARE; + break; + case ']': + pToken->nType = XJSON_TOKEN_RSQUARE; + break; + case ':': + pToken->nType = XJSON_TOKEN_COLON; + break; + case ',': + pToken->nType = XJSON_TOKEN_COMMA; + break; + default: + return XJSON_UnexpectedToken(pJson); + } + + return XJSON_SUCCESS; +} + +static int XJSON_Expect(xjson_t *pJson, xjson_token_type_t nType) +{ + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + xjson_token_t *pToken = &pJson->lastToken; + + if (pToken->nType == nType) return XJSON_SUCCESS; + return XJSON_UnexpectedToken(pJson); +} + +// End of lexical parser +///////////////////////////////////////////////////////////////////////// + +static xjson_type_t XJSON_GetItemType(xjson_token_type_t eToken) +{ + switch(eToken) + { + case XJSON_TOKEN_INTEGER: return XJSON_TYPE_NUMBER; + case XJSON_TOKEN_QUOTE: return XJSON_TYPE_STRING; + case XJSON_TOKEN_FLOAT: return XJSON_TYPE_FLOAT; + case XJSON_TOKEN_BOOL: return XJSON_TYPE_BOOLEAN; + case XJSON_TOKEN_NULL: return XJSON_TYPE_NULL; + case XJSON_TOKEN_EOF: + case XJSON_TOKEN_COMMA: + case XJSON_TOKEN_COLON: + case XJSON_TOKEN_LCURLY: + case XJSON_TOKEN_RCURLY: + case XJSON_TOKEN_LPAREN: + case XJSON_TOKEN_RPAREN: + case XJSON_TOKEN_LSQUARE: + case XJSON_TOKEN_RSQUARE: + case XJSON_TOKEN_INVALID: + default: break; + } + + return XJSON_TYPE_INVALID; +} + +void XJSON_FreeObject(xjson_obj_t *pObj) +{ + if (pObj != NULL) + { + if (pObj->pData != NULL) + { + if (pObj->nType == XJSON_TYPE_OBJECT) + XMap_Destroy((xmap_t*)pObj->pData); + else if (pObj->nType == XJSON_TYPE_ARRAY) + XArray_Destroy((xarray_t*)pObj->pData); + else free(pObj->pData); + } + + if (pObj->pName != NULL) free(pObj->pName); + if (pObj->nAllocated) free(pObj); + } +} + +static void XJSON_ObjectClearCb(xmap_pair_t *pPair) +{ + if (pPair == NULL) return; + XJSON_FreeObject((xjson_obj_t*)pPair->pData); +} + +static void XJSON_ArrayClearCb(xarray_data_t *pItem) +{ + if (pItem == NULL) return; + XJSON_FreeObject((xjson_obj_t*)pItem->pData); +} + +xjson_error_t XJSON_AddObject(xjson_obj_t *pDst, xjson_obj_t *pSrc) +{ + if (pDst == NULL || pSrc == NULL) return XJSON_ERR_INVALID; + else if (pDst->nType == XJSON_TYPE_OBJECT) + { + xmap_t *pMap = (xmap_t*)pDst->pData; + int nHash = 0; + + xjson_obj_t *pFound = (xjson_obj_t*)XMap_GetIndex(pMap, pSrc->pName, &nHash); + if (pFound != NULL) + { + if (!pDst->nAllowUpdate) return XJSON_ERR_EXITS; + XJSON_FreeObject(pFound); + + int nStatus = XMap_Update(pMap, nHash, pSrc->pName, (void*)pSrc); + return nStatus < 0 ? XJSON_ERR_BOUNDS : XJSON_ERR_NONE; + } + + int nStatus = XMap_Put(pMap, pSrc->pName, (void*)pSrc); + return nStatus < 0 ? XJSON_ERR_ALLOC : XJSON_ERR_NONE; + } + else if (pDst->nType == XJSON_TYPE_ARRAY) + { + xarray_t *pArray = (xarray_t*)pDst->pData; + int nStatus = XArray_AddData(pArray, (void*)pSrc, 0); + return nStatus < 0 ? XJSON_ERR_ALLOC : XJSON_ERR_NONE; + } + + return XJSON_ERR_INVALID; +} + +xjson_obj_t* XJSON_CreateObject(const char *pName, void *pValue, xjson_type_t nType) +{ + xjson_obj_t *pObj = (xjson_obj_t*)malloc(sizeof(xjson_obj_t)); + if (pObj == NULL) return NULL; + + pObj->nAllowUpdate = 0; + pObj->nAllocated = 1; + pObj->pName = NULL; + pObj->pData = pValue; + pObj->nType = nType; + + if (pName == NULL) return pObj; + size_t nLength = strlen(pName); + + if (nLength > 0) + { + pObj->pName = (char*)malloc(nLength + 1); + if (pObj->pName == NULL) + { + free(pObj); + return NULL; + } + + memcpy(pObj->pName, pName, nLength); + pObj->pName[nLength] = '\0'; + } + + return pObj; +} + +xjson_obj_t* XJSON_NewObject(const char *pName, uint8_t nAllowUpdate) +{ + xmap_t *pMap = XMap_New(XOBJ_INITIAL_SIZE); + if (pMap == NULL) return NULL; + + pMap->clearCb = XJSON_ObjectClearCb; + xjson_obj_t *pObj = XJSON_CreateObject(pName, pMap, XJSON_TYPE_OBJECT); + + if (pObj == NULL) + { + XMap_Destroy(pMap); + return NULL; + } + + pObj->nAllowUpdate = nAllowUpdate; + return pObj; +} + +xjson_obj_t* XJSON_NewArray(const char *pName, uint8_t nAllowUpdate) +{ + xarray_t *pArray = XArray_New(XOBJ_INITIAL_SIZE, 0); + if (pArray == NULL) return NULL; + + pArray->clearCb = XJSON_ArrayClearCb; + xjson_obj_t *pObj = XJSON_CreateObject(pName, pArray, XJSON_TYPE_ARRAY); + + if (pObj == NULL) + { + XArray_Destroy(pArray); + return NULL; + } + + pObj->nAllowUpdate = nAllowUpdate; + return pObj; +} + +xjson_obj_t* XJSON_NewU64(const char *pName, uint64_t nValue) +{ + char *pValue = (char*)malloc(XJSON_NUMBER_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_NUMBER_MAX, "%"PRIu64, nValue); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_NUMBER); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddU64(xjson_obj_t *pObject, const char *pName, uint64_t nValue) +{ + xjson_obj_t *pNewObj = XJSON_NewU64(pName, nValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewU32(const char *pName, uint32_t nValue) +{ + char *pValue = (char*)malloc(XJSON_NUMBER_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_NUMBER_MAX, "%u", nValue); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_NUMBER); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddU32(xjson_obj_t *pObject, const char *pName, uint32_t nValue) +{ + xjson_obj_t *pNewObj = XJSON_NewU32(pName, nValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewInt(const char *pName, int nValue) +{ + char *pValue = (char*)malloc(XJSON_NUMBER_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_NUMBER_MAX, "%d", nValue); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_NUMBER); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddInt(xjson_obj_t *pObject, const char *pName, int nValue) +{ + xjson_obj_t *pNewObj = XJSON_NewInt(pName, nValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewFloat(const char *pName, double fValue) +{ + char *pValue = (char*)malloc(XJSON_NUMBER_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_NUMBER_MAX, "%lf", fValue); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_FLOAT); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddFloat(xjson_obj_t *pObject, const char *pName, double fValue) +{ + xjson_obj_t *pNewObj = XJSON_NewFloat(pName, fValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewString(const char *pName, const char *pValue) +{ + char *pSaveValue = xstrdup(pValue); + if (pSaveValue == NULL) return NULL; + + xjson_obj_t *pObj = XJSON_CreateObject(pName, pSaveValue, XJSON_TYPE_STRING); + if (pObj == NULL) + { + free(pSaveValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddString(xjson_obj_t *pObject, const char *pName, const char *pValue) +{ + xjson_obj_t *pNewObj = XJSON_NewString(pName, pValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewBool(const char *pName, int nValue) +{ + char *pValue = (char*)malloc(XJSON_BOOL_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_BOOL_MAX, "%s", nValue ? "true" : "false"); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_BOOLEAN); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddBool(xjson_obj_t *pObject, const char *pName, int nValue) +{ + xjson_obj_t *pNewObj = XJSON_NewBool(pName, nValue); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +xjson_obj_t* XJSON_NewNull(const char *pName) +{ + char *pValue = (char*)malloc(XJSON_NULL_MAX); + if (pValue == NULL) return NULL; + + xstrncpyf(pValue, XJSON_NULL_MAX, "%s", "null"); + xjson_obj_t *pObj = XJSON_CreateObject(pName, pValue, XJSON_TYPE_NULL); + + if (pObj == NULL) + { + free(pValue); + return NULL; + } + + return pObj; +} + +xjson_error_t XJSON_AddNull(xjson_obj_t *pObject, const char *pName) +{ + xjson_obj_t *pNewObj = XJSON_NewNull(pName); + if (pNewObj == NULL) return XJSON_ERR_ALLOC; + xjson_error_t status = XJSON_AddObject(pObject, pNewObj); + if (status != XJSON_ERR_NONE) XJSON_FreeObject(pNewObj); + return status; +} + +/* Forward declararions */ +int XJSON_ParseObject(xjson_t *pJson, xjson_obj_t *pObj); +int XJSON_ParseArray(xjson_t *pJson, xjson_obj_t *pObj); + +static int XJSON_ParseNewObject(xjson_t *pJson, xjson_obj_t *pObj, const char *pName) +{ + xjson_obj_t *pNewObj = XJSON_NewObject(pName, 0); + if (pNewObj == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + if (!XJSON_ParseObject(pJson, pNewObj)) + { + XJSON_FreeObject(pNewObj); + return XJSON_FAILURE; + } + + pJson->nError = XJSON_AddObject(pObj, pNewObj); + if (pJson->nError != XJSON_ERR_NONE) + { + XJSON_FreeObject(pNewObj); + return XJSON_FAILURE; + } + + return XJSON_Expect(pJson, XJSON_TOKEN_RCURLY); +} + +static int XJSON_ParseNewArray(xjson_t *pJson, xjson_obj_t *pObj, const char *pName) +{ + xjson_obj_t *pNewObj = XJSON_NewArray(pName, 0); + if (pNewObj == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + if (!XJSON_ParseArray(pJson, pNewObj)) + { + XJSON_FreeObject(pNewObj); + return XJSON_FAILURE; + } + + pJson->nError = XJSON_AddObject(pObj, pNewObj); + if (pJson->nError != XJSON_ERR_NONE) + { + XJSON_FreeObject(pNewObj); + return XJSON_FAILURE; + } + + return XJSON_Expect(pJson, XJSON_TOKEN_RSQUARE); +} + +static int XJSON_CheckObject(xjson_obj_t *pObj, xjson_type_t nType) +{ + return (pObj != NULL && + pObj->pData != NULL && + pObj->nType == nType) ? + XJSON_SUCCESS : XJSON_FAILURE; +} + +static int XJSON_TokenIsItem(xjson_token_t *pToken) +{ + return (pToken->nType == XJSON_TOKEN_QUOTE || + pToken->nType == XJSON_TOKEN_FLOAT || + pToken->nType == XJSON_TOKEN_BOOL || + pToken->nType == XJSON_TOKEN_NULL || + pToken->nType == XJSON_TOKEN_INTEGER) ? + XJSON_SUCCESS : XJSON_FAILURE; +} + +static char* JSON_LastTokenValue(xjson_t *pJson) +{ + xjson_token_t *pToken = &pJson->lastToken; + char *pValue = malloc(pToken->nLength + 1); + + if (pValue == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return NULL; + } + + memcpy(pValue, pToken->pData, pToken->nLength); + pValue[pToken->nLength] = '\0'; + + return pValue; +} + +static int XJSON_PutItem(xjson_t *pJson, xjson_obj_t *pObj, const char *pName) +{ + xjson_token_t *pToken = &pJson->lastToken; + xjson_type_t nType = XJSON_GetItemType(pToken->nType); + + if (nType == XJSON_TYPE_INVALID) + { + pJson->nError = XJSON_ERR_INVALID; + return XJSON_FAILURE; + } + + char *pValue = JSON_LastTokenValue(pJson); + if (pValue == NULL) return XJSON_FAILURE; + + xjson_obj_t *pNewObj = XJSON_CreateObject(pName, pValue, nType); + if (pNewObj == NULL) + { + free(pValue); + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + pJson->nError = XJSON_AddObject(pObj, pNewObj); + if (pJson->nError != XJSON_ERR_NONE) + { + XJSON_FreeObject(pNewObj); + return XJSON_FAILURE; + } + + return XJSON_SUCCESS; +} + +int XJSON_ParseArray(xjson_t *pJson, xjson_obj_t *pObj) +{ + xjson_token_t *pToken = &pJson->lastToken; + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + + if (pToken->nType == XJSON_TOKEN_RSQUARE) + return XJSON_UndoLastToken(pJson); + else if (XJSON_TokenIsItem(pToken)) + { XJSON_ASSERT(XJSON_PutItem(pJson, pObj, NULL)); } + else if (pToken->nType == XJSON_TOKEN_LCURLY) + { XJSON_ASSERT(XJSON_ParseNewObject(pJson, pObj, NULL)); } + else if (pToken->nType == XJSON_TOKEN_LSQUARE) + { XJSON_ASSERT(XJSON_ParseNewArray(pJson, pObj, NULL)); } + else return XJSON_UnexpectedToken(pJson); + + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + + if (pToken->nType == XJSON_TOKEN_COMMA) + return XJSON_ParseArray(pJson, pObj); + else if (pToken->nType != XJSON_TOKEN_RSQUARE) + return XJSON_UnexpectedToken(pJson); + + return XJSON_UndoLastToken(pJson); +} + +static int XJSON_ParsePair(xjson_t* pJson, xjson_obj_t* pObj) +{ + xjson_token_t* pToken = &pJson->lastToken; + size_t nSize = pToken->nLength + 1; + + char* pPairName = (char*)malloc(nSize); + if (pPairName == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + xstrncpys(pPairName, nSize, pToken->pData, pToken->nLength); + + if (!XJSON_Expect(pJson, XJSON_TOKEN_COLON) || + !XJSON_GetNextToken(pJson)) + { + free(pPairName); + return XJSON_FAILURE; + } + + if (XJSON_TokenIsItem(pToken)) + { + if (!XJSON_PutItem(pJson, pObj, pPairName)) + { + free(pPairName); + return XJSON_FAILURE; + } + } + else if (pToken->nType == XJSON_TOKEN_LCURLY) + { + if (!XJSON_ParseNewObject(pJson, pObj, pPairName)) + { + free(pPairName); + return XJSON_FAILURE; + } + } + else if (pToken->nType == XJSON_TOKEN_LSQUARE) + { + if (!XJSON_ParseNewArray(pJson, pObj, pPairName)) + { + free(pPairName); + return XJSON_FAILURE; + } + } + else + { + free(pPairName); + return XJSON_UnexpectedToken(pJson); + } + + free(pPairName); + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + + if (pToken->nType == XJSON_TOKEN_COMMA) + return XJSON_ParseObject(pJson, pObj); + else if (pToken->nType != XJSON_TOKEN_RCURLY) + return XJSON_UnexpectedToken(pJson); + + return XJSON_UndoLastToken(pJson); +} + +int XJSON_ParseObject(xjson_t *pJson, xjson_obj_t *pObj) +{ + xjson_token_t *pToken = &pJson->lastToken; + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + + if (pToken->nType == XJSON_TOKEN_RCURLY) return XJSON_UndoLastToken(pJson); + else if (pToken->nType == XJSON_TOKEN_QUOTE) return XJSON_ParsePair(pJson, pObj); + else if (pToken->nType == XJSON_TOKEN_COMMA) return XJSON_ParseObject(pJson, pObj); + else if (pToken->nType == XJSON_TOKEN_EOF) return XJSON_FAILURE; + + return XJSON_UnexpectedToken(pJson); +} + +int XJSON_Parse(xjson_t *pJson, const char *pData, size_t nSize) +{ + pJson->nError = XJSON_ERR_NONE; + pJson->nDataSize = nSize; + pJson->pData = pData; + pJson->nOffset = 0; + + xjson_token_t *pToken = &pJson->lastToken; + XJSON_ASSERT(XJSON_GetNextToken(pJson)); + + if (pToken->nType == XJSON_TOKEN_LCURLY) + { + pJson->pRootObj = XJSON_NewObject(NULL, 0); + if (pJson->pRootObj == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + XJSON_ASSERT(XJSON_ParseObject(pJson, pJson->pRootObj)); + return XJSON_Expect(pJson, XJSON_TOKEN_RCURLY); + } + else if (pToken->nType == XJSON_TOKEN_LSQUARE) + { + pJson->pRootObj = XJSON_NewArray(NULL, 0); + if (pJson->pRootObj == NULL) + { + pJson->nError = XJSON_ERR_ALLOC; + return XJSON_FAILURE; + } + + XJSON_ASSERT(XJSON_ParseArray(pJson, pJson->pRootObj)); + return XJSON_Expect(pJson, XJSON_TOKEN_RSQUARE); + } + + return XJSON_UnexpectedToken(pJson); +} + +void XJSON_Destroy(xjson_t *pJson) +{ + XJSON_FreeObject(pJson->pRootObj); + pJson->nDataSize = 0; + pJson->nOffset = 0; + pJson->pData = NULL; +} + +xjson_obj_t* XJSON_GetObject(xjson_obj_t *pObj, const char *pName) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_OBJECT)) return NULL; + return XMap_Get((xmap_t*)pObj->pData, pName); +} + +xjson_obj_t *XJSON_GetOrCreateObject(xjson_obj_t *pObj, const char *pName, uint8_t nAllowUpdate) +{ + xjson_obj_t *pChild = XJSON_GetObject(pObj, pName); + if (pChild != NULL) + { + pChild->nAllowUpdate = nAllowUpdate; + return pChild; + } + + pChild = XJSON_NewObject(pName, nAllowUpdate); + if (pChild == NULL) return NULL; + + if (XJSON_AddObject(pObj, pChild) != XJSON_ERR_NONE) + { + if (pChild != NULL) + { + XJSON_FreeObject(pChild); + pChild = NULL; + } + } + + return pChild; +} + +xjson_obj_t *XJSON_GetOrCreateArray(xjson_obj_t *pObj, const char *pName, uint8_t nAllowUpdate) +{ + xjson_obj_t *pChild = XJSON_GetObject(pObj, pName); + if (pChild != NULL) + { + pChild->nAllowUpdate = nAllowUpdate; + return pChild; + } + + pChild = XJSON_NewArray(pName, nAllowUpdate); + if (pChild == NULL) return NULL; + + if (XJSON_AddObject(pObj, pChild) != XJSON_ERR_NONE) + { + if (pChild != NULL) + { + XJSON_FreeObject(pChild); + pChild = NULL; + } + } + + return pChild; +} + +xjson_obj_t* XJSON_GetArrayItem(xjson_obj_t *pObj, size_t nIndex) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_ARRAY)) return NULL; + return XArray_GetData((xarray_t*)pObj->pData, nIndex); +} + +int XJSON_RemoveArrayItem(xjson_obj_t *pObj, size_t nIndex) +{ + xjson_obj_t *pTmp = XJSON_GetArrayItem(pObj, nIndex); + if (pTmp != NULL) + { + XArray_Remove((xarray_t*)pObj->pData, nIndex); + XJSON_FreeObject(pTmp); + } + return 0; +} + +size_t XJSON_GetArrayLength(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_ARRAY)) return 0; + return XArray_Used((xarray_t*)pObj->pData); +} + +int XJSON_GetInt(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_NUMBER)) return 0; + return atoi((const char*)pObj->pData); +} + +double XJSON_GetFloat(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_FLOAT)) return 0.; + return atof((const char*)pObj->pData); +} + +uint32_t XJSON_GetU32(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_NUMBER)) return 0; + return atol((const char*)pObj->pData); +} + +uint64_t XJSON_GetU64(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_NUMBER)) return 0; + return strtoull((const char*)pObj->pData, NULL, 0); +} + +uint8_t XJSON_GetBool(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_BOOLEAN)) return 0; + return !strncmp((const char*)pObj->pData, "true", 4); +} + +const char* XJSON_GetString(xjson_obj_t *pObj) +{ + if (!XJSON_CheckObject(pObj, XJSON_TYPE_STRING)) return ""; + return (const char*)pObj->pData; +} + +static int XJSON_Realloc(xjson_writer_t *pWriter, size_t nSize) +{ + if (nSize < pWriter->nAvail) return XJSON_SUCCESS; + else if (!pWriter->nAlloc) return XJSON_FAILURE; + + size_t nNewSize = pWriter->nSize + nSize; + char* pDataOld = pWriter->pData; + + pWriter->pData = realloc(pWriter->pData, nNewSize); + if (pWriter->pData == NULL) + { + pWriter->pData = pDataOld; + return XJSON_FAILURE; + } + + pWriter->nAvail += nSize; + pWriter->nSize = nNewSize; + return XJSON_SUCCESS; +} + +static int XJSON_AppedSpaces(xjson_writer_t *pWriter) +{ + if (!pWriter->nTabSize) return XJSON_SUCCESS; + char *pSpaces = (char*)calloc(pWriter->nIdents + 1, sizeof(char)); + if (pSpaces == NULL) return XJSON_FAILURE; + + size_t nLenght = 0; + while (nLenght < pWriter->nIdents) + pSpaces[nLenght++] = ' '; + + pSpaces[nLenght] = '\0'; + if (!XJSON_Realloc(pWriter, nLenght)) + { + free(pSpaces); + return XJSON_FAILURE; + } + + char *pOffset = &pWriter->pData[pWriter->nLength]; + nLenght = xstrncpyf(pOffset, pWriter->nAvail, "%s", pSpaces); + free(pSpaces); + + pWriter->nLength += nLenght; + pWriter->nAvail -= nLenght; + return XJSON_SUCCESS; +} + +static int XJSON_WriteString(xjson_writer_t *pWriter, int nIdent, const char *pFmt, ...) +{ + if (nIdent) XJSON_ASSERT(XJSON_AppedSpaces(pWriter)); + + char *pBuffer = NULL; + size_t nBytes = 0; + + va_list args; + va_start(args, pFmt); +#ifdef _XUTILS_USE_GNU + nBytes += vasprintf(&pBuffer, pFmt, args); +#else + pBuffer = xstracpyargs(pFmt, args, &nBytes); +#endif + va_end(args); + + if (!nBytes || !XJSON_Realloc(pWriter, nBytes)) + { + free(pBuffer); + return XJSON_FAILURE; + } + + size_t nAvail = pWriter->nSize - pWriter->nLength; + size_t nWrited = xstrncpys(&pWriter->pData[pWriter->nLength], nAvail, pBuffer, nBytes); + + pWriter->nLength += nWrited; + pWriter->nAvail -= nWrited; + pWriter->pData[pWriter->nLength] = '\0'; + + free(pBuffer); + return XJSON_SUCCESS; +} + +static int XJSON_WriteName(xjson_obj_t *pObj, xjson_writer_t *pWriter) +{ + if (pObj->pName == NULL) return XJSON_SUCCESS; + return XJSON_WriteString(pWriter, 1, "\"%s\":%s", + pObj->pName, pWriter->nTabSize ? " " : ""); +} + +static int XJSON_WriteItem(xjson_obj_t *pObj, xjson_writer_t *pWriter) +{ + if (pObj->nType == XJSON_TYPE_INVALID || + pObj->nType == XJSON_TYPE_OBJECT || + pObj->nType == XJSON_TYPE_ARRAY) + return XJSON_FAILURE; + + XJSON_ASSERT(pObj->pData); + XJSON_ASSERT(XJSON_WriteName(pObj, pWriter)); + int nIdent = pObj->pName == NULL ? 1 : 0; + + return (pObj->nType == XJSON_TYPE_STRING) ? + XJSON_WriteString(pWriter, nIdent, "\"%s\"", (const char*)pObj->pData): + XJSON_WriteString(pWriter, nIdent, "%s", (const char*)pObj->pData); +} + +static int XJSON_MapIt(xmap_pair_t *pPair, void *pContext) +{ + xjson_iterator_t *pIt = (xjson_iterator_t*)pContext; + xjson_obj_t *pItem = (xjson_obj_t *)pPair->pData; + + return (!XJSON_WriteObject(pItem, pIt->pWriter) || + (++pIt->nCurrent < pIt->nUsed && + !XJSON_WriteString(pIt->pWriter, 0, ",")) || + (pIt->pWriter->nTabSize && + !XJSON_WriteString(pIt->pWriter, 0, "\n"))) ? + XMAP_STOP : XMAP_OK; +} + +static int XJSON_Ident(xjson_writer_t *pWriter, int nIncrease) +{ + if (pWriter->nTabSize) + { + if (nIncrease) pWriter->nIdents += pWriter->nTabSize; + else if (pWriter->nTabSize <= pWriter->nIdents) + pWriter->nIdents -= pWriter->nTabSize; + else return XJSON_FAILURE; + } + + return XJSON_SUCCESS; +} + +static int XJSON_WriteHashmap(xjson_obj_t *pObj, xjson_writer_t *pWriter) +{ + XJSON_ASSERT(XJSON_CheckObject(pObj, XJSON_TYPE_OBJECT)); + XJSON_ASSERT(XJSON_WriteName(pObj, pWriter)); + int nIdent = pObj->pName == NULL ? 1 : 0; + xmap_t *pMap = (xmap_t*)pObj->pData; + + XJSON_ASSERT(XJSON_WriteString(pWriter, nIdent, "{")); + nIdent = (pWriter->nTabSize && pMap->nUsed) ? 1 : 0; + + if (nIdent) + { + XJSON_ASSERT(XJSON_WriteString(pWriter, 0, "\n")); + XJSON_ASSERT(XJSON_Ident(pWriter, XJSON_IDENT_INC)); + } + + if (pMap->nUsed) + { + xjson_iterator_t *pIterator = (xjson_iterator_t*)malloc(sizeof(xjson_iterator_t)); + if (pIterator == NULL) return XJSON_FAILURE; + + pIterator->nCurrent = 0; + pIterator->pWriter = pWriter; + pIterator->nUsed = pMap->nUsed; + + if (XMap_Iterate(pMap, XJSON_MapIt, pIterator) != XMAP_OK) + { + free(pIterator); + return XJSON_FAILURE; + } + + free(pIterator); + } + + if (nIdent) XJSON_ASSERT(XJSON_Ident(pWriter, XJSON_IDENT_DEC)); + return XJSON_WriteString(pWriter, nIdent, "}"); +} + +static int XJSON_WriteArray(xjson_obj_t *pObj, xjson_writer_t *pWriter) +{ + XJSON_ASSERT(XJSON_CheckObject(pObj, XJSON_TYPE_ARRAY)); + XJSON_ASSERT(XJSON_WriteName(pObj, pWriter)); + int nIdent = pObj->pName == NULL ? 1 : 0; + + xarray_t* pArray = (xarray_t*)pObj->pData; + XJSON_ASSERT(XJSON_WriteString(pWriter, nIdent, "[")); + + size_t i, nUsed = XArray_Used(pArray); + nIdent = (pWriter->nTabSize && nUsed) ? 1 : 0; + + if (nIdent) + { + XJSON_ASSERT(XJSON_WriteString(pWriter, 0, "\n")); + XJSON_ASSERT(XJSON_Ident(pWriter, XJSON_IDENT_INC)); + } + + for (i = 0; i < nUsed; i++) + { + xjson_obj_t *pItem = XArray_GetData(pArray, i); + XJSON_ASSERT(XJSON_WriteObject(pItem, pWriter)); + if ((i + 1) < nUsed) XJSON_ASSERT(XJSON_WriteString(pWriter, 0, ",")); + if (pWriter->nTabSize) XJSON_ASSERT(XJSON_WriteString(pWriter, 0, "\n")); + } + + if (nIdent) XJSON_ASSERT(XJSON_Ident(pWriter, XJSON_IDENT_DEC)); + return XJSON_WriteString(pWriter, nIdent, "]"); +} + +int XJSON_WriteObject(xjson_obj_t *pObj, xjson_writer_t *pWriter) +{ + if (pObj == NULL || pWriter == NULL) return XJSON_FAILURE; + + switch (pObj->nType) + { + case XJSON_TYPE_ARRAY: + return XJSON_WriteArray(pObj, pWriter); + case XJSON_TYPE_OBJECT: + return XJSON_WriteHashmap(pObj, pWriter); + case XJSON_TYPE_BOOLEAN: + case XJSON_TYPE_NUMBER: + case XJSON_TYPE_STRING: + case XJSON_TYPE_FLOAT: + case XJSON_TYPE_NULL: + return XJSON_WriteItem(pObj, pWriter); + case XJSON_TYPE_INVALID: + default: break; + } + + return XJSON_FAILURE; +} + +int XJSON_InitWriter(xjson_writer_t *pWriter, char *pOutput, size_t nSize) +{ + pWriter->nAvail = nSize; + pWriter->pData = pOutput; + pWriter->nSize = nSize; + pWriter->nAlloc = 0; + + if (pWriter->pData == NULL && pWriter->nSize) + { + pWriter->pData = malloc(pWriter->nSize); + if (pWriter->pData == NULL) return 0; + pWriter->nAlloc = 1; + } + + if (pWriter->pData != NULL) + pWriter->pData[0] = '\0'; + + pWriter->nTabSize = 0; + pWriter->nIdents = 0; + pWriter->nLength = 0; + + return 1; +} + +void XJSON_DestroyWriter(xjson_writer_t *pWriter) +{ + if (pWriter && pWriter->nAlloc) + { + free(pWriter->pData); + pWriter->pData = NULL; + pWriter->nAlloc = 0; + } +} + +int XJSON_Write(xjson_t *pJson, char *pOutput, size_t nSize) +{ + if (pJson == NULL || + pOutput == NULL || + !nSize) return XJSON_FAILURE; + + xjson_writer_t writer; + XJSON_InitWriter(&writer, pOutput, nSize); + return XJSON_WriteObject(pJson->pRootObj, &writer);; +} diff --git a/src/xjson.h b/src/xjson.h new file mode 100644 index 0000000..07a14b2 --- /dev/null +++ b/src/xjson.h @@ -0,0 +1,145 @@ +/*! + * @file libxutils/src/xjson.h + * + * This source is part of "libxutils" project + * 2019-2021 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of the lexical analyzer + * and recursive descent parser with JSON grammar + */ + +#include +#include +#include + +#ifndef __XUTILS_JSON_H__ +#define __XUTILS_JSON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XJSON_SUCCESS 1 +#define XJSON_FAILURE 0 + +typedef enum { + XJSON_TOKEN_INVALID = (uint8_t)0, + XJSON_TOKEN_COMMA, + XJSON_TOKEN_COLON, + XJSON_TOKEN_QUOTE, + XJSON_TOKEN_LCURLY, + XJSON_TOKEN_RCURLY, + XJSON_TOKEN_LPAREN, + XJSON_TOKEN_RPAREN, + XJSON_TOKEN_LSQUARE, + XJSON_TOKEN_RSQUARE, + XJSON_TOKEN_INTEGER, + XJSON_TOKEN_FLOAT, + XJSON_TOKEN_BOOL, + XJSON_TOKEN_NULL, + XJSON_TOKEN_EOF +} xjson_token_type_t; + +typedef struct xjson_token_ { + xjson_token_type_t nType; + const char *pData; + size_t nLength; +} xjson_token_t; + +typedef enum { + XJSON_TYPE_INVALID = (uint8_t)0, + XJSON_TYPE_OBJECT, + XJSON_TYPE_ARRAY, + XJSON_TYPE_BOOLEAN, + XJSON_TYPE_STRING, + XJSON_TYPE_NUMBER, + XJSON_TYPE_FLOAT, + XJSON_TYPE_NULL, +} xjson_type_t; + +typedef struct xjson_obj_ { + xjson_type_t nType; + uint8_t nAllowUpdate; + uint8_t nAllocated; + void *pData; + char *pName; +} xjson_obj_t; + +typedef enum { + XJSON_ERR_NONE = (uint8_t)0, + XJSON_ERR_UNEXPECTED, + XJSON_ERR_INVALID, + XJSON_ERR_BOUNDS, + XJSON_ERR_EXITS, + XJSON_ERR_ALLOC +} xjson_error_t; + +xjson_error_t XJSON_AddObject(xjson_obj_t *pDst, xjson_obj_t *pSrc); +xjson_obj_t* XJSON_CreateObject(const char *pName, void *pValue, xjson_type_t nType); +xjson_obj_t* XJSON_NewObject(const char *pName, uint8_t nAllowUpdate); +xjson_obj_t* XJSON_NewArray(const char *pName, uint8_t nAllowUpdate); +xjson_obj_t* XJSON_NewU64(const char *pName, uint64_t nValue); +xjson_obj_t* XJSON_NewU32(const char *pName, uint32_t nValue); +xjson_obj_t* XJSON_NewInt(const char *pName, int nValue); +xjson_obj_t* XJSON_NewFloat(const char *pName, double fValue); +xjson_obj_t* XJSON_NewString(const char *pName, const char *pValue); +xjson_obj_t* XJSON_NewBool(const char *pName, int nValue); +xjson_obj_t* XJSON_NewNull(const char *pName); +void XJSON_FreeObject(xjson_obj_t *pObj); + +xjson_error_t XJSON_AddU64(xjson_obj_t *pObject, const char *pName, uint64_t nValue); +xjson_error_t XJSON_AddU32(xjson_obj_t *pObject, const char *pName, uint32_t nValue); +xjson_error_t XJSON_AddInt(xjson_obj_t *pObject, const char *pName, int nValue); +xjson_error_t XJSON_AddFloat(xjson_obj_t *pObject, const char *pName, double fValue); +xjson_error_t XJSON_AddString(xjson_obj_t *pObject, const char *pName, const char *pValue); +xjson_error_t XJSON_AddBool(xjson_obj_t *pObject, const char *pName, int nValue); +xjson_error_t XJSON_AddNull(xjson_obj_t *pObject, const char *pName); + +typedef struct xjson_ { + xjson_token_t lastToken; + xjson_error_t nError; + xjson_obj_t *pRootObj; + const char *pData; + size_t nDataSize; + size_t nOffset; +} xjson_t; + +size_t XJSON_GetErrorStr(xjson_t *pJson, char *pOutput, size_t nSize); + +int XJSON_Parse(xjson_t *pJson, const char *pData, size_t nSize); +void XJSON_Destroy(xjson_t *pJson); + +size_t XJSON_GetArrayLength(xjson_obj_t *pObj); +int XJSON_RemoveArrayItem(xjson_obj_t *pObj, size_t nIndex); +xjson_obj_t* XJSON_GetObject(xjson_obj_t *pObj, const char *pName); +xjson_obj_t* XJSON_GetArrayItem(xjson_obj_t *pObj, size_t nIndex); +xjson_obj_t *XJSON_GetOrCreateObject(xjson_obj_t *pObj, const char *pName, uint8_t nAllowUpdate); +xjson_obj_t *XJSON_GetOrCreateArray(xjson_obj_t *pObj, const char *pName, uint8_t nAllowUpdate); + +const char* XJSON_GetString(xjson_obj_t *pObj); +uint32_t XJSON_GetU32(xjson_obj_t *pObj); +uint64_t XJSON_GetU64(xjson_obj_t *pObj); +uint8_t XJSON_GetBool(xjson_obj_t *pObj); +double XJSON_GetFloat(xjson_obj_t *pObj); +int XJSON_GetInt(xjson_obj_t *pObj); + +typedef struct xjson_writer_ { + size_t nTabSize; + size_t nIdents; + size_t nLength; + size_t nAvail; + size_t nSize; + char *pData; + int nAlloc:1; +} xjson_writer_t; + +void XJSON_DestroyWriter(xjson_writer_t *pWriter); +int XJSON_InitWriter(xjson_writer_t *pWriter, char *pOutput, size_t nSize); +int XJSON_WriteObject(xjson_obj_t *pObj, xjson_writer_t *pWriter); +int XJSON_Write(xjson_t *pJson, char *pOutput, size_t nSize); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_JSON_H__ */ \ No newline at end of file diff --git a/src/xlog.c b/src/xlog.c new file mode 100644 index 0000000..71f5777 --- /dev/null +++ b/src/xlog.c @@ -0,0 +1,469 @@ +/* + * libxutils/src/xlog.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * Advanced logging library for C/C++ + */ + +#include "xstd.h" +#include "sync.h" +#include "xstr.h" +#include "xlog.h" +#include "xtime.h" + +typedef struct XLog { + xsync_mutex_t lock; + xlog_cfg_t config; +} xlog_t; + +typedef struct XLogCtx { + const char *pFormat; + xlog_flag_t eFlag; + uint8_t nFullColor; + uint8_t nNewLine; + xtime_t time; +} xlog_ctx_t; + +static xlog_t g_xlog; + +static const char *XLog_GetIdent(xlog_flag_t eFlag) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + if (!pCfg->nIdent) return XSTR_EMPTY; + + switch (eFlag) + { + case XLOG_NONE: + return XLOG_SPACE_IDENT; + case XLOG_NOTE: + case XLOG_INFO: + case XLOG_WARN: + return XSTR_SPACE; + case XLOG_DEBUG: + case XLOG_TRACE: + case XLOG_FATAL: + case XLOG_ERROR: + case XLOG_ALL: + default: break; + } + + return XSTR_EMPTY; +} + +static const char *XLog_GetTagStr(xlog_flag_t eFlag) +{ + switch (eFlag) + { + case XLOG_NOTE: return "note"; + case XLOG_INFO: return "info"; + case XLOG_WARN: return "warn"; + case XLOG_DEBUG: return "debug"; + case XLOG_TRACE: return "trace"; + case XLOG_ERROR: return "error"; + case XLOG_FATAL: return "fatal"; + case XLOG_NONE: + case XLOG_ALL: + default: break; + } + + return NULL; +} + +static const char *XLog_GetColor(xlog_flag_t eFlag) +{ + switch (eFlag) + { + case XLOG_NOTE: return XLOG_COLOR_NORMAL; + case XLOG_INFO: return XLOG_COLOR_GREEN; + case XLOG_WARN: return XLOG_COLOR_YELLOW; + case XLOG_DEBUG: return XLOG_COLOR_BLUE; + case XLOG_ERROR: return XLOG_COLOR_RED; + case XLOG_TRACE: return XLOG_COLOR_CYAN; + case XLOG_FATAL: return XLOG_COLOR_MAGENTA; + case XLOG_NONE: + case XLOG_ALL: + default: break; + } + + return XLOG_COLOR_NORMAL; +} + +static uint32_t XLog_GetThreadID(void) +{ +#ifdef __linux__ + return syscall(__NR_gettid); +#elif _WIN32 + return (uint32_t)GetCurrentThreadId(); +#else + return (uint32_t)pthread_self(); +#endif +} + +static void XLog_CreateTag(char *pOut, int nSize, xlog_flag_t eFlag, const char *pColor) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + pOut[0] = XSTR_NUL; + + const char *pIdent = XLog_GetIdent(eFlag); + const char *pTag = XLog_GetTagStr(eFlag); + + if (pTag == NULL) + { + xstrncpy(pOut, nSize, pIdent); + return; + } + + if (pCfg->eColorFormat != XLOG_COLORING_TAG) xstrncpyf(pOut, nSize, "<%s>%s", pTag, pIdent); + else xstrncpyf(pOut, nSize, "%s<%s>%s%s", pColor, pTag, XLOG_COLOR_RESET, pIdent); +} + +static void XLog_CreateTid(char *pOut, int nSize, uint8_t nTraceTid) +{ + if (!nTraceTid) pOut[0] = XSTR_NUL; + else xstrncpyf(pOut, nSize, "(%u) ", XLog_GetThreadID()); +} + +static void XLog_DisplayMessage(const xlog_ctx_t *pCtx, const char *pInfo, size_t nInfoLen, const char *pInput) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + int nCbVal = 1; + + const char *pSeparator = nInfoLen > 0 ? &pCfg->sSeparator[0] : XSTR_EMPTY; + const char *pReset = pCtx->nFullColor ? XLOG_COLOR_RESET : XSTR_EMPTY; + const char *pNewLine = pCtx->nNewLine ? XSTR_NEW_LINE : XSTR_EMPTY; + const char *pMessage = pInput != NULL ? pInput : XSTR_EMPTY; + + if (pCfg->logCallback != NULL) + { + size_t nLength = 0; + char *pLog = xstracpyn(&nLength, "%s%s%s%s%s", + pInfo, pSeparator, pMessage, pReset, pNewLine); + + if (pLog != NULL) + { + nCbVal = pCfg->logCallback(pLog, nLength, pCtx->eFlag, pCfg->pCbCtx); + free(pLog); + } + } + + if (pCfg->nToScreen && nCbVal > 0) + { + printf("%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine); + if (pCfg->nFlush) fflush(stdout); + } + + if (!pCfg->nToFile || nCbVal < 0) return; + const xtime_t *pTime = &pCtx->time; + + char sFilePath[XLOG_PATH_MAX + XLOG_NAME_MAX + XLOG_TIME_MAX]; + xstrncpyf(sFilePath, sizeof(sFilePath), "%s/%s-%04d-%02d-%02d.log", + pCfg->sFilePath, pCfg->sFileName, pTime->nYear, pTime->nMonth, pTime->nDay); + + FILE* pFile = NULL; +#ifdef _WIN32 + if (fopen_s(&pFile, sFilePath, "a")) return; +#else + pFile = fopen(sFilePath, "a"); +#endif + + if (pFile == NULL) return; + fprintf(pFile, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine); + fclose(pFile); +} + +static size_t XLog_CreateLogInfo(const xlog_ctx_t *pCtx, char* pOut, size_t nSize) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + const xtime_t *pTime = &pCtx->time; + char sDate[XLOG_TIME_MAX] = XSTR_INIT; + + const char *pSpace = pCtx->eFlag == XLOG_NONE ? + XSTR_EMPTY : XSTR_SPACE; + + if (pCfg->eTimeFormat == XLOG_TIME) + { + xstrncpyf(sDate, sizeof(sDate), + "%02d:%02d:%02d.%02d%s", + pTime->nHour,pTime->nMin, + pTime->nSec, pTime->nFraq, + pSpace); + } + else if (pCfg->eTimeFormat == XLOG_DATE) + { + xstrncpyf(sDate, sizeof(sDate), + "%04d.%02d.%02d-%02d:%02d:%02d.%02d%s", + pTime->nYear, pTime->nMonth, + pTime->nDay, pTime->nHour, + pTime->nMin, pTime->nSec, + pTime->nFraq, pSpace); + } + + char sTid[XLOG_TAG_MAX], sTag[XLOG_TAG_MAX]; + const char *pColorCode = XLog_GetColor(pCtx->eFlag); + const char *pColor = pCtx->nFullColor ? pColorCode : XSTR_EMPTY; + + XLog_CreateTid(sTid, sizeof(sTid), pCfg->nTraceTid); + XLog_CreateTag(sTag, sizeof(sTag), pCtx->eFlag, pColorCode); + return xstrncpyf(pOut, nSize, "%s%s%s%s", pColor, sTid, sDate, sTag); +} + +static void XLog_DisplayHeap(const xlog_ctx_t *pCtx, va_list args) +{ + char *pInput = xstracpyarg(pCtx->pFormat, args); + char sLogInfo[XLOG_INFO_MAX]; + size_t nLength = 0; + + nLength = XLog_CreateLogInfo(pCtx, sLogInfo, sizeof(sLogInfo)); + XLog_DisplayMessage(pCtx, sLogInfo, nLength, pInput); + if (pInput != NULL) free(pInput); +} + +static void XLog_DisplayStack(const xlog_ctx_t *pCtx, va_list args) +{ + char sMessage[XLOG_MESSAGE_MAX]; + char sLogInfo[XLOG_INFO_MAX]; + size_t nLength = 0; + + vsnprintf(sMessage, sizeof(sMessage), pCtx->pFormat, args); + nLength = XLog_CreateLogInfo(pCtx, sLogInfo, sizeof(sLogInfo)); + XLog_DisplayMessage(pCtx, sLogInfo, nLength, sMessage); +} + +void XLog_Display(xlog_flag_t eFlag, uint8_t nNewLine, const char *pFormat, ...) +{ + XSync_Lock(&g_xlog.lock); + xlog_cfg_t *pCfg = &g_xlog.config; + + if ((XLOG_FLAGS_CHECK(g_xlog.config.nFlags, eFlag)) && + (g_xlog.config.nToScreen || g_xlog.config.nToFile)) + { + xlog_ctx_t ctx; + XTime_Get(&ctx.time); + + ctx.eFlag = eFlag; + ctx.pFormat = pFormat; + ctx.nNewLine = nNewLine; + ctx.nFullColor = pCfg->eColorFormat == XLOG_COLORING_FULL ? 1 : 0; + + void(*XLog_DisplayArgs)(const xlog_ctx_t *pCtx, va_list args); + XLog_DisplayArgs = pCfg->nUseHeap ? XLog_DisplayHeap : XLog_DisplayStack; + + va_list args; + va_start(args, pFormat); + XLog_DisplayArgs(&ctx, args); + va_end(args); + } + + XSync_Unlock(&g_xlog.lock); +} + +size_t XLog_Version(char *pDest, size_t nSize, int nMin) +{ + size_t nLength = 0; + + /* Version short */ + if (nMin) nLength = xstrncpyf(pDest, nSize, "%d.%d.%d", + XLOG_VERSION_MAJOR, XLOG_VERSION_MINOR, XLOG_BUILD_NUM); + + /* Version long */ + else nLength = xstrncpyf(pDest, nSize, "%d.%d build %d (%s)", + XLOG_VERSION_MAJOR, XLOG_VERSION_MINOR, XLOG_BUILD_NUM, __DATE__); + + return nLength; +} + +void XLog_ConfigGet(struct XLogConfig *pCfg) +{ + XSync_Lock(&g_xlog.lock); + *pCfg = g_xlog.config; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_ConfigSet(struct XLogConfig *pCfg) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config = *pCfg; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_FlagEnable(xlog_flag_t eFlag) +{ + XSync_Lock(&g_xlog.lock); + + if (eFlag == XLOG_NONE || eFlag == XLOG_ALL) + g_xlog.config.nFlags = eFlag; + else if (!XLOG_FLAGS_CHECK(g_xlog.config.nFlags, eFlag)) + g_xlog.config.nFlags |= eFlag; + + XSync_Unlock(&g_xlog.lock); +} + +void XLog_FlagDisable(xlog_flag_t eFlag) +{ + XSync_Lock(&g_xlog.lock); + + if (XLOG_FLAGS_CHECK(g_xlog.config.nFlags, eFlag)) + g_xlog.config.nFlags &= ~eFlag; + + XSync_Unlock(&g_xlog.lock); +} + +void XLog_CallbackSet(xlog_cb_t callback, void *pContext) +{ + XSync_Lock(&g_xlog.lock); + xlog_cfg_t *pCfg = &g_xlog.config; + pCfg->pCbCtx = pContext; + pCfg->logCallback = callback; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_SeparatorSet(const char *pSeparator) +{ + XSync_Lock(&g_xlog.lock); + xlog_cfg_t *pCfg = &g_xlog.config; + + if (snprintf(pCfg->sSeparator, + sizeof(pCfg->sSeparator), + " %s ", pSeparator) <= 0) + { + pCfg->sSeparator[0] = ' '; + pCfg->sSeparator[1] = '\0'; + } + + XSync_Unlock(&g_xlog.lock); +} + +void XLog_ColorFormatSet(xlog_coloring_t eFmt) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.eColorFormat = eFmt; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_TimeFormatSet(xlog_timing_t eFmt) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.eTimeFormat = eFmt; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_IdentSet(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nIdent = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_FlushSet(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nFlush = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_FileLogSet(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nToFile = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_ScreenLogSet(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nToScreen = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_TraceTid(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nTraceTid = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_UseHeap(uint8_t nEnable) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nUseHeap = nEnable; + XSync_Unlock(&g_xlog.lock); +} + +void XLog_FlagsSet(uint16_t nFlags) +{ + XSync_Lock(&g_xlog.lock); + g_xlog.config.nFlags = nFlags; + XSync_Unlock(&g_xlog.lock); +} + +uint16_t XLog_FlagsGet(void) +{ + uint16_t nFlags = 0; + XSync_Lock(&g_xlog.lock); + nFlags = g_xlog.config.nFlags; + XSync_Unlock(&g_xlog.lock); + return nFlags; +} + +size_t XLog_PathSet(const char *pPath) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + size_t nLength = 0; + + XSync_Lock(&g_xlog.lock); + xstrncpy(pCfg->sFilePath, sizeof(pCfg->sFilePath), pPath); + XSync_Unlock(&g_xlog.lock); + + return nLength; +} + +size_t XLog_NameSet(const char *pName) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + size_t nLength = 0; + + XSync_Lock(&g_xlog.lock); + nLength = xstrncpy(pCfg->sFileName, sizeof(pCfg->sFileName), pName); + XSync_Unlock(&g_xlog.lock); + + return nLength; +} + +void XLog_Init(const char* pName, uint16_t nFlags, uint8_t nTdSafe) +{ + xlog_cfg_t *pCfg = &g_xlog.config; + pCfg->eColorFormat = XLOG_COLORING_TAG; + pCfg->eTimeFormat = XLOG_DISABLE; + pCfg->pCbCtx = NULL; + pCfg->logCallback = NULL; + + pCfg->sSeparator[0] = ' '; + pCfg->sSeparator[1] = '\0'; + pCfg->sFilePath[0] = '.'; + pCfg->sFilePath[1] = '\0'; + + pCfg->nTraceTid = 0; + pCfg->nToScreen = 1; + pCfg->nUseHeap = 0; + pCfg->nToFile = 0; + pCfg->nIdent = 0; + pCfg->nFlush = 0; + pCfg->nFlags = nFlags; + + const char *pFileName = (pName != NULL) ? pName : XLOG_NAME_DEFAULT; + xstrncpyf(pCfg->sFileName, sizeof(pCfg->sFileName), "%s", pFileName); + + /* Init mutex sync */ + if (nTdSafe) XSync_Init(&g_xlog.lock); + else g_xlog.lock.bEnabled = XFALSE; +} + +void XLog_Destroy(void) +{ + memset(&g_xlog.config, 0, sizeof(g_xlog.config)); + g_xlog.config.pCbCtx = NULL; + g_xlog.config.logCallback = NULL; + XSync_Destroy(&g_xlog.lock); +} diff --git a/src/xlog.h b/src/xlog.h new file mode 100644 index 0000000..23a1ac1 --- /dev/null +++ b/src/xlog.h @@ -0,0 +1,202 @@ +/* + * libxutils/src/xlog.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * Advanced logging library for C/C++ + */ + +#ifndef __XLOG_H__ +#define __XLOG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" +#include "sync.h" + +/* Definations for version info */ +#define XLOG_VERSION_MAJOR 1 +#define XLOG_VERSION_MINOR 8 +#define XLOG_BUILD_NUM 26 + +#define XLOG_NAME_DEFAULT "xlog" + +/* Supported colors and tags */ +#define XLOG_COLOR_NORMAL "\x1B[0m" +#define XLOG_COLOR_RED "\x1B[31m" +#define XLOG_COLOR_GREEN "\x1B[32m" +#define XLOG_COLOR_YELLOW "\x1B[33m" +#define XLOG_COLOR_BLUE "\x1B[34m" +#define XLOG_COLOR_MAGENTA "\x1B[35m" +#define XLOG_COLOR_CYAN "\x1B[36m" +#define XLOG_COLOR_WHITE "\x1B[37m" +#define XLOG_COLOR_RESET "\033[0m" +#define XLOG_SPACE_IDENT " " + +/* Trace source location helpers */ +#define XLOG_LOCATION_LVL1(LINE) #LINE +#define XLOG_LOCATION_LVL2(LINE) XLOG_LOCATION_LVL1(LINE) +#define XLOG_THROW_LOCATION "[" __FILE__ ":" XLOG_LOCATION_LVL2(__LINE__) "] " + +#define XLOG_FLAGS_CHECK(c, f) (((c) & (f)) == (f)) +#define XLOG_FLAGS_DEFAULT 203 + +/* SLog limits (To be safe while avoiding dynamic allocations) */ +#define XLOG_MESSAGE_MAX 8196 +#define XLOG_VERSION_MAX 128 +#define XLOG_PATH_MAX 2048 +#define XLOG_INFO_MAX 512 +#define XLOG_NAME_MAX 256 +#define XLOG_TIME_MAX 64 +#define XLOG_TAG_MAX 32 +#define XLOG_CLR_MAX 16 + +typedef enum +{ + XLOG_NONE = (1 << 0), + XLOG_NOTE = (1 << 1), + XLOG_INFO = (1 << 2), + XLOG_WARN = (1 << 3), + XLOG_DEBUG = (1 << 4), + XLOG_TRACE = (1 << 5), + XLOG_ERROR = (1 << 6), + XLOG_FATAL = (1 << 7), + XLOG_DEFAULT = 203, + XLOG_ALL = 255 +} xlog_flag_t; + +typedef int(*xlog_cb_t)(const char *pLog, size_t nLength, xlog_flag_t eFlag, void *pCtx); + +/* Output coloring control flags */ +typedef enum +{ + XLOG_COLORING_DISABLE = 0, + XLOG_COLORING_TAG, + XLOG_COLORING_FULL +} xlog_coloring_t; + +/* Output time and date control flags */ +typedef enum +{ + XLOG_DISABLE = 0, + XLOG_TIME, + XLOG_DATE +} xlog_timing_t; + +#define XLog(...) \ + XLog_Display(XLOG_NONE, 1, __VA_ARGS__) + +#define XLog_(...) \ + XLog_Display(XLOG_NONE, 0, __VA_ARGS__) + +#define XLog_Note(...) \ + XLog_Display(XLOG_NOTE, 1, __VA_ARGS__) + +#define XLog_Info(...) \ + XLog_Display(XLOG_INFO, 1, __VA_ARGS__) + +#define XLog_Warn(...) \ + XLog_Display(XLOG_WARN, 1, __VA_ARGS__) + +#define XLog_Debug(...) \ + XLog_Display(XLOG_DEBUG, 1, __VA_ARGS__) + +#define XLog_Error(...) \ + XLog_Display(XLOG_ERROR, 1, __VA_ARGS__) + +#define XLog_Trace(...) \ + XLog_Display(XLOG_TRACE, 1, XLOG_THROW_LOCATION __VA_ARGS__) + +#define XLog_Fatal(...) \ + XLog_Display(XLOG_FATAL, 1, XLOG_THROW_LOCATION __VA_ARGS__) + +/* Lower case short name functions */ +#define xlog_(...) XLog_(__VA_ARGS__) +#define xlog(...) XLog(__VA_ARGS__) +#define xlogn(...) XLog_Note(__VA_ARGS__) +#define xlogi(...) XLog_Info(__VA_ARGS__) +#define xlogw(...) XLog_Warn(__VA_ARGS__) +#define xlogd(...) XLog_Debug( __VA_ARGS__) +#define xloge(...) XLog_Error( __VA_ARGS__) +#define xlogt(...) XLog_Trace(__VA_ARGS__) +#define xlogf(...) XLog_Fatal(__VA_ARGS__) +#define xlogx(f, ...) XLog_Display(f, 0, __VA_ARGS__) +#define xlogfl(f, ...) XLog_Display(f, 1, __VA_ARGS__) + +#define xlog_init(name,flags,safe) XLog_Init(name, flags, safe) +#define xlog_defaults() xlog_init(NULL, XLOG_DEFAULT, 0) +#define xlog_get(cfg) XLog_ConfigGet(cfg) +#define xlog_set(cfg) XLog_ConfigSet(cfg) +#define xlog_destroy() XLog_Destroy() + +#define xlog_callback(cb,ctx) XLog_CallbackSet(cb,ctx) +#define xlog_separator(sep) XLog_SeparatorSet(sep) +#define xlog_coloring(fmt) XLog_ColorFormatSet(fmt) +#define xlog_tracetid(fl) XLog_TraceTid(fl) +#define xlog_useheap(fl) XLog_UseHeap(fl) +#define xlog_timing(fmt) XLog_TimeFormatSet(fmt) +#define xlog_screen(fl) XLog_ScreenLogSet(fl) +#define xlog_file(fl) XLog_FileLogSet(fl) +#define xlog_ident(fl) XLog_IdentSet(fl) +#define xlog_flush(fl) XLog_FlushSet(fl) +#define xlog_enable(fl) XLog_FlagEnable(fl) +#define xlog_disable(fl) XLog_FlagDisable(fl) +#define xlog_getfl(fl) XLog_FlagsGet(fl) +#define xlog_setfl(fl) XLog_FlagsSet(fl) +#define xlog_path(path) XLog_PathSet(path) +#define xlog_name(name) XLog_NameSet(name) + +typedef struct XLogConfig { + xlog_coloring_t eColorFormat; + xlog_timing_t eTimeFormat; + xlog_cb_t logCallback; + void* pCbCtx; + + uint8_t nTraceTid; + uint8_t nToScreen; + uint8_t nUseHeap; + uint8_t nToFile; + uint8_t nIdent; + uint8_t nFlush; + uint16_t nFlags; + + char sFileName[XLOG_NAME_MAX]; + char sFilePath[XLOG_PATH_MAX]; + char sSeparator[XLOG_NAME_MAX]; +} xlog_cfg_t; + +size_t XLog_Version(char *pDest, size_t nSize, int nMin); +void XLog_ConfigGet(struct XLogConfig *pCfg); +void XLog_ConfigSet(struct XLogConfig *pCfg); + +void XLog_FlagEnable(xlog_flag_t eFlag); +void XLog_FlagDisable(xlog_flag_t eFlag); + +void XLog_CallbackSet(xlog_cb_t callback, void *pContext); +void XLog_SeparatorSet(const char *pSeparator); +void XLog_ColorFormatSet(xlog_coloring_t eFmt); +void XLog_TimeFormatSet(xlog_timing_t eFmt); +void XLog_IdentSet(uint8_t nEnable); + +size_t XLog_PathSet(const char *pPath); +size_t XLog_NameSet(const char *pName); +void XLog_ScreenLogSet(uint8_t nEnable); +void XLog_FileLogSet(uint8_t nEnable); +void XLog_FlushSet(uint8_t nEnable); +void XLog_TraceTid(uint8_t nEnable); +void XLog_UseHeap(uint8_t nEnable); +void XLog_FlagsSet(uint16_t nFlags); +uint16_t XLog_FlagsGet(void); + +void XLog_Init(const char* pName, uint16_t nFlags, uint8_t nTdSafe); +void XLog_Display(xlog_flag_t nFlag, uint8_t nNewLine, const char *pFormat, ...); +void XLog_Destroy(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __XLOG_H__ */ \ No newline at end of file diff --git a/src/xmap.c b/src/xmap.c new file mode 100644 index 0000000..50367fb --- /dev/null +++ b/src/xmap.c @@ -0,0 +1,293 @@ +/*! + * @file libxutils/src/xmap.c + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of dynamically allocated hash map + */ + +#include "xstd.h" +#include "crypt.h" +#include "xmap.h" + +int XMap_Init(xmap_t *pMap, size_t nSize) +{ + pMap->nTableSize = nSize; + pMap->clearCb = NULL; + pMap->pPairs = NULL; + pMap->nAlloc = 0; + pMap->nUsed = 0; + if (!nSize) return XMAP_OK; + + pMap->pPairs = (xmap_pair_t*)calloc(nSize, sizeof(xmap_pair_t)); + return (pMap->pPairs == NULL) ? XMAP_OMEM : XMAP_OK; +} + +xmap_t *XMap_New(size_t nSize) +{ + xmap_t *pMap = (xmap_t*)malloc(sizeof(xmap_t)); + if(pMap == NULL) return NULL; + + if (XMap_Init(pMap, nSize) < 0) + { + free(pMap); + return NULL; + } + + pMap->nAlloc = 1; + return pMap; +} + +void XMap_Free(xmap_t *pMap) +{ + if (pMap != NULL) + { + if (pMap->pPairs != NULL) + { + free(pMap->pPairs); + pMap->pPairs = NULL; + } + + if (pMap->nAlloc) free(pMap); + else + { + pMap->clearCb = NULL; + pMap->nUsed = 0; + } + } +} + +static int XMap_ClearIt(xmap_pair_t *pPair, void *pCtx) +{ + xmap_t *pMap = (xmap_t*)pCtx; + if (pMap->clearCb != NULL) + pMap->clearCb(pPair); + + pPair->nUsed = 0; + pPair->pData = NULL; + pPair->pKey = NULL; + return XMAP_OK; +} + +int XMap_Iterate(xmap_t *pMap, xmap_iterator_t itfunc, void *pCtx) +{ + if (!XMap_UsedSize(pMap)) return XMAP_EMPTY; + size_t i; + + for (i = 0; i < pMap->nTableSize; i++) + { + if (pMap->pPairs[i].nUsed) + { + int nStatus = itfunc(&pMap->pPairs[i], pCtx); + if (nStatus != XMAP_OK) return nStatus; + } + } + + return XMAP_OK; +} + +void XMap_Destroy(xmap_t *pMap) +{ + if (pMap == NULL || pMap->pPairs == NULL) return; + XMap_Iterate(pMap, XMap_ClearIt, pMap); + XMap_Free(pMap); +} + +int XMap_Hash(xmap_t *pMap, const char *pStr) +{ + if (!pMap->nTableSize) return XMAP_EINIT; + uint32_t nHash = XCrypt_CRC32((unsigned char*)(pStr), strlen(pStr)); + + /* Robert Jenkins' 32 bit Mix Function */ + nHash += (nHash << 12); + nHash ^= (nHash >> 22); + nHash += (nHash << 4); + nHash ^= (nHash >> 9); + nHash += (nHash << 10); + nHash ^= (nHash >> 2); + nHash += (nHash << 7); + nHash ^= (nHash >> 12); + + /* Knuth's Multiplicative Method */ + nHash = (nHash >> 3) * 2654435761; + return nHash % pMap->nTableSize; +} + +int XMap_GetHash(xmap_t *pMap, const char* pKey) +{ + if (pMap->nUsed >= pMap->nTableSize) return XMAP_FULL; + int i, nIndex = XMap_Hash(pMap, pKey); + + for (i = 0; i < XMAP_CHAIN_LENGTH; i++) + { + if (!pMap->pPairs[nIndex].nUsed) return nIndex; + else if (!strcmp(pMap->pPairs[nIndex].pKey, pKey)) return nIndex; + nIndex = (nIndex + 1) % (int)pMap->nTableSize; + } + + return XMAP_FULL; +} + +int XMap_Realloc(xmap_t *pMap) +{ + size_t nNewSize = pMap->nTableSize ? pMap->nTableSize * 2 : XMAP_INITIAL_SIZE; + xmap_pair_t *pPairs = (xmap_pair_t*)calloc(nNewSize, sizeof(xmap_pair_t)); + if (pPairs == NULL) return XMAP_OMEM; + int nStatus = XMAP_OK; + + xmap_pair_t* pOldPairs = pMap->pPairs; + size_t nOldSize = pMap->nTableSize; + size_t i, nUsed = pMap->nUsed; + + pMap->nUsed = 0; + pMap->pPairs = pPairs; + pMap->nTableSize = nNewSize; + if (pOldPairs == NULL) return nStatus; + + for(i = 0; i < nOldSize; i++) + { + if (!pOldPairs[i].nUsed) continue; + nStatus = XMap_Put(pMap, pOldPairs[i].pKey, pOldPairs[i].pData); + + if (nStatus != XMAP_OK) + { + pMap->nTableSize = nOldSize; + pMap->pPairs = pOldPairs; + pMap->nUsed = nUsed; + + free(pPairs); + return nStatus; + } + } + + free(pOldPairs); + return nStatus; +} + +int XMap_Put(xmap_t *pMap, char* pKey, void *pValue) +{ + if (pMap == NULL || pKey == NULL) return XMAP_OINV; + int nHash = XMap_GetHash(pMap, pKey); + + while (nHash == XMAP_FULL) + { + int nStatus = XMap_Realloc(pMap); + if (nStatus < 0) return nStatus; + nHash = XMap_GetHash(pMap, pKey); + } + + /* Set the data */ + pMap->pPairs[nHash].pData = pValue; + pMap->pPairs[nHash].pKey = pKey; + pMap->pPairs[nHash].nUsed = 1; + pMap->nUsed++; + return XMAP_OK; +} + +int XMap_PutPair(xmap_t *pMap, xmap_pair_t *pPair) +{ + if (pMap == NULL || pPair == NULL) return XMAP_OINV; + return XMap_Put(pMap, pPair->pKey, pPair->pData); +} + +int XMap_Update(xmap_t *pMap, int nHash, char *pKey, void *pValue) +{ + if ((size_t)nHash >= pMap->nTableSize) return XMAP_MISSING; + pMap->pPairs[nHash].pData = pValue; + pMap->pPairs[nHash].pKey = pKey; + pMap->pPairs[nHash].nUsed = 1; + return XMAP_OK; +} + +xmap_pair_t *XMap_GetPair(xmap_t *pMap, const char* pKey) +{ + if (pMap == NULL || pKey == NULL) return NULL; + + int nIndex = -1; + int i = 0; + + nIndex = XMap_Hash(pMap, pKey); + if (nIndex == XMAP_EINIT) return NULL; + + for (i = 0; i < XMAP_CHAIN_LENGTH; i++) + { + if (pMap->pPairs[nIndex].nUsed) + { + if (!strcmp(pMap->pPairs[nIndex].pKey, pKey)) + return &pMap->pPairs[nIndex]; + } + + nIndex = (nIndex + 1) % (int)pMap->nTableSize; + } + + return NULL; +} + +void* XMap_GetIndex(xmap_t *pMap, const char* pKey, int *pIndex) +{ + if (pMap == NULL || pKey == NULL) return NULL; + + *pIndex = -1; + int i = 0; + + *pIndex = XMap_Hash(pMap, pKey); + if (*pIndex == XMAP_EINIT) return NULL; + + for (i = 0; i < XMAP_CHAIN_LENGTH; i++) + { + if (pMap->pPairs[*pIndex].nUsed) + { + if (!strcmp(pMap->pPairs[*pIndex].pKey, pKey)) + return pMap->pPairs[*pIndex].pData; + } + + *pIndex = (*pIndex + 1) % (int)pMap->nTableSize; + } + + return NULL; +} + +void* XMap_Get(xmap_t *pMap, const char* pKey) +{ + int nDummy = 0; + return XMap_GetIndex(pMap, pKey, &nDummy); +} + +int XMap_Remove(xmap_t *pMap, const char* pKey) +{ + if (pMap == NULL || pKey == NULL) return XMAP_OINV; + int i, nIndex = XMap_Hash(pMap, pKey); + if (nIndex == XMAP_EINIT) return nIndex; + + for (i = 0; i < XMAP_CHAIN_LENGTH; i++) + { + if (pMap->pPairs[nIndex].nUsed) + { + if (!strcmp(pMap->pPairs[nIndex].pKey, pKey)) + { + xmap_pair_t *pPair = &pMap->pPairs[nIndex]; + if (pMap->nUsed) pMap->nUsed--; + + if (pMap->clearCb != NULL) + pMap->clearCb(pPair); + + pPair->nUsed = 0; + pPair->pData = NULL; + pPair->pKey = NULL; + + return XMAP_OK; + } + } + + nIndex = (nIndex + 1) % (int)pMap->nTableSize; + } + + return XMAP_MISSING; +} + +int XMap_UsedSize(xmap_t *pMap) +{ + if(pMap == NULL) return XMAP_OINV; + return (int)pMap->nUsed; +} \ No newline at end of file diff --git a/src/xmap.h b/src/xmap.h new file mode 100644 index 0000000..7aa4725 --- /dev/null +++ b/src/xmap.h @@ -0,0 +1,74 @@ +/*! + * @file libxutils/src/xmap.h + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of dynamically allocated hash map + */ + +#ifndef __XUTILS_NTP_H__ +#define __XUTILS_NTP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xstd.h" + +#define XMAP_INITIAL_SIZE 16 +#define XMAP_CHAIN_LENGTH 32 + +#define XMAP_EINIT -6 /* Map is not initialized */ +#define XMAP_MISSING -5 /* No such element */ +#define XMAP_OINV -4 /* Invalid parameter */ +#define XMAP_FULL -3 /* Hashmap is full */ +#define XMAP_OMEM -2 /* Out of Memory */ +#define XMAP_STOP -1 /* Stop iteration */ +#define XMAP_EMPTY 0 /* Map is empty */ +#define XMAP_OK 1 /* Success */ + +typedef struct XMapPair { + char *pKey; + void *pData; + int nUsed; +} xmap_pair_t; + +typedef int(*xmap_iterator_t)(xmap_pair_t*, void*); +typedef void(*xmap_clear_cb_t)(xmap_pair_t*); + +typedef struct XMap { + xmap_clear_cb_t clearCb; + xmap_pair_t *pPairs; + size_t nTableSize; + size_t nUsed; + int nAlloc; +} xmap_t; + +int XMap_Init(xmap_t *pMap, size_t nSize); +int XMap_Realloc(xmap_t *pMap); +void XMap_Destroy(xmap_t *pMap); + +xmap_t *XMap_New(size_t nSize); +void XMap_Free(xmap_t *pMap); + +xmap_pair_t *XMap_GetPair(xmap_t *pMap, const char* pKey); +void* XMap_GetIndex(xmap_t *pMap, const char* pKey, int *pIndex); +void* XMap_Get(xmap_t *pMap, const char* pKey); +int XMap_Put(xmap_t *pMap, char* pKey, void *pValue); +int XMap_PutPair(xmap_t *pMap, xmap_pair_t *pPair); +int XMap_Remove(xmap_t *pMap, const char* pKey); +int XMap_Update(xmap_t *pMap, int nHash, char *pKey, void *pValue); + +int XMap_GetHash(xmap_t *pMap, const char* pKey); +int XMap_Hash(xmap_t *pMap, const char *pStr); + +int XMap_Iterate(xmap_t *pMap, xmap_iterator_t itfunc, void *pCtx); +int XMap_UsedSize(xmap_t *pMap); + +#ifdef __cplusplus +} +#endif + + +#endif /* __XUTILS_NTP_H__ */ \ No newline at end of file diff --git a/src/xstd.h b/src/xstd.h new file mode 100644 index 0000000..f122ac2 --- /dev/null +++ b/src/xstd.h @@ -0,0 +1,140 @@ +/*! + * @file libxutils/src/xmap.c + * + * This source is part of "libxutils" project + * 2019-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Standart includes. + */ + +#ifndef __XUTILS_STDINC_H__ +#define __XUTILS_STDINC_H__ + +/* C includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef _WIN32 +/* Linux includes */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif +#else +#include +#include +#include +#include +#include +#endif + +#ifdef _XUTILS_DEBUG +#include "xlog.h" +#endif + +typedef int XSTATUS; + +#ifndef XSTD_MIN +#define XSTD_MIN(a,b)(ab?a:b) +#endif + +#ifndef XSTD_FIRSTOF +#define XSTD_FIRSTOF(a,b)(a?a:b) +#endif + +#ifndef XMSG_MIN +#define XMSG_MIN 2048 +#endif + +#ifndef XMSG_MID +#define XMSG_MID 4098 +#endif + +#ifndef XMSG_MAX +#define XMSG_MAX 8196 +#endif + +#ifndef XPATH_MAX +#define XPATH_MAX 2048 +#endif + +#ifdef LINE_MAX +#define XLINE_MAX LINE_MAX +#else +#define XLINE_MAX 2048 +#endif + +#ifndef XADDR_MAX +#define XADDR_MAX 64 +#endif + +#ifndef XNAME_MAX +#define XNAME_MAX 256 +#endif + +#ifndef XADDR_MAX +#define XADDR_MAX 64 +#endif + +#ifndef XPERM_MAX +#define XPERM_MAX 16 +#endif + +#ifndef XPERM_LEN +#define XPERM_LEN 9 +#endif + +#ifndef XSTDNON +#define XSTDNON 0 +#endif + +#ifndef XSTDERR +#define XSTDERR -1 +#endif + +#ifndef XSTDINV +#define XSTDINV -2 +#endif + +#ifndef XSTDOK +#define XSTDOK 1 +#endif + +#ifndef XSTDUSR +#define XSTDUSR 2 +#endif + +#endif /* __XUTILS_STDINC_H__ */ \ No newline at end of file diff --git a/src/xstr.c b/src/xstr.c new file mode 100644 index 0000000..486aed3 --- /dev/null +++ b/src/xstr.c @@ -0,0 +1,1395 @@ +/*! + * @file libxutils/src/xstr.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes string operations. + */ + +#ifdef _XUTILS_USE_GNU +#define _GNU_SOURCE +#endif + +#include "xstd.h" +#include "xstr.h" +#include "sync.h" +#include "xtype.h" + +///////////////////////////////////////////////////////////////////////// +// C string functions +///////////////////////////////////////////////////////////////////////// + +char *xstrtok(char* pString, const char* pDelimiter, char **pContext) +{ +#ifdef _WIN32 + return strtok_s(pString, pDelimiter, pContext); +#else + return strtok_r(pString, pDelimiter, pContext); +#endif +} + +size_t xstrarglen(const char *pFmt, va_list args) +{ + va_list locArgs; +#ifdef va_copy + va_copy(locArgs, args); +#else + memcpy(&locArgs, &args, sizeof(va_list)); +#endif + + int nLength = vsnprintf(0, 0, pFmt, locArgs); + +#ifdef _XUTILS_USE_EXT + if (nLength > 0) + { + va_end(locArgs); + return nLength; + } + + int nFmtLen = nLength = strlen(pFmt); + int nOffset = 0; + + while (nOffset < nFmtLen) + { + int nPosit = xstrsrc(&pFmt[nOffset], "%"); + if (nPosit < 0) break; + + nOffset += nPosit + 1; + if (nOffset >= nFmtLen) break; + + char sBuff[XSTR_INT_ARG_MAX]; + uint8_t nBuffPosit = 0; + + while (isdigit(pFmt[nOffset]) && + nOffset < nFmtLen && + nBuffPosit < sizeof(sBuff) - 1) + sBuff[nBuffPosit++] = pFmt[nOffset++]; + + sBuff[nBuffPosit] = XSTR_NUL; + if (nBuffPosit) nLength += atoi(sBuff); + + if (pFmt[nOffset] == '%') + if (++nOffset >= nFmtLen) break; + + switch(pFmt[nOffset]) + { + case '%': + nLength++; + break; + case 's': + const char* pArg = va_arg(locArgs,const char*); + if (pArg != NULL) nLength += strlen(pArg); + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + nLength += XSTR_DOUBLE_ARG_MAX; + va_arg(locArgs,double); + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + nLength += XSTR_INT_ARG_MAX; + va_arg(locArgs,int); + break; + default: + nLength += XSTR_INT_ARG_MAX; + va_arg(locArgs,void*); + break; + } + } +#endif + + va_end(locArgs); + return nLength; +} + +char* xstralloc(size_t nSize) +{ + if (!nSize) return NULL; + char *pStr = (char*)malloc(nSize); + if (pStr != NULL) pStr[0] = XSTR_NUL; + return pStr; +} + +size_t xstrrand(char *pDst, size_t nSize, size_t nLength, xbool_t bUpper, xbool_t bNumbers) +{ + if (pDst == NULL || !nSize || !nLength) return XSTDNON; + const char *pChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *pNums = "0123456789"; + + size_t i, nCharCount = XSTD_MIN(nLength, nSize - 1); + int nLimit = bUpper ? XSTR_LETTERS : XSTR_LETTERS / 2; + + for (i = 0; i < nCharCount; i++) + { + if (bNumbers && rand() % 2) + { + int nKey = rand() % 10; + pDst[i] = pNums[nKey]; + } + else + { + int nKey = rand() % nLimit; + pDst[i] = pChars[nKey]; + } + } + + pDst[nCharCount] = XSTR_NUL; + return nCharCount; +} + +size_t xstrfill(char *pDst, size_t nSize, size_t nLength, char cFill) +{ + if (pDst == NULL || !nSize) return XSTDNON; + + size_t i, nFillLength = XSTD_MIN(nLength, nSize - 1); + for (i = 0; i < nFillLength; i++) pDst[i] = cFill; + + pDst[nFillLength] = XSTR_NUL; + return nFillLength; +} + +int xstrncpyarg(char *pDest, size_t nSize, const char *pFmt, va_list args) +{ + if (pDest == NULL || !nSize) return XSTDERR; + size_t nLength = 0; + + int nBytes = vsnprintf(pDest, nSize, pFmt, args); + if (nBytes > 0) nLength = XSTD_MIN((size_t)nBytes, nSize - 1); + + pDest[nLength] = XSTR_NUL; + return (int)nLength; +} + +char* xstracpyargs(const char *pFmt, va_list args, size_t *nSize) +{ + size_t nLength = xstrarglen(pFmt, args); + if (!nLength) return NULL; + + char *pDest = (char*)malloc(++nLength); + if (pDest == NULL) return NULL; + + *nSize = xstrncpyarg(pDest, nLength, pFmt, args); + if (*nSize <= 0 && pDest) + { + free(pDest); + return NULL; + } + + return pDest; +} + +char* xstracpyarg(const char *pFmt, va_list args) +{ + size_t nDummy = 0; + return xstracpyargs(pFmt, args, &nDummy); +} + +char* xstracpy(const char *pFmt, ...) +{ + va_list args; + va_start(args, pFmt); + char *pDest = xstracpyarg(pFmt, args); + va_end(args); + return pDest; +} + +char* xstracpyn(size_t *nSize, const char *pFmt, ...) +{ + *nSize = 0; + va_list args; + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, nSize); + va_end(args); + return pDest; +} + +char* xstrxcpy(const char *pFmt, ...) +{ +#ifdef _XUTILS_USE_GNU + va_list args; + char *pDest = NULL; + int nLength = 0; + + va_start(args, pFmt); + nLength = vasprintf(&pDest, pFmt, args); + va_end(args); + + if (nLength <= 0 && pDest) + { + free(pDest); + return NULL; + } + + pDest[nLength] = XSTR_NUL; + return pDest; +#endif + (void)pFmt; + return NULL; +} + +size_t xstrxcpyf(char **pDst, const char *pFmt, ...) +{ +#ifdef _XUTILS_USE_GNU + va_list args; + int nBytes = 0; + + va_start(args, pFmt); + nBytes = vasprintf(pDst, pFmt, args); + va_end(args); + + if (nBytes <= 0 && pDst) + { + free(pDst); + pDst = NULL; + return 0; + } + + pDst[nBytes] = XSTR_NUL; + return nBytes; +#endif + (void)pDst; + (void)pFmt; + return 0; +} + +size_t xstrncpy(char *pDst, size_t nSize, const char* pSrc) +{ + if (pDst == NULL || pSrc == NULL || !nSize) return 0; + size_t nCopySize = strnlen(pSrc, nSize - 1); + if (!nCopySize) return 0; + + memcpy(pDst, pSrc, nCopySize); + pDst[nCopySize] = XSTR_NUL; + return nCopySize; +} + +size_t xstrncpys(char *pDst, size_t nDstSize, const char *pSrc, size_t nSrcLen) +{ + if (pDst == NULL || pSrc == NULL || !nDstSize) return 0; + size_t nCopySize = XSTD_MIN(nSrcLen, nDstSize - 1); + if (!nCopySize) return 0; + + memcpy(pDst, pSrc, nCopySize); + pDst[nCopySize] = XSTR_NUL; + return nCopySize; +} + +size_t xstrncpyf(char *pDst, size_t nSize, const char *pFmt, ...) +{ + int nBytes = 0; + va_list args; + va_start(args, pFmt); + nBytes = xstrncpyarg(pDst, nSize, pFmt, args); + va_end(args); + return nBytes; +} + +size_t xstrncpyfl(char *pDst, size_t nSize, size_t nFLen, char cFChar, const char* pFmt, ...) +{ + size_t nBytes = 0; + va_list args; + + va_start(args, pFmt); + nBytes = xstrncpyarg(pDst, nSize, pFmt, args); + va_end(args); + + nSize -= nBytes; + nFLen -= nBytes; + pDst += nBytes; + nBytes += xstrfill(pDst, nSize, nFLen, cFChar); + + return nBytes; +} + +size_t xstrnlcpyf(char *pDst, size_t nSize, size_t nFLen, char cFChar, const char* pFmt, ...) +{ + if (pDst == NULL || !nSize) return 0; + if (nFLen > nSize) nFLen = nSize - 1; + + size_t nBytes = 0; + va_list args; + + va_start(args, pFmt); + nBytes = xstrarglen(pFmt, args); + + if (!nBytes) + { + va_end(args); + return nBytes; + } + else if (nBytes >= nFLen) + { + nBytes = xstrncpyarg(pDst, nBytes, pFmt, args); + va_end(args); + return nBytes; + } + + pDst[0] = XSTR_NUL; + nFLen -= nBytes; + nBytes = 0; + + nBytes += xstrfill(pDst, nSize, nFLen, cFChar); + nBytes = xstrncpyarg(&pDst[nBytes], nSize - nBytes, pFmt, args); + + va_end(args); + return nBytes + nFLen; +} + +size_t xstrisextra(const char *pOffset) +{ + if (!strncmp(pOffset, XSTR_CLR_LIGHT_MAGENTA, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_YELLOW, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_WHITE, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_GREEN, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_CYAN, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_BLUE, 7) || + !strncmp(pOffset, XSTR_CLR_LIGHT_RED, 7)) return 7; + + if (!strncmp(pOffset, XSTR_CLR_MAGENTA, 5) || + !strncmp(pOffset, XSTR_CLR_YELLOW, 5) || + !strncmp(pOffset, XSTR_CLR_WHITE, 5) || + !strncmp(pOffset, XSTR_CLR_GREEN, 5) || + !strncmp(pOffset, XSTR_CLR_CYAN, 5) || + !strncmp(pOffset, XSTR_CLR_BLUE, 5) || + !strncmp(pOffset, XSTR_CLR_RED, 5)) return 5; + + if (!strncmp(pOffset, XSTR_BACK_MAGENTA, 5) || + !strncmp(pOffset, XSTR_BACK_YELLOW, 5) || + !strncmp(pOffset, XSTR_BACK_WHITE, 5) || + !strncmp(pOffset, XSTR_BACK_GREEN, 5) || + !strncmp(pOffset, XSTR_BACK_BLACK, 5) || + !strncmp(pOffset, XSTR_BACK_CYAN, 5) || + !strncmp(pOffset, XSTR_BACK_BLUE, 5) || + !strncmp(pOffset, XSTR_BACK_RED, 5)) return 5; + + if (!strncmp(pOffset, XSTR_FMT_HIGHLITE, 4) || + !strncmp(pOffset, XSTR_FMT_ITALIC, 4) || + !strncmp(pOffset, XSTR_FMT_ULINE, 4) || + !strncmp(pOffset, XSTR_FMT_FLICK, 4) || + !strncmp(pOffset, XSTR_FMT_BLINK, 4) || + !strncmp(pOffset, XSTR_FMT_CROSS, 4) || + !strncmp(pOffset, XSTR_FMT_RESET, 4) || + !strncmp(pOffset, XSTR_FMT_BOLD, 4) || + !strncmp(pOffset, XSTR_FMT_HIDE, 4) || + !strncmp(pOffset, XSTR_FMT_DIM, 4)) return 4; + + return 0; +} + +size_t xstrextra(const char *pStr, size_t nLength, size_t nMaxChars, size_t *pChars, size_t *pPosit) +{ + if (pStr == NULL || !nLength) return XSTDNON; + size_t nPosit, nChars = 0, nExtra = 0; + + for (nPosit = 0; nPosit < nLength; nPosit++) + { + if (pPosit != NULL) *pPosit = nPosit; + if (nMaxChars && nChars >= nMaxChars) break; + const char *pOffset = &pStr[nPosit]; + + if (*pOffset == '\x1B') + { + size_t nFound = xstrisextra(pOffset); + if (nFound) + { + nPosit += nFound - 1; + nExtra += nFound; + continue; + } + } + + nChars++; + } + + if (pChars != NULL) *pChars = nChars; + return nExtra; +} + +size_t xstrncat(char *pDst, size_t nSize, const char *pFmt, ...) +{ + if (pDst == NULL || !nSize) return 0; + size_t nUsed = strnlen(pDst, nSize - 1); + if (nSize <= nUsed) return 0; + + size_t nAvail = nSize - nUsed; + char *pBuffer = &pDst[nUsed]; + int nLength = 0; + + va_list args; + va_start(args, pFmt); + nLength = xstrncpyarg(pBuffer, nAvail, pFmt, args); + va_end(args); + + return nLength; +} + +size_t xstrncatf(char *pDst, size_t nAvail, const char *pFmt, ...) +{ + if (pDst == NULL || !nAvail) return 0; + size_t nUsed = strlen(pDst); + char *pBuffer = &pDst[nUsed]; + + va_list args; + va_start(args, pFmt); + int nLength = xstrncpyarg(pBuffer, nAvail, pFmt, args); + va_end(args); + + return nAvail - nLength; +} + +size_t xstrncatsf(char *pDst, size_t nSize, size_t nAvail, const char *pFmt, ...) +{ + if (pDst == NULL || !nSize || !nAvail) return 0; + size_t nUsed = nSize - nAvail; + char *pBuffer = &pDst[nUsed]; + + va_list args; + va_start(args, pFmt); + int nLength = xstrncpyarg(pBuffer, nAvail, pFmt, args); + va_end(args); + + return nAvail - nLength; +} + +size_t xstrnclr(char *pDst, size_t nSize, const char* pClr, const char* pStr, ...) +{ + if (pDst == NULL || !nSize) return 0; + char sBuffer[XSTR_STACK]; + char *pBuffer = &sBuffer[0]; + uint8_t nAlloc = 0; + + if (nSize > XSTR_STACK) + { + pBuffer = (char*)malloc(nSize); + if (pBuffer == NULL) return 0; + nAlloc = 1; + } + + va_list args; + va_start(args, pStr); + xstrncpyarg(pBuffer, nSize, pStr, args); + va_end(args); + + size_t nLength = xstrncpyf(pDst, nSize, "%s%s%s", + pClr, pBuffer, XSTR_FMT_RESET); + + if (nAlloc) free(pBuffer); + return nLength; +} + +size_t xstrcase(char *pSrc, xstr_case_t eCase) +{ + size_t i, nCopyLength = strlen(pSrc); + if (!nCopyLength) return 0; + + for (i = 0; i < nCopyLength; i++) + { + switch (eCase) + { + case XSTR_LOWER: + pSrc[i] = (char)tolower(pSrc[i]); + break; + case XSTR_UPPER: + pSrc[i] = (char)toupper(pSrc[i]); + break; + default: + return 0; + } + } + + pSrc[nCopyLength] = 0; + return nCopyLength; +} + +size_t xstrncases(char* pDst, size_t nSize, xstr_case_t eCase, const char *pSrc, size_t nSrcLen) +{ + if (pDst == NULL || pSrc == NULL || !nSize) return 0; + size_t i, nCopyLength = XSTD_MIN(nSrcLen, nSize - 1); + + for (i = 0; i < nCopyLength; i++) + { + switch (eCase) + { + case XSTR_LOWER: + pDst[i] = (char)tolower(pSrc[i]); + break; + case XSTR_UPPER: + pDst[i] = (char)toupper(pSrc[i]); + break; + default: + pDst[i] = pSrc[i]; + break; + } + } + + pDst[nCopyLength] = 0; + return nCopyLength; +} + +size_t xstrncase(char* pDst, size_t nSize, xstr_case_t eCase, const char *pSrc) +{ + size_t nCopyLength = strnlen(pSrc, nSize - 1); + return xstrncases(pDst, nSize, eCase, pSrc, nCopyLength); +} + +char* xstracase(const char *pSrc, xstr_case_t eCase) +{ + size_t nCopyLength = strlen(pSrc); + if (!nCopyLength) return NULL; + + size_t nDstSize = nCopyLength + 1; + char *pDst = (char*)malloc(nDstSize); + if (pDst == NULL) return NULL; + + xstrncases(pDst, nDstSize, eCase, pSrc, nCopyLength); + return pDst; +} + +char* xstracasen(const char *pSrc, xstr_case_t eCase, size_t nLength) +{ + size_t nCopyLength = strnlen(pSrc, nLength); + if (!nCopyLength) return NULL; + + size_t nDstSize = nCopyLength + 1; + char *pDst = (char*)malloc(nDstSize); + if (pDst == NULL) return NULL; + + xstrncases(pDst, nDstSize, eCase, pSrc, nCopyLength); + return pDst; +} + +int xstrsrc(const char *pStr, const char *pSrc) +{ + if (!xstrused(pStr) || !xstrused(pSrc)) return XSTDERR; + const char *pEndPosition = strstr(pStr, pSrc); + if (pEndPosition == NULL) return XSTDERR; + return (int)(pEndPosition - pStr); +} + +int xstrnsrc(const char *pStr, size_t nLen, const char *pSrc, size_t nPos) +{ + if (pStr == NULL || pSrc == NULL || + nPos >= nLen) return XSTDERR; + return xstrsrc(&pStr[nPos], pSrc); +} + +int xstrsrcp(const char *pStr, const char *pSrc, size_t nPos) +{ + if (pStr == NULL || pSrc == NULL) return XSTDERR; + return xstrnsrc(pStr, strlen(pStr), pSrc, nPos); +} + +int xstrntok(char *pDst, size_t nSize, const char *pStr, size_t nPosit, const char *pDlmt) +{ + if (pDst != NULL) pDst[0] = XSTR_NUL; + if (nPosit >= strlen(pStr)) return XSTDERR; + + int nOffset = xstrsrc(&pStr[nPosit], pDlmt); + if (nOffset <= 0) + { + xstrncpy(pDst, nSize, &pStr[nPosit]); + return 0; + } + + xstrncpys(pDst, nSize, &pStr[nPosit], nOffset); + return (int)nPosit + nOffset + (int)strlen(pDlmt); +} + +size_t xstrncuts(char *pDst, size_t nSize, const char *pSrc, const char *pStart, const char *pEnd) +{ + pDst[0] = XSTR_NUL; + if (pStart == NULL) + { + if (pEnd == NULL) return 0; + int nStatus = xstrntok(pDst, nSize, pSrc, 0, pEnd); + return (nStatus >= 0) ? strnlen(pDst, nSize - 1) : 0; + } + + int nPosit = xstrsrc(pSrc, pStart); + if (nPosit < 0) return 0; + nPosit += (int)strlen(pStart); + + int nStatus = xstrntok(pDst, nSize, pSrc, nPosit, pEnd); + return (nStatus >= 0) ? strnlen(pDst, nSize - 1) : 0; +} + +char* xstrcut(char *pData, const char *pStart, const char *pEnd) +{ + if (pStart == NULL) + { + if (pEnd == NULL) return NULL; + char *pSavePtr = NULL; + return xstrtok(pData, pEnd, &pSavePtr); + } + + char *pLine = strstr(pData, pStart); + if (pLine != NULL) + { + pLine += strlen(pStart); + if (pEnd == NULL) return pLine; + return xstrcut(pLine, NULL, pEnd); + } + + return NULL; +} + +size_t xstrncut(char *pDst, size_t nDstSize, const char *pSrc, size_t nPosit, size_t nSize) +{ + if (pSrc == NULL || !nDstSize || !nSize) return 0; + size_t nDataLen = strlen(pSrc); + if (!nDataLen || nPosit >= nDataLen) return 0; + + size_t nCopySize = XSTD_MIN(nSize, nDstSize); + size_t nPartSize = nDataLen - nPosit; + + nCopySize = XSTD_MIN(nCopySize, nPartSize); + xstrncpys(pDst, nDstSize, &pSrc[nPosit], nCopySize); + return nCopySize; +} + +char* xstracut(const char *pSrc, size_t nPosit, size_t nSize) +{ + if (pSrc == NULL || !nSize) return NULL; + size_t nDataLen = strlen(pSrc); + if (!nDataLen || nPosit >= nDataLen) return NULL; + + char *pDst = (char*)malloc(nSize + 1); + if (pDst == NULL) return NULL; + + size_t nPartSize = nDataLen - nPosit; + size_t nCopySize = XSTD_MIN(nSize, nPartSize); + xstrncpys(pDst, nSize + 1, &pSrc[nPosit], nCopySize); + return pDst; +} + +size_t xstrnrm(char *pStr, size_t nPosit, size_t nSize) +{ + if (pStr == NULL || !nSize) return 0; + int nStrLen = (int)strlen(pStr); + + if (nStrLen <= 0 || nPosit >= (size_t)nStrLen) return 0; + nSize = ((nPosit + nSize) > (size_t)nStrLen) ? nStrLen - nPosit : nSize; + + size_t nTailOffset = nPosit + nSize; + if (nTailOffset >= (size_t)nStrLen) + { + nStrLen = (int)nPosit; + pStr[nStrLen] = XSTR_NUL; + return nStrLen; + } + + size_t nTailSize = nStrLen - nTailOffset; + const char *pTail = &pStr[nTailOffset]; + memmove(&pStr[nPosit], pTail, nTailSize); + + nStrLen -= (int)nSize; + pStr[nStrLen] = XSTR_NUL; + return nStrLen; +} + +char *xstrrep(const char *pOrig, const char *pRep, const char *pWith) +{ + size_t nOrigLen = strlen(pOrig); + size_t nWithLen = strlen(pWith); + size_t nRepLen = strlen(pRep); + int nNext = 0, nCount = 0; + + while ((nNext = xstrntok(NULL, 0, pOrig, nNext, pRep)) > 0) nCount++; + size_t nFreeBytes = ((nWithLen - nRepLen) * nCount) + nOrigLen + 1; + char *pResult = (char*)malloc(nFreeBytes); + + if (pResult == NULL) return NULL; + char *pOffset = pResult; + + while (nCount--) + { + int nCopyLen = xstrsrc(pOrig, pRep); + if (nCopyLen <= 0) break; + + xstrncpyf(pOffset, nFreeBytes, "%s", pOrig); + nFreeBytes -= nCopyLen; + pOffset += nCopyLen; + + xstrncpyf(pOffset, nFreeBytes, "%s", pWith); + nFreeBytes -= nWithLen; + pOffset += nWithLen; + pOrig += nCopyLen + nRepLen; + } + + xstrncpyf(pOffset, nFreeBytes, "%s", pOrig); + return pResult; +} + +char *xstrdup(const char *pStr) +{ + size_t nLength = strlen(pStr); + if (!nLength) return NULL; + + char *pDst = (char*)malloc(++nLength); + if (pDst == NULL) return NULL; + + xstrncpy(pDst, nLength, pStr); + return pDst; +} + +xbool_t xstrused(const char *pStr) +{ + if (pStr == NULL) return XFALSE; + return (pStr[0] != XSTR_NUL) ? XTRUE : XFALSE; +} + +void xstrnull(char *pString, size_t nLength) +{ + if (!nLength) *pString = 0; + else while (nLength--) *pString++ = 0; +} + +void xstrnul(char *pString) +{ + if (pString == NULL) return; + pString[0] = XSTR_NUL; +} + +xarray_t* xstrsplit(const char *pString, const char *pDlmt) +{ + if (!xstrused(pString) || pDlmt == NULL) return NULL; + size_t nDlmtLen = strlen(pDlmt); + if (!nDlmtLen) return NULL; + + xarray_t *pArray = XArray_New(XSTDNON, XFALSE); + if (pArray == NULL) return NULL; + + char sToken[XSTR_MAX]; + size_t nPosit = 0; + int nNext = 0; + + while (!strncmp(&pString[nPosit], pDlmt, nDlmtLen)) nPosit += nDlmtLen; + while((nNext = xstrntok(sToken, sizeof(sToken), &pString[nPosit], nNext, pDlmt)) >= 0) + { + XArray_AddData(pArray, sToken, strlen(sToken)+1); + if (!nNext) break; + } + + if (!pArray->nUsed) + { + XArray_Destroy(pArray); + return NULL; + } + + return pArray; +} + +///////////////////////////////////////////////////////////////////////// +// XString Implementation +///////////////////////////////////////////////////////////////////////// + +int XString_Status(xstring_t *pString) +{ + return (pString->nStatus == XSTDERR) ? + XSTDERR : (int)pString->nLength; +} + +int XString_Resize(xstring_t *pString, size_t nSize) +{ + if (pString->pData == NULL && nSize) + { + pString->pData = (char*)malloc(nSize); + if (pString->pData == NULL) + { + pString->nStatus = XSTDERR; + return pString->nStatus; + } + + pString->nSize = nSize; + pString->nLength = 0; + return (int)nSize; + } + else if (!pString->nSize && nSize) + { + char *pOldBuff = pString->pData; + pString->pData = (char*)malloc(nSize); + + if (pString->pData == NULL) + { + pString->nStatus = XSTDERR; + return pString->nStatus; + } + + pString->nLength = xstrncpyf(pString->pData, nSize, "%s", pOldBuff); + pString->nSize = nSize; + return (int)nSize; + } + else if (pString->pData && pString->nSize && !nSize) + { + free(pString->pData); + pString->pData = NULL; + pString->nLength = 0; + pString->nSize = 0; + return 0; + } + else if (!nSize) + { + pString->pData = NULL; + pString->nLength = 0; + pString->nSize = 0; + return 0; + } + + char* pOldData = pString->pData; + pString->pData = (char*)realloc(pString->pData, nSize); + pString->nLength = XSTD_MIN(pString->nLength, nSize); + + if (nSize && pString->pData == NULL) + { + pString->pData = pOldData; + pString->nStatus = XSTDERR; + return pString->nStatus; + } + + pString->nSize = nSize; + return (int)pString->nSize; +} + +int XString_Increase(xstring_t *pString, size_t nSize) +{ + if (pString->nStatus == XSTDERR) return XSTDERR; + size_t nNewSize = pString->nLength + nSize; + if (nNewSize <= pString->nSize) return (int)pString->nSize; + else if (pString->nFast) nNewSize *= 2; + return XString_Resize(pString, nNewSize); +} + +int XString_Init(xstring_t *pString, size_t nSize, uint8_t nFastAlloc) +{ + pString->nStatus = 0; + pString->nLength = 0; + pString->nAlloc = 0; + pString->nSize = 0; + pString->pData = NULL; + pString->nFast = nFastAlloc; + + XString_Resize(pString, nSize); + + if (nSize && pString->pData != NULL && + pString->nStatus != XSTDERR) + pString->pData[pString->nLength] = XSTR_NUL; + + return XString_Status(pString); +} + +void XString_Clear(xstring_t *pString) +{ + if (pString == NULL) return; + + if (pString->nSize > 0 && + pString->pData != NULL) + free(pString->pData); + + if (pString->nAlloc) + { + free(pString); + return; + } + + pString->nStatus = XSTDNON; + pString->nLength = 0; + pString->nSize = 0; + pString->pData = NULL; +} + +int XString_Set(xstring_t *pString, char *pData, size_t nLength) +{ + pString->nStatus = XSTDNON; + pString->nLength = nLength; + pString->pData = pData; + pString->nSize = 0; + return (int)pString->nLength; +} + +int XString_Add(xstring_t *pString, const char *pData, size_t nLength) +{ + if (!nLength) return (int)pString->nLength; + else if (XString_Increase(pString, nLength + 1) <= 0) return XSTDERR; + size_t nLeft = XSTD_MIN(nLength, pString->nSize - pString->nLength); + + memcpy(&pString->pData[pString->nLength], pData, nLeft); + pString->nLength += nLeft; + pString->pData[pString->nLength] = XSTR_NUL; + return (int)pString->nLength; +} + +int XString_Append(xstring_t *pString, const char *pFmt, ...) +{ + va_list args; + size_t nBytes = 0; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nBytes); + va_end(args); + + if (pDest == NULL) + { + pString->nStatus = XSTDERR; + return XSTDERR; + } + + XString_Add(pString, pDest, nBytes); + free(pDest); + + return XString_Status(pString); +} + +int XString_AddString(xstring_t *pString, xstring_t *pSrc) +{ + if (pSrc->pData == NULL || !pSrc->nLength) return XSTDERR; + XString_Add(pString, pSrc->pData, pSrc->nLength); + return XString_Status(pString); +} + +int XString_Copy(xstring_t *pString, xstring_t *pSrc) +{ + if (pSrc->pData == NULL) return XSTDERR; + XString_Init(pString, pSrc->nSize, pSrc->nFast); + if (pString->nStatus == XSTDERR) return XSTDERR; + + memcpy(pString->pData, pSrc->pData, pSrc->nSize); + pString->nLength = pSrc->nLength; + pString->pData[pString->nLength] = XSTR_NUL; + return (int)pString->nLength; +} + +int XString_Insert(xstring_t *pString, size_t nPosit, const char *pData, size_t nLength) +{ + if (nPosit >= pString->nLength) return XString_Add(pString, pData, nLength); + else if (XString_Increase(pString, nLength + 1) <= 0) return XSTDERR; + + char *pOffset = &pString->pData[nPosit]; + size_t nTailSize = pString->nLength - nPosit + 1; + + memmove(pOffset + nLength, pOffset, nTailSize); + memcpy(pOffset, pData, nLength); + + pString->nLength += nLength; + pString->pData[pString->nLength] = XSTR_NUL; + return (int)pString->nLength; +} + +int XString_InsertFmt(xstring_t *pString, size_t nPosit, const char *pFmt, ...) +{ + va_list args; + size_t nBytes = 0; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nBytes); + va_end(args); + + if (pDest == NULL) + { + pString->nStatus = XSTDERR; + return XSTDERR; + } + + XString_Insert(pString, nPosit, pDest, nBytes); + free(pDest); + + return XString_Status(pString); +} + +int XString_Remove(xstring_t *pString, size_t nPosit, size_t nSize) +{ + if (!nSize || nPosit >= pString->nLength) return 0; + nSize = ((nPosit + nSize) > pString->nLength) ? + pString->nLength - nPosit : nSize; + + size_t nTailOffset = nPosit + nSize; + if (nTailOffset >= pString->nLength) + { + pString->nLength = nPosit; + pString->pData[pString->nLength] = XSTR_NUL; + return (int)pString->nLength; + } + + size_t nTailSize = pString->nLength - nTailOffset; + const char *pTail = &pString->pData[nTailOffset]; + memmove(&pString->pData[nPosit], pTail, nTailSize); + + pString->nLength -= nSize; + pString->pData[pString->nLength] = XSTR_NUL; + return (int)pString->nLength; +} + +int XString_Delete(xstring_t *pString, size_t nPosit, size_t nSize) +{ + XString_Remove(pString, nPosit, nSize); + XString_Resize(pString, pString->nLength + 1); + return XString_Status(pString); +} + +int XString_Advance(xstring_t *pString, size_t nSize) +{ + XString_Delete(pString, 0, nSize); + return XString_Status(pString); +} + +int XString_Case(xstring_t *pString, xstr_case_t eCase, size_t nPosit, size_t nSize) +{ + if (pString->pData == NULL) return XSTDERR; + size_t i, nStart = XSTD_MIN(nPosit, pString->nLength); + size_t nLength = XSTD_MIN(nStart + nSize, pString->nLength); + + for (i = nStart; i < nLength; i++) + { + switch (eCase) + { + case XSTR_LOWER: + pString->pData[i] = (char)tolower(pString->pData[i]); + break; + case XSTR_UPPER: + pString->pData[i] = (char)toupper(pString->pData[i]); + break; + default: + break; + } + } + + return (int)pString->nLength; +} + +int XString_ChangeCase(xstring_t *pString, xstr_case_t eCase) +{ + if (pString->pData == NULL || !pString->nLength) return XSTDERR; + XString_Case(pString, eCase, 0, pString->nLength); + return (pString->nStatus == XSTDERR) ? XSTDERR : (int)pString->nLength; +} + +int XString_Color(xstring_t *pString, const char* pClr, size_t nPosit, size_t nSize) +{ + if (pString->pData == NULL || !pString->nLength) return XSTDERR; + size_t nFirstPart = XSTD_MIN(nPosit, pString->nLength); + size_t nLastPart = nFirstPart + nSize; + size_t nLastSize = pString->nLength - nLastPart; + + if (nFirstPart >= pString->nLength || + nLastPart > pString->nLength) return XSTDERR; + + xstring_t *pTempStr = XString_New(pString->nLength, pString->nFast); + if (pTempStr == NULL) return XSTDERR; + + if ((XString_Add(pTempStr, pString->pData, nFirstPart) == XSTDERR) || + (XString_Add(pTempStr, pClr, strlen(pClr)) == XSTDERR) || + (XString_Add(pTempStr, &pString->pData[nFirstPart], nSize) == XSTDERR) || + (XString_Add(pTempStr, XSTR_FMT_RESET, strlen(XSTR_FMT_RESET)) == XSTDERR) || + (XString_Add(pTempStr, &pString->pData[nLastPart], nLastSize) == XSTDERR)) + { + XString_Clear(pTempStr); + return XSTDERR; + } + + pString->nLength = 0; + XString_AddString(pString, pTempStr); + XString_Clear(pTempStr); + + return XString_Status(pString); +} + +int XString_ChangeColor(xstring_t *pString, const char* pClr) +{ + if (pString->pData == NULL || !pString->nLength) return XSTDERR; + XString_Color(pString, pClr, 0, pString->nLength); + return XString_Status(pString); +} + +int XString_Search(xstring_t *pString, size_t nPos, const char *pSearch) +{ + if (pString == NULL || nPos >= pString->nLength) return XSTDERR; + return xstrsrc(&pString->pData[nPos], pSearch); +} + +int XString_Tokenize(xstring_t *pString, char *pDst, size_t nSize, size_t nPosit, const char *pDlmt) +{ + if (pString == NULL || !pString->nLength) return XSTDERR; + else if (pDst != NULL) pDst[0] = XSTR_NUL; + + if (nPosit >= pString->nLength) return XSTDERR; + int nOffset = xstrsrc(&pString->pData[nPosit], pDlmt); + + if (nOffset <= 0) + { + xstrncpy(pDst, nSize, &pString->pData[nPosit]); + return 0; + } + + xstrncpys(pDst, nSize, &pString->pData[nPosit], nOffset); + return (int)nPosit + nOffset + (int)strlen(pDlmt); +} + +int XString_Token(xstring_t *pString, xstring_t *pDst, size_t nPosit, const char *pDlmt) +{ + if (pString == NULL || !pString->nLength) return XSTDERR; + pDst->pData[0] = XSTR_NUL; + pDst->nLength = 0; + + if (nPosit >= pString->nLength) return XSTDERR; + int nOffset = xstrsrc(&pString->pData[nPosit], pDlmt); + + if (nOffset <= 0) + { + size_t nLength = pString->nLength - nPosit; + XString_Add(pDst, &pString->pData[nPosit], nLength); + return 0; + } + + XString_Add(pDst, &pString->pData[nPosit], nOffset); + return (int)nPosit + nOffset + (int)strlen(pDlmt); +} + +int XString_Replace(xstring_t *pString, const char *pRep, const char *pWith) +{ + if (pString == NULL || !pString->nLength) return XSTDERR; + size_t nWithLen = strlen(pWith); + size_t nRepLen = strlen(pRep); + int nPos = 0; + + while ((nPos = XString_Search(pString, nPos, pRep)) >= 0) + { + XString_Remove(pString, nPos, nRepLen); + XString_Insert(pString, nPos, pWith, nWithLen); + nPos += (int)nWithLen; + } + + return XString_Status(pString); +} + +int XString_Sub(xstring_t *pString, char *pDst, size_t nDstSize, size_t nPos, size_t nSize) +{ + if (pString == NULL || nPos >= pString->nLength) return XSTDERR; + size_t nCopySize = XSTD_MIN(nSize, nDstSize); + size_t nPartSize = pString->nLength - nPos; + nCopySize = XSTD_MIN(nCopySize, nPartSize); + xstrncpys(pDst, nDstSize, &pString->pData[nPos], nCopySize); + return (int)nCopySize; +} + +int XString_SubStr(xstring_t *pString, xstring_t *pSub, size_t nPos, size_t nSize) +{ + if (pString == NULL || nPos >= pString->nLength) return XSTDERR; + XString_Init(pSub, nSize + 1, pString->nFast); + if (pSub->nStatus == XSTDERR) return XSTDERR; + + int nLength = XString_Sub(pString, pSub->pData, pSub->nSize, nPos, nSize); + if (nLength <= 0) + { + XString_Clear(pString); + return XSTDERR; + } + + pSub->nLength = nLength; + return (int)pSub->nLength; +} + +xstring_t *XString_SubNew(xstring_t *pString, size_t nPos, size_t nSize) +{ + xstring_t* pSub = (xstring_t*)malloc(sizeof(xstring_t)); + if (pSub == NULL) return NULL; + + if (XString_SubStr(pString, pSub, nPos, nSize) <= 0) + { + free(pSub); + return NULL; + } + + pSub->nAlloc = 1; + return pSub; +} + +xstring_t *XString_New(size_t nSize, uint8_t nFastAlloc) +{ + xstring_t* pString = (xstring_t*)malloc(sizeof(xstring_t)); + if (pString == NULL) return NULL; + + XString_Init(pString, nSize, nFastAlloc); + pString->nAlloc = 1; + + if (pString->nStatus == XSTDERR) + { + XString_Clear(pString); + return NULL; + } + + return pString; +} + +xstring_t *XString_From(const char *pData, size_t nLength) +{ + if (pData == NULL || !nLength) return NULL; + + xstring_t* pString = XString_New(nLength, 0); + if (pString == NULL) return NULL; + + XString_Add(pString, pData, nLength); + if (pString->nStatus == XSTDERR) + { + XString_Clear(pString); + return NULL; + } + + return pString; +} + +xstring_t *XString_FromFmt(const char *pFmt, ...) +{ + va_list args; + size_t nBytes = 0; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nBytes); + va_end(args); + + if (pDest == NULL) return NULL; + xstring_t* pString = XString_From(pDest, nBytes); + + free(pDest); + return pString; +} + +int XString_InitFrom(xstring_t *pStr, const char *pFmt, ...) +{ + va_list args; + size_t nBytes = 0; + + va_start(args, pFmt); + char *pDest = xstracpyargs(pFmt, args, &nBytes); + va_end(args); + if (pDest == NULL) return XSTDERR; + + if (XString_Init(pStr, nBytes, 0) == XSTDERR || + XString_Add(pStr, pDest, nBytes) == XSTDERR) + { + free(pDest); + return XSTDERR; + } + + free(pDest); + return (int)pStr->nLength; +} + +xstring_t *XString_FromStr(xstring_t *pString) +{ + if (pString == NULL || !pString->nLength) return NULL; + return XString_From(pString->pData, pString->nLength); +} + +int XString_Cut(xstring_t *pString, char *pDst, size_t nSize, const char *pFrom, const char *pTo) +{ + if (pString == NULL || !pString->nLength) return XSTDERR; + int nStartPos = XString_Search(pString, 0, pFrom); + if (nStartPos < 0) return XSTDERR; + + int nSubSize = 0; + nStartPos += (int)strlen(pFrom); + + if ((size_t)nStartPos >= pString->nLength) return XSTDERR; + if (pTo == NULL) nSubSize = (int)pString->nLength - nStartPos; + else nSubSize = XString_Search(pString, (size_t)nStartPos, pTo); + + if (nSubSize < 0) return XSTDERR; + xstrncpys(pDst, nSize, &pString->pData[nStartPos], nSubSize); + return nSubSize; +} + +int XString_CutSub(xstring_t *pString, xstring_t *pSub, const char *pFrom, const char *pTo) +{ + if (pString == NULL || !pString->nLength) return XSTDERR; + XString_Init(pSub, pString->nLength, pString->nFast); + if (pSub->nStatus == XSTDERR) return XSTDERR; + + int nLength = XString_Cut(pString, pSub->pData, pSub->nSize, pFrom, pTo); + if (nLength <= 0) + { + XString_Clear(pSub); + return XSTDERR; + } + + pSub->nLength = nLength; + return (int)pSub->nLength; +} + +xstring_t *XString_CutNew(xstring_t *pString, const char *pFrom, const char *pTo) +{ + xstring_t* pSub = (xstring_t*)malloc(sizeof(xstring_t)); + if (pSub == NULL) return NULL; + + if (XString_CutSub(pString, pSub, pFrom, pTo) == XSTDERR) + { + free(pSub); + return NULL; + } + + pSub->nAlloc = 1; + return pSub; +} + +void XString_ArrayClearCb(xarray_data_t *pArrData) +{ + if (pArrData == NULL || pArrData->pData == NULL) return; + xstring_t *pString = (xstring_t*)pArrData->pData; + XString_Clear(pString); +} + +xarray_t* XString_SplitStr(xstring_t *pString, const char *pDlmt) +{ + if (pString == NULL || !pString->nLength) return NULL; + + xstring_t *pToken = XString_New(XSTR_MIN, 0); + if (pToken == NULL) return NULL; + + xarray_t *pArray = XArray_New(2, 0); + if (pArray == NULL) + { + XString_Clear(pToken); + return NULL; + } + + pArray->clearCb = XString_ArrayClearCb; + int nNext = 0; + + while ((nNext = XString_Token(pString, pToken, nNext, pDlmt)) >= 0) + { + XArray_AddData(pArray, pToken, 0); + if (!nNext) break; + + pToken = XString_New(XSTR_MIN, 0); + if (pToken == NULL) + { + XArray_Destroy(pArray); + return NULL; + } + } + + if (!pArray->nUsed) + { + XArray_Destroy(pArray); + return NULL; + } + + return pArray; +} + +xarray_t* XString_Split(const char *pCStr, const char *pDlmt) +{ + xstring_t *pString = XString_From(pCStr, strlen(pCStr)); + if (pString == NULL) return NULL; + + xarray_t *pArray = XString_SplitStr(pString, pDlmt); + XString_Clear(pString); + return pArray; +} diff --git a/src/xstr.h b/src/xstr.h new file mode 100644 index 0000000..098c561 --- /dev/null +++ b/src/xstr.h @@ -0,0 +1,203 @@ +/*! + * @file libxutils/src/xstr.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief This source includes string operations. + */ + +#ifndef __XUTILS_XSTR_H__ +#define __XUTILS_XSTR_H__ + +#include "xstd.h" +#include "xtype.h" +#include "array.h" + +/* Supported colors */ +#define XSTR_CLR_NONE "\x1B[0m" +#define XSTR_CLR_RED "\x1B[31m" +#define XSTR_CLR_GREEN "\x1B[32m" +#define XSTR_CLR_YELLOW "\x1B[33m" +#define XSTR_CLR_BLUE "\x1B[34m" +#define XSTR_CLR_MAGENTA "\x1B[35m" +#define XSTR_CLR_CYAN "\x1B[36m" +#define XSTR_CLR_WHITE "\x1B[37m" + +#define XSTR_CLR_LIGHT_RED "\x1B[31;1m" +#define XSTR_CLR_LIGHT_GREEN "\x1B[32;1m" +#define XSTR_CLR_LIGHT_YELLOW "\x1B[33;1m" +#define XSTR_CLR_LIGHT_BLUE "\x1B[34;1m" +#define XSTR_CLR_LIGHT_MAGENTA "\x1B[35;1m" +#define XSTR_CLR_LIGHT_CYAN "\x1B[36;1m" +#define XSTR_CLR_LIGHT_WHITE "\x1B[37;1m" + +/* Supported background colors */ +#define XSTR_BACK_BLACK "\x1B[40m" +#define XSTR_BACK_RED "\x1B[41m" +#define XSTR_BACK_GREEN "\x1B[42m" +#define XSTR_BACK_YELLOW "\x1B[43m" +#define XSTR_BACK_BLUE "\x1B[44m" +#define XSTR_BACK_MAGENTA "\x1B[45m" +#define XSTR_BACK_CYAN "\x1B[46m" +#define XSTR_BACK_WHITE "\x1B[47m" + +/* Supported string formats */ +#define XSTR_FMT_BOLD "\x1B[1m" +#define XSTR_FMT_DIM "\x1B[2m" +#define XSTR_FMT_ITALIC "\x1B[3m" +#define XSTR_FMT_ULINE "\x1B[4m" +#define XSTR_FMT_FLICK "\x1B[5m" +#define XSTR_FMT_BLINK "\x1B[6m" +#define XSTR_FMT_HIGHLITE "\x1B[7m" +#define XSTR_FMT_HIDE "\x1B[8m" +#define XSTR_FMT_CROSS "\x1B[9m" +#define XSTR_FMT_RESET XSTR_CLR_NONE + +#define XSTR_DOUBLE_ARG_MAX 309 +#define XSTR_INT_ARG_MAX 32 +#define XSTR_LETTERS 52 + +#define XSTR_MAX 8192 +#define XSTR_MID 4096 +#define XSTR_MIN 2048 +#define XSTR_TINY 256 +#define XSTR_NPOS UINT_MAX +#define XSTR_STACK XSTR_MID + +#define XSTR_SPACE_CHAR ' ' +#define XSTR_NEW_LINE "\n" +#define XSTR_SPACE " " +#define XSTR_EMPTY "" +#define XSTR_NUL '\0' +#define XSTR_INIT { XSTR_NUL } + +#define XSTR_AVAIL(arr)((int)sizeof(arr)-(int)strlen(arr)) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + XSTR_LOWER = 0, + XSTR_UPPER +} xstr_case_t; + +///////////////////////////////////////////////////////////////////////// +// C string functions +///////////////////////////////////////////////////////////////////////// + +xarray_t* xstrsplit(const char *pString, const char *pDlmt); +char* xstrrep(const char *pOrig, const char *pRep, const char *pWith); +char* xstrdup(const char *pStr); +char* xstracpy(const char *pFmt, ...); +char* xstracpyn(size_t *nSize, const char *pFmt, ...); +char* xstralloc(size_t nSize); + +int xstrncpyarg(char *pDest, size_t nSize, const char *pFmt, va_list args); +char* xstracpyarg(const char *pFmt, va_list args); +char* xstracpyargs(const char *pFmt, va_list args, size_t *nSize); +size_t xstrarglen(const char *pFmt, va_list args); + +size_t xstrxcpyf(char **pDst, const char *pFmt, ...); +char* xstrxcpy(const char *pFmt, ...); + +size_t xstrncpy(char *pDst, size_t nSize, const char* pSrc); +size_t xstrncpys(char *pDst, size_t nDstSize, const char *pSrc, size_t nSrcLen); +size_t xstrncpyf(char *pDst, size_t nSize, const char *pFmt, ...); +size_t xstrncat(char *pDst, size_t nSize, const char *pFmt, ...); +size_t xstrncatf(char *pDst, size_t nAvail, const char *pFmt, ...); +size_t xstrncatsf(char *pDst, size_t nSize, size_t nAvail, const char *pFmt, ...); + +size_t xstrrand(char *pDst, size_t nSize, size_t nLength, xbool_t bUpper, xbool_t bNumbers); +size_t xstrextra(const char *pStr, size_t nLength, size_t nMaxChars, size_t *pChars, size_t *pPosit); +size_t xstrncpyfl(char *pDst, size_t nSize, size_t nFLen, char cFChar, const char* pFmt, ...); +size_t xstrnlcpyf(char *pDst, size_t nSize, size_t nFLen, char cFChar, const char* pFmt, ...); +size_t xstrfill(char *pDst, size_t nSize, size_t nLength, char cFill); +size_t xstrisextra(const char *pOffset); + +int xstrnsrc(const char *pStr, size_t nLen, const char *pSrc, size_t nPos); +int xstrsrcp(const char *pStr, const char *pSrc, size_t nPos); +int xstrsrc(const char *pStr, const char *pSrc); + +char* xstracase(const char *pSrc, xstr_case_t eCase); +char* xstracasen(const char *pSrc, xstr_case_t eCase, size_t nLength); +size_t xstrcase(char *pSrc, xstr_case_t eCase); +size_t xstrncase(char *pDst, size_t nSize, xstr_case_t eCase, const char *pSrc); +size_t xstrncases(char* pDst, size_t nSize, xstr_case_t eCase, const char *pSrc, size_t nSrcLen); + +char* xstrtok(char* pString, const char* pDelimiter, char** pContext); +char* xstrcut(char *pData, const char *pStart, const char *pEnd); +char* xstracut(const char *pSrc, size_t nPosit, size_t nSize); +size_t xstrncut(char *pDst, size_t nDstSize, const char *pData, size_t nPosit, size_t nSize); +size_t xstrncuts(char *pDst, size_t nSize, const char *pSrc, const char *pStart, const char *pEnd); + +int xstrntok(char *pDst, size_t nSize, const char *pStr, size_t nPosit, const char *pDlmt); +size_t xstrnclr(char *pDst, size_t nSize, const char* pClr, const char* pStr, ...); +size_t xstrnrm(char *pStr, size_t nPosit, size_t nSize); +void xstrnull(char *pString, size_t nLength); +void xstrnul(char *pString); +xbool_t xstrused(const char *pStr); + +///////////////////////////////////////////////////////////////////////// +// XString Implementation +///////////////////////////////////////////////////////////////////////// + +typedef struct XString { + char* pData; + size_t nLength; + size_t nSize; + int16_t nStatus; + xbool_t nAlloc; + xbool_t nFast; +} xstring_t; + +xstring_t *XString_New(size_t nSize, uint8_t nFastAlloc); +xstring_t *XString_From(const char *pData, size_t nLength); +xstring_t *XString_FromFmt(const char *pFmt, ...); +xstring_t *XString_FromStr(xstring_t *pString); + +int XString_Init(xstring_t *pString, size_t nSize, uint8_t nFastAlloc); +int XString_InitFrom(xstring_t *pStr, const char *pFmt, ...); +int XString_Set(xstring_t *pString, char *pData, size_t nLength); +int XString_Increase(xstring_t *pString, size_t nSize); +int XString_Resize(xstring_t *pString, size_t nSize); +void XString_Clear(xstring_t *pString); + +int XString_Add(xstring_t *pString, const char *pData, size_t nLength); +int XString_Copy(xstring_t *pString, xstring_t *pSrc); +int XString_Append(xstring_t *pString, const char *pFmt, ...); +int XString_AddString(xstring_t *pString, xstring_t *pSrc); + +int XString_Insert(xstring_t *pString, size_t nPosit, const char *pData, size_t nLength); +int XString_InsertFmt(xstring_t *pString, size_t nPosit, const char *pFmt, ...); + +int XString_Remove(xstring_t *pString, size_t nPosit, size_t nSize); +int XString_Delete(xstring_t *pString, size_t nPosit, size_t nSize); +int XString_Advance(xstring_t *pString, size_t nSize); + +int XString_Tokenize(xstring_t *pString, char *pDst, size_t nSize, size_t nPosit, const char *pDlmt); +int XString_Token(xstring_t *pString, xstring_t *pDst, size_t nPosit, const char *pDlmt); +int XString_Case(xstring_t *pString, xstr_case_t eCase, size_t nPosit, size_t nSize); +int XString_Color(xstring_t *pString, const char* pClr, size_t nPosit, size_t nSize); +int XString_ChangeCase(xstring_t *pString, xstr_case_t eCase); +int XString_ChangeColor(xstring_t *pString, const char* pClr); +int XString_Replace(xstring_t *pString, const char *pRep, const char *pWith); +int XString_Search(xstring_t *pString, size_t nPos, const char *pSearch); + +xarray_t* XString_SplitStr(xstring_t *pString, const char *pDlmt); +xarray_t* XString_Split(const char *pCStr, const char *pDlmt); + +int XString_Sub(xstring_t *pString, char *pDst, size_t nDstSize, size_t nPos, size_t nSize); +int XString_SubStr(xstring_t *pString, xstring_t *pSub, size_t nPos, size_t nSize); +xstring_t *XString_SubNew(xstring_t *pString, size_t nPos, size_t nSize); + +int XString_Cut(xstring_t *pString, char *pDst, size_t nSize, const char *pFrom, const char *pTo); +int XString_CutSub(xstring_t *pString, xstring_t *pSub, const char *pFrom, const char *pTo); +xstring_t *XString_CutNew(xstring_t *pString, const char *pFrom, const char *pTo); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XSTR_H__ */ \ No newline at end of file diff --git a/src/xtime.c b/src/xtime.c new file mode 100644 index 0000000..fee7667 --- /dev/null +++ b/src/xtime.c @@ -0,0 +1,318 @@ +/*! + * @file libxutils/src/xtime.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get system date, convert between formats, + * get month days, calculate leap year, and e.t.c. + */ + +#include "xstr.h" +#include "xtime.h" + +#if defined(__linux__) && defined(CLOCK_REALTIME) +#define XTIME_USE_CLOCK +#endif + +void XTime_Init(xtime_t *pTime) +{ + pTime->nYear = pTime->nMonth = 0; + pTime->nDay = pTime->nHour = 0; + pTime->nMin = pTime->nSec = 0; + pTime->nFraq = 0; +} + +void XTime_FromTm(xtime_t *pTime, const struct tm *pTm) +{ + pTime->nYear = (uint16_t)pTm->tm_year+1900; + pTime->nMonth = (uint8_t)pTm->tm_mon+1; + pTime->nDay = (uint8_t)pTm->tm_mday; + pTime->nHour = (uint8_t)pTm->tm_hour; + pTime->nMin = (uint8_t)pTm->tm_min; + pTime->nSec = (uint8_t)pTm->tm_sec; + pTime->nFraq = 0; +} + +int XTime_FromStr(xtime_t *pTime, const char *pStr) +{ + XTime_Init(pTime); +#ifdef _WIN32 + return sscanf_s(pStr, "%04d%02d%02d%02d%02d%02d%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec, + (int*)&pTime->nFraq); +#else + return sscanf(pStr, "%04d%02d%02d%02d%02d%02d%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec, + (int*)&pTime->nFraq); +#endif +} + +int XTime_FromHStr(xtime_t *pTime, const char *pStr) +{ + XTime_Init(pTime); +#ifdef _WIN32 + return sscanf_s(pStr, "%04d.%02d.%02d-%02d:%02d:%02d.%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec, + (int*)&pTime->nFraq); +#else + return sscanf(pStr, "%04d.%02d.%02d-%02d:%02d:%02d.%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec, + (int*)&pTime->nFraq); +#endif +} + +int XTime_FromLstr(xtime_t *pTime, const char *pStr) +{ + XTime_Init(pTime); + +#ifdef _WIN32 + return sscanf_s(pStr, "%04d/%02d/%02d/%02d/%02d/%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec); +#else + return sscanf(pStr, "%04d/%02d/%02d/%02d/%02d/%02d", + (int*)&pTime->nYear, (int*)&pTime->nMonth, (int*)&pTime->nDay, + (int*)&pTime->nHour, (int*)&pTime->nMin, (int*)&pTime->nSec); +#endif +} + +void XTime_FromEpoch(xtime_t *pTime, const time_t nTime) +{ + struct tm timeinfo; +#ifdef _WIN32 + localtime_s(&timeinfo, &nTime); +#else + localtime_r(&nTime, &timeinfo); +#endif + XTime_FromTm(pTime, &timeinfo); + pTime->nFraq = 0; +} + +void XTime_FromU64(xtime_t *pTime, const uint64_t nTime) +{ + pTime->nYear = XTIME_U64_YEAR(nTime); + pTime->nMonth = XTIME_U64_MONTH(nTime); + pTime->nDay = XTIME_U64_DAY(nTime); + pTime->nHour = XTIME_U64_HOUR(nTime); + pTime->nMin = XTIME_U64_MIN(nTime); + pTime->nSec = XTIME_U64_SEC(nTime); + pTime->nFraq = XTIME_U64_FRAQ(nTime); +} + +void XTime_ToTm(const xtime_t *pTime, struct tm *pTm) +{ + pTm->tm_year = pTime->nYear - 1900; + pTm->tm_mon = pTime->nMonth - 1; + pTm->tm_mday = pTime->nDay; + pTm->tm_hour = pTime->nHour; + pTm->tm_min = pTime->nMin; + pTm->tm_sec = pTime->nSec; + pTm->tm_isdst = -1; +} + +time_t XTime_ToEpoch(const xtime_t *pTime) +{ + struct tm tminf; + XTime_ToTm(pTime, &tminf); + return mktime(&tminf); +} + +size_t XTime_ToStr(const xtime_t *pTime, char *pStr, size_t nSize) +{ + return xstrncpyf(pStr, nSize, "%04d%02d%02d%02d%02d%02d%02d", + pTime->nYear, pTime->nMonth, pTime->nDay, pTime->nHour, + pTime->nMin, pTime->nSec, pTime->nFraq); +} + +size_t XTime_ToHstr(const xtime_t *pTime, char *pStr, size_t nSize) +{ + return xstrncpyf(pStr, nSize, "%04d.%02d.%02d-%02d:%02d:%02d.%02d", + pTime->nYear, pTime->nMonth, pTime->nDay, pTime->nHour, + pTime->nMin, pTime->nSec, pTime->nFraq); +} + +size_t XTime_ToLstr(const xtime_t *pTime, char *pStr, size_t nSize) +{ + return xstrncpyf(pStr, nSize, "%04d/%02d/%02d/%02d/%02d/%02d", + pTime->nYear, pTime->nMonth, pTime->nDay, + pTime->nHour, pTime->nMin, pTime->nSec); +} + +size_t XTime_ToHTTP(const xtime_t *pTime, char *pStr, size_t nSize) +{ + struct tm timeinfo; + time_t rawTime = XTime_ToEpoch(pTime); +#ifdef _WIN32 + gmtime_s(&timeinfo, &rawTime); +#else + gmtime_r(&rawTime, &timeinfo); +#endif + return strftime(pStr, nSize, "%a, %d %b %G %H:%M:%S GMT", &timeinfo); +} + +uint64_t XTime_ToU64(const xtime_t *pTime) +{ + uint64_t nTime; + nTime = (uint64_t)pTime->nYear; + nTime <<= 8; + nTime += (uint64_t)pTime->nMonth; + nTime <<= 8; + nTime += (uint64_t)pTime->nDay; + nTime <<= 8; + nTime += (uint64_t)pTime->nHour; + nTime <<= 8; + nTime += (uint64_t)pTime->nMin; + nTime <<= 8; + nTime += (uint64_t)pTime->nSec; + nTime <<= 8; + nTime += (uint64_t)pTime->nFraq; + return nTime; +} + +int XTime_GetLeapYear(int nYear) +{ + int nRetVal = 0; + if (nYear % 4 == 0) + { + if (nYear % 100 == 0) + { + if (nYear % 400 == 0) nRetVal = 1; + else nRetVal = 0; + } + else nRetVal = 1; + } + else nRetVal = 0; + return nRetVal; +} + +int XTime_GetMonthDays(int nYear, int nMonth) +{ + int nLeap = XTime_GetLeapYear(nYear); + if (nMonth == 2) return nLeap ? 28 : 29; + + if (nMonth == 4 || + nMonth == 6 || + nMonth == 9 || + nMonth == 11) + return 30; + + return 31; +} + +int XTime_MonthDays(const xtime_t *pTime) +{ + return XTime_GetMonthDays(pTime->nYear, pTime->nMonth); +} + +int XTime_LeapYear(const xtime_t *pTime) +{ + return XTime_GetLeapYear(pTime->nYear); +} + +double XTime_Diff(const xtime_t *pSrc1, const xtime_t *pSrc2) +{ + struct tm tm1, tm2; + XTime_ToTm(pSrc1, &tm1); + XTime_ToTm(pSrc2, &tm2); + return difftime(mktime(&tm1), mktime(&tm2)); +} + +void XTime_Copy(xtime_t *pDst, const xtime_t *pSrc) +{ + if (pDst == NULL || pSrc == NULL) return; + XTime_FromU64(pDst, XTime_ToU64(pSrc)); +} + +void XTime_Make(xtime_t *pSrc) +{ + struct tm timeinfo; + XTime_ToTm(pSrc, &timeinfo); + mktime(&timeinfo); + XTime_FromTm(pSrc, &timeinfo); +} + +int XTime_GetClock(xtime_spec_t *pTs) +{ + pTs->nNanoSec = 0; + pTs->nSec = 0; +#ifdef XTIME_USE_CLOCK + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) return XSTDERR; + pTs->nNanoSec = ts.tv_nsec; + pTs->nSec = ts.tv_sec; +#elif _WIN32 + const uint64_t nEpoch = ((uint64_t)116444736000000000ULL); + SYSTEMTIME sysTime; + FILETIME fileTime; + uint64_t nTime; + + GetSystemTime(&sysTime); + SystemTimeToFileTime(&sysTime, &fileTime); + nTime = ((uint64_t)fileTime.dwLowDateTime); + nTime += ((uint64_t)fileTime.dwHighDateTime) << 32; + + pTs->nNanoSec = ((uint64_t)sysTime.wMilliseconds * 1000000); + pTs->nSec = (time_t)((nTime - nEpoch) / 10000000L); +#else + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) return XSTDERR; + pTs->nNanoSec = tv.tv_usec * 1000; + pTs->nSec = tv.tv_sec; +#endif + return XSTDOK; +} + +uint32_t XTime_GetUsec(void) +{ + xtime_spec_t now; + XTime_GetClock(&now); + uint32_t nTimeStamp = ((uint32_t)now.nNanoSec / 1000); + return (uint32_t)now.nSec * 1000000 + nTimeStamp; +} + +void XTime_Get(xtime_t *pTime) +{ + xtime_spec_t now; + XTime_GetClock(&now); + XTime_FromEpoch(pTime, now.nSec); + pTime->nFraq = (uint8_t)(now.nNanoSec / 10000000); +} + +size_t XTime_GetStr(char *pDst, size_t nSize, xtime_fmt_t eFmt) +{ + if (pDst == NULL || !nSize) return 0; + + xtime_t date; + XTime_Get(&date); + + switch (eFmt) + { + case XTIME_STR_SIMPLE: return XTime_ToStr(&date, pDst, nSize); + case XTIME_STR_HTTP: return XTime_ToHTTP(&date, pDst, nSize); + case XTIME_STR_LSTR: return XTime_ToLstr(&date, pDst, nSize); + case XTIME_STR_HSTR: return XTime_ToHstr(&date, pDst, nSize); + default: break; + } + + pDst[0] = '\0'; + return 0; +} + +uint64_t XTime_GetU64(void) +{ + xtime_t xtime; + XTime_Get(&xtime); + return XTime_ToU64(&xtime); +} + +void XTime_GetTm(struct tm *pTm) +{ + xtime_t xtime; + XTime_FromEpoch(&xtime, time(NULL)); + XTime_ToTm(&xtime, pTm); +} diff --git a/src/xtime.h b/src/xtime.h new file mode 100644 index 0000000..5866344 --- /dev/null +++ b/src/xtime.h @@ -0,0 +1,88 @@ +/*! + * @file libxutils/src/xtime.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get system date, convert between formats, + * get month days, calculate leap year, and e.t.c. + */ + +#ifndef __XUTILS_XTIME_H__ +#define __XUTILS_XTIME_H__ + +#include "xstd.h" + +#define XTIME_U64_YEAR(tm) (int)(tm >> 48) // 16 bit +#define XTIME_U64_MONTH(tm) (int)((tm >> 40) & 0xFF) // 8 bit +#define XTIME_U64_DAY(tm) (int)((tm >> 32) & 0xFF) // 8 bit +#define XTIME_U64_HOUR(tm) (int)((tm >> 24) & 0xFF) // 8 bit +#define XTIME_U64_MIN(tm) (int)((tm >> 16) & 0xFF) // 8 bit +#define XTIME_U64_SEC(tm) (int)((tm >> 8) & 0xFF) // 8 bit +#define XTIME_U64_FRAQ(tm) (int)(tm & 0xFF) // 8 bit + +#define XTIME_MAX 64 + +typedef enum { + XTIME_STR_SIMPLE = 0, + XTIME_STR_HTTP, + XTIME_STR_LSTR, + XTIME_STR_HSTR +} xtime_fmt_t; + +typedef struct XTimeSpec { + uint64_t nNanoSec; + time_t nSec; +} xtime_spec_t; + +typedef struct XTime { + uint16_t nYear; + uint8_t nMonth; + uint8_t nDay; + uint8_t nHour; + uint8_t nMin; + uint8_t nSec; + uint8_t nFraq; +} xtime_t; + +#ifdef __cplusplus +extern "C" { +#endif + +void XTime_Init(xtime_t *pTime); +void XTime_Get(xtime_t *pTime); +void XTime_GetTm(struct tm *pTm); +uint64_t XTime_GetU64(void); +uint32_t XTime_GetUsec(void); +int XTime_GetClock(xtime_spec_t *pTs); +size_t XTime_GetStr(char *pDst, size_t nSize, xtime_fmt_t eFmt); + +int XTime_GetLeapYear(int nYear); +int XTime_GetMonthDays(int nYear, int nMonth); +int XTime_MonthDays(const xtime_t *pTime); +int XTime_LeapYear(const xtime_t *pTime); + +void XTime_Make(xtime_t *pSrc); +void XTime_Copy(xtime_t *pDst, const xtime_t *pSrc); +double XTime_Diff(const xtime_t *pSrc1, const xtime_t *pSrc2); + +uint64_t XTime_ToU64(const xtime_t *pTime); +time_t XTime_ToEpoch(const xtime_t *pTime); +size_t XTime_ToStr(const xtime_t *pTime, char *pStr, size_t nSize); +size_t XTime_ToHstr(const xtime_t *pTime, char *pStr, size_t nSize); +size_t XTime_ToLstr(const xtime_t *pTime, char *pStr, size_t nSize); +size_t XTime_ToHTTP(const xtime_t *pTime, char *pStr, size_t nSize); +void XTime_ToTm(const xtime_t* pTime, struct tm* pTm); + +void XTime_FromEpoch(xtime_t *pTime, const time_t nTime); +void XTime_FromU64(xtime_t *pTime, const uint64_t nTime); +void XTime_FromTm(xtime_t *pTime, const struct tm *pTm); +int XTime_FromHstr(xtime_t *pTime, const char *pStr); +int XTime_FromLstr(xtime_t *pTime, const char *pStr); +int XTime_FromStr(xtime_t *pTime, const char *pStr); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XTIME_H__ */ diff --git a/src/xtop.c b/src/xtop.c new file mode 100644 index 0000000..6117ec7 --- /dev/null +++ b/src/xtop.c @@ -0,0 +1,535 @@ +/*! + * @file libxutils/src/xtop.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get process and system statistics about + * cpu usage, network usage, memory usage, etc... + */ + +#include "xstd.h" +#include "xtype.h" +#include "sync.h" +#include "xfs.h" +#include "xstr.h" +#include "addr.h" +#include "xtop.h" + +#define XPROC_BUFFER_SIZE 2048 + +void XTop_ClearCb(xarray_data_t *pArrData) +{ + if (pArrData == NULL) return; + free(pArrData->pData); +} + +#ifndef _WIN32 +int XTop_GetMemoryInfo(xtop_stats_t *pStats, xmem_info_t *pMemInfo) +{ + pMemInfo->nResidentMemory = XSYNC_ATOMIC_GET(&pStats->memInfo.nResidentMemory); + pMemInfo->nVirtualMemory = XSYNC_ATOMIC_GET(&pStats->memInfo.nVirtualMemory); + pMemInfo->nMemoryShared = XSYNC_ATOMIC_GET(&pStats->memInfo.nMemoryShared); + pMemInfo->nMemoryCached = XSYNC_ATOMIC_GET(&pStats->memInfo.nMemoryCached); + pMemInfo->nReclaimable = XSYNC_ATOMIC_GET(&pStats->memInfo.nReclaimable); + pMemInfo->nMemoryAvail = XSYNC_ATOMIC_GET(&pStats->memInfo.nMemoryAvail); + pMemInfo->nMemoryTotal = XSYNC_ATOMIC_GET(&pStats->memInfo.nMemoryTotal); + pMemInfo->nMemoryFree = XSYNC_ATOMIC_GET(&pStats->memInfo.nMemoryFree); + pMemInfo->nSwapCached = XSYNC_ATOMIC_GET(&pStats->memInfo.nSwapCached); + pMemInfo->nSwapTotal = XSYNC_ATOMIC_GET(&pStats->memInfo.nSwapTotal); + pMemInfo->nSwapFree = XSYNC_ATOMIC_GET(&pStats->memInfo.nSwapFree); + pMemInfo->nBuffers = XSYNC_ATOMIC_GET(&pStats->memInfo.nBuffers); + return pMemInfo->nMemoryTotal; +} + +static void XTop_CopyCPUUsage(xproc_info_t *pDstUsage, xproc_info_t *pSrcUsage) +{ + pDstUsage->nUserSpaceChilds = XSYNC_ATOMIC_GET(&pSrcUsage->nUserSpaceChilds); + pDstUsage->nKernelSpaceChilds = XSYNC_ATOMIC_GET(&pSrcUsage->nUserSpaceChilds); + pDstUsage->nUserSpace = XSYNC_ATOMIC_GET(&pSrcUsage->nUserSpace); + pDstUsage->nKernelSpace = XSYNC_ATOMIC_GET(&pSrcUsage->nKernelSpace); + pDstUsage->nTotalTime = XSYNC_ATOMIC_GET(&pSrcUsage->nTotalTime); + pDstUsage->nUserSpaceUsage = XSYNC_ATOMIC_GET(&pSrcUsage->nUserSpaceUsage); + pDstUsage->nKernelSpaceUsage = XSYNC_ATOMIC_GET(&pSrcUsage->nKernelSpaceUsage); +} + +static void XTop_CopyCPUInfo(xcpu_info_t *pDstInfo, xcpu_info_t *pSrcInfo) +{ + pDstInfo->nSoftInterruptsRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nSoftInterruptsRaw); + pDstInfo->nHardInterruptsRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nHardInterruptsRaw); + pDstInfo->nKernelSpaceRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nKernelSpaceRaw); + pDstInfo->nUserSpaceNicedRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nUserSpaceNicedRaw); + pDstInfo->nGuestNicedRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nGuestNicedRaw); + pDstInfo->nUserSpaceRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nUserSpaceRaw); + pDstInfo->nIdleTimeRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nIdleTimeRaw); + pDstInfo->nIOWaitRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nIOWaitRaw); + pDstInfo->nStealRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nStealRaw); + pDstInfo->nGuestRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nGuestRaw); + pDstInfo->nTotalRaw = XSYNC_ATOMIC_GET(&pSrcInfo->nTotalRaw); + pDstInfo->nSoftInterrupts = XSYNC_ATOMIC_GET(&pSrcInfo->nSoftInterrupts); + pDstInfo->nHardInterrupts = XSYNC_ATOMIC_GET(&pSrcInfo->nHardInterrupts); + pDstInfo->nKernelSpace = XSYNC_ATOMIC_GET(&pSrcInfo->nKernelSpace); + pDstInfo->nUserSpaceNiced = XSYNC_ATOMIC_GET(&pSrcInfo->nUserSpaceNiced); + pDstInfo->nGuestNiced = XSYNC_ATOMIC_GET(&pSrcInfo->nGuestNiced); + pDstInfo->nUserSpace = XSYNC_ATOMIC_GET(&pSrcInfo->nUserSpace); + pDstInfo->nIdleTime = XSYNC_ATOMIC_GET(&pSrcInfo->nIdleTime); + pDstInfo->nIOWait = XSYNC_ATOMIC_GET(&pSrcInfo->nIOWait); + pDstInfo->nStealTime = XSYNC_ATOMIC_GET(&pSrcInfo->nStealTime); + pDstInfo->nGuestTime = XSYNC_ATOMIC_GET(&pSrcInfo->nGuestTime); + pDstInfo->nID = XSYNC_ATOMIC_GET(&pSrcInfo->nID); +} + +int XTop_GetCPUStats(xtop_stats_t *pStats, xcpu_stats_t *pCpuStats) +{ + pCpuStats->nLoadAvg[0] = pCpuStats->nLoadAvg[1] = 0; + pCpuStats->nLoadAvg[2] = pCpuStats->cores.nUsed = 0; + + int i, nCPUCores = XSYNC_ATOMIC_GET(&pStats->cpuStats.nCoreCount); + if (nCPUCores <= 0) return 0; + + if (XArray_Init(&pCpuStats->cores, 1, 0) == NULL) return -1; + pCpuStats->cores.clearCb = XTop_ClearCb; + + XTop_CopyCPUUsage(&pCpuStats->usage, &pStats->cpuStats.usage); + XTop_CopyCPUInfo(&pCpuStats->sum, &pStats->cpuStats.sum); + + for (i = 0; i < nCPUCores; i++) + { + xcpu_info_t *pSrcInfo = (xcpu_info_t*)XArray_GetData(&pStats->cpuStats.cores, i); + if (pSrcInfo == NULL) continue; + + xcpu_info_t *pDstInfo = (xcpu_info_t*)malloc(sizeof(xcpu_info_t)); + if (pDstInfo == NULL) continue; + + XTop_CopyCPUInfo(pDstInfo, pSrcInfo); + int nStatus = XArray_AddData(&pCpuStats->cores, pDstInfo, 0); + if (nStatus < 0) free(pDstInfo); + } + + pCpuStats->nCoreCount = pCpuStats->cores.nUsed; + pCpuStats->nLoadAvg[0] = XSYNC_ATOMIC_GET(&pStats->cpuStats.nLoadAvg[0]); + pCpuStats->nLoadAvg[1] = XSYNC_ATOMIC_GET(&pStats->cpuStats.nLoadAvg[1]); + pCpuStats->nLoadAvg[2] = XSYNC_ATOMIC_GET(&pStats->cpuStats.nLoadAvg[2]); + + if (pCpuStats->nCoreCount) return pCpuStats->nCoreCount; + XArray_Destroy(&pCpuStats->cores); + return -2; +} + +int XTop_GetNetworkStats(xtop_stats_t *pStats, xarray_t *pIfaces) +{ + XSync_Lock(&pStats->netLock); + + if (!pStats->netIfaces.nUsed || + !XArray_Init(pIfaces, 1, 0)) + { + XSync_Unlock(&pStats->netLock); + return 0; + } + + pIfaces->clearCb = XTop_ClearCb; + int i, nUsed = pStats->netIfaces.nUsed; + + for (i = 0; i < nUsed; i++) + { + xnet_iface_t *pSrcIface = (xnet_iface_t*)XArray_GetData(&pStats->netIfaces, i); + if (pSrcIface == NULL) continue; + + xnet_iface_t *pDstIface = (xnet_iface_t*)malloc(sizeof(xnet_iface_t)); + if (pDstIface == NULL) continue; + + memcpy(pDstIface, pSrcIface, sizeof(xnet_iface_t)); + if (XArray_AddData(pIfaces, pDstIface, 0) < 0) free(pDstIface); + } + + XSync_Unlock(&pStats->netLock); + return pIfaces->nUsed; +} + +static uint64_t XTop_ParseMemInfo(char *pBuffer, size_t nBuffSize, const char *pField) +{ + const char *pEnd = pBuffer + nBuffSize; + char *pOffset = strstr(pBuffer, pField); + if (pOffset == NULL) return 0; + pOffset += strlen(pField) + 1; + if (pOffset >= pEnd) return 0; + return atoll(pOffset); +} + +static void XTop_UpdateNetworkStats(xtop_stats_t *pStats) +{ + DIR *pDir = opendir(XSYS_CLASS_NET); + if (pDir == NULL) return; + + XSync_Lock(&pStats->netLock); + xarray_t *pIfaces = &pStats->netIfaces; + + struct dirent *pEntry = readdir(pDir); + while(pEntry != NULL) + { + /* Found an entry, but ignore . and .. */ + if (!strcmp(".", pEntry->d_name) || + !strcmp("..", pEntry->d_name)) + { + pEntry = readdir(pDir); + continue; + } + + char sBuffer[XPROC_BUFFER_SIZE]; + char sIfacePath[XPATH_MAX]; + xbool_t nHaveIface = XFALSE; + + xnet_iface_t netIface; + memset(&netIface, 0, sizeof(netIface)); + + xstrncpy(netIface.sName, sizeof(netIface.sName), pEntry->d_name); + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/address", XSYS_CLASS_NET, netIface.sName); + + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) + { + char *pSavePtr = NULL; + xstrncpy(netIface.sHWAddr, sizeof(netIface.sHWAddr), sBuffer); + strtok_r(netIface.sHWAddr, "\n", &pSavePtr); + if (netIface.sHWAddr[0] == '\n') xstrnul(netIface.sHWAddr); + } + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/type", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nType = atol(sBuffer); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/speed", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nBandwidth = atol(sBuffer); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/statistics/rx_bytes", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nBytesReceived = atol(sBuffer); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/statistics/tx_bytes", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nBytesSent = atol(sBuffer); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/statistics/rx_packets", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nPacketsReceived = atol(sBuffer); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s/statistics/tx_packets", XSYS_CLASS_NET, netIface.sName); + if (XPath_Read(sIfacePath, (uint8_t*)sBuffer, sizeof(sBuffer)) > 0) netIface.nPacketsSent = atol(sBuffer); + + if (netIface.nBandwidth < 0) netIface.nBandwidth = 0; + if (!xstrused(netIface.sHWAddr)) xstrncpy(netIface.sHWAddr, sizeof(netIface.sHWAddr), XNET_HWADDR_DEFAULT); + + xstrncpyf(sIfacePath, sizeof(sIfacePath), "%s/%s", XSYS_CLASS_NET, netIface.sName); + DIR *pIfaceDir = opendir(sIfacePath); + + if (pIfaceDir != NULL) + { + struct dirent *pIfaceEntry = readdir(pIfaceDir); + while(pIfaceEntry != NULL) + { + /* Found an entry, but ignore . and .. */ + if (!strcmp(".", pIfaceEntry->d_name) || + !strcmp("..", pIfaceEntry->d_name)) + { + pIfaceEntry = readdir(pIfaceDir); + continue; + } + + if (!strncmp(pIfaceEntry->d_name, "slave_", 6) || !strncmp(pIfaceEntry->d_name, "upper_", 6)) + xstrncpy(netIface.sMembers[netIface.nMemberCount++], XNAME_MAX, &pIfaceEntry->d_name[6]); + + pIfaceEntry = readdir(pIfaceDir); + } + + closedir(pIfaceDir); + } + + if (pIfaces->nUsed > 0) + { + unsigned int i, nIntervalSecs = pStats->nIntervalU / XTOP_INTERVAL_USEC; + for (i = 0; i < pIfaces->nUsed; i++) + { + xnet_iface_t *pIface = (xnet_iface_t*)XArray_GetData(pIfaces, i); + if (!strcmp(pIface->sName, netIface.sName)) + { + if (netIface.nBytesReceived > pIface->nBytesReceived && pIface->nBytesReceived > 0) + netIface.nBytesReceivedPerSec = (netIface.nBytesReceived - pIface->nBytesReceived) / nIntervalSecs; + + if (netIface.nPacketsReceived > pIface->nPacketsReceived && pIface->nPacketsReceived > 0) + netIface.nPacketsReceivedPerSec = (netIface.nPacketsReceived - pIface->nPacketsReceived) / nIntervalSecs; + + if (netIface.nBytesSent > pIface->nBytesSent && pIface->nBytesSent > 0) + netIface.nBytesSentPerSec = (netIface.nBytesSent - pIface->nBytesSent) / nIntervalSecs; + + if (netIface.nPacketsSent > pIface->nPacketsSent && pIface->nPacketsSent > 0) + netIface.nPacketsSentPerSec = (netIface.nPacketsSent - pIface->nPacketsSent) / nIntervalSecs; + + if (XAddr_GetIFCIP(netIface.sName, netIface.sIPAddr, sizeof(netIface.sIPAddr)) <= 0) + xstrncpy(netIface.sIPAddr, sizeof(netIface.sIPAddr), XNET_IPADDR_DEFAULT); + + memcpy(pIface, &netIface, sizeof(xnet_iface_t)); + nHaveIface = XTRUE; + } + } + } + + if (!nHaveIface) + { + xnet_iface_t *pNewIface = (xnet_iface_t*)malloc(sizeof(xnet_iface_t)); + if (pNewIface != NULL) + { + memcpy(pNewIface, &netIface, sizeof(xnet_iface_t)); + + if (XAddr_GetIFCIP(pNewIface->sName, pNewIface->sIPAddr, sizeof(pNewIface->sIPAddr)) <= 0) + xstrncpy(pNewIface->sIPAddr, sizeof(pNewIface->sIPAddr), XNET_IPADDR_DEFAULT); + + if (XArray_AddData(pIfaces, pNewIface, 0) < 0) free(pNewIface); + } + } + + pEntry = readdir(pDir); + } + + closedir(pDir); + XSync_Unlock(&pStats->netLock); +} + +static uint8_t XTop_UpdateMemoryInfo(xmem_info_t *pDstInfo, xpid_t nPID) +{ + char sBuffer[XPROC_BUFFER_SIZE]; + + if (XPath_Read(XPROC_FILE_MEMINFO, (uint8_t*)sBuffer, sizeof(sBuffer)) <= 0) return 0; + XSYNC_ATOMIC_SET(&pDstInfo->nMemoryTotal, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "MemTotal")); + XSYNC_ATOMIC_SET(&pDstInfo->nMemoryFree, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "MemFree")); + XSYNC_ATOMIC_SET(&pDstInfo->nMemoryShared, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "Shmem")); + XSYNC_ATOMIC_SET(&pDstInfo->nMemoryCached, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "Cached")); + XSYNC_ATOMIC_SET(&pDstInfo->nReclaimable, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "SReclaimable")); + XSYNC_ATOMIC_SET(&pDstInfo->nMemoryAvail, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "MemAvailable")); + XSYNC_ATOMIC_SET(&pDstInfo->nBuffers, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "Buffers")); + XSYNC_ATOMIC_SET(&pDstInfo->nSwapCached, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "SwapCached")); + XSYNC_ATOMIC_SET(&pDstInfo->nSwapTotal, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "SwapTotal")); + XSYNC_ATOMIC_SET(&pDstInfo->nSwapFree, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "SwapFree")); + + char sPath[XPATH_MAX]; + if (nPID <= 0) xstrncpy(sPath, sizeof(sPath), XPROC_FILE_PIDSTATUS); + else xstrncpyf(sPath, sizeof(sPath), "/proc/%d/status", nPID); + + if (XPath_Read(sPath, (uint8_t*)sBuffer, sizeof(sBuffer)) <= 0) return 0; + XSYNC_ATOMIC_SET(&pDstInfo->nResidentMemory, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "VmRSS")); + XSYNC_ATOMIC_SET(&pDstInfo->nVirtualMemory, XTop_ParseMemInfo(sBuffer, sizeof(sBuffer), "VmSize")); + + return 1; +} + +static uint8_t XTop_UpdateCPUStats(xcpu_stats_t *pCpuStats, xpid_t nPID) +{ + char sBuffer[XPROC_BUFFER_SIZE]; + if (XPath_Read(XPROC_FILE_STAT, (uint8_t*)sBuffer, sizeof(sBuffer)) <= 0) return 0; + + xproc_info_t lastCpuUsage; + XTop_CopyCPUUsage(&lastCpuUsage, &pCpuStats->usage); + + int nCoreCount = XSYNC_ATOMIC_GET(&pCpuStats->nCoreCount); + char *pSavePtr = NULL; + int nCPUID = -1; + + char *ptr = strtok_r(sBuffer, "\n", &pSavePtr); + while (ptr != NULL && !strncmp(ptr, "cpu", 3)) + { + xcpu_info_t cpuInfo; + memset(&cpuInfo, 0, sizeof(xcpu_info_t)); + + sscanf(ptr, "%*s %u %u %u %u %u %u %u %u %u %u", &cpuInfo.nUserSpaceRaw, + &cpuInfo.nUserSpaceNicedRaw, &cpuInfo.nKernelSpaceRaw, &cpuInfo.nIdleTimeRaw, + &cpuInfo.nIOWaitRaw, &cpuInfo.nHardInterruptsRaw, &cpuInfo.nSoftInterruptsRaw, + &cpuInfo.nStealRaw, &cpuInfo.nGuestRaw, &cpuInfo.nGuestNicedRaw); + + cpuInfo.nTotalRaw = cpuInfo.nHardInterruptsRaw + cpuInfo.nSoftInterruptsRaw; + cpuInfo.nTotalRaw += cpuInfo.nUserSpaceRaw + cpuInfo.nKernelSpaceRaw; + cpuInfo.nTotalRaw += cpuInfo.nUserSpaceNicedRaw + cpuInfo.nStealRaw; + cpuInfo.nTotalRaw += cpuInfo.nIdleTimeRaw + cpuInfo.nIOWaitRaw; + + cpuInfo.nID = nCPUID++; + if (!nCoreCount && cpuInfo.nID >= 0) + { + xcpu_info_t *pInfo = (xcpu_info_t*)malloc(sizeof(xcpu_info_t)); + if (pInfo != NULL) + { + memcpy(pInfo, &cpuInfo, sizeof(xcpu_info_t)); + int nStatus = XArray_AddData(&pCpuStats->cores, pInfo, 0); + if (nStatus < 0) free(pInfo); + } + } + else + { + xcpu_info_t lastCpuInfo, *pGenCpuInfo; + if (cpuInfo.nID < 0) pGenCpuInfo = &pCpuStats->sum; + else pGenCpuInfo = (xcpu_info_t*)XArray_GetData(&pCpuStats->cores, cpuInfo.nID); + + XTop_CopyCPUInfo(&lastCpuInfo, pGenCpuInfo); + uint32_t nTotalDiff = cpuInfo.nTotalRaw - lastCpuInfo.nTotalRaw; + + float fHardInterrupts = ((cpuInfo.nHardInterruptsRaw - lastCpuInfo.nHardInterruptsRaw) / (float)nTotalDiff) * 100; + float fSoftInterrupts = ((cpuInfo.nSoftInterruptsRaw - lastCpuInfo.nSoftInterruptsRaw) / (float)nTotalDiff) * 100; + float fKernelSpace = ((cpuInfo.nKernelSpaceRaw - lastCpuInfo.nKernelSpaceRaw) / (float)nTotalDiff) * 100; + float fUserSpace = ((cpuInfo.nUserSpaceRaw - lastCpuInfo.nUserSpaceRaw) / (float)nTotalDiff) * 100; + float fUserNiced = ((cpuInfo.nUserSpaceNicedRaw - lastCpuInfo.nUserSpaceNicedRaw) / (float)nTotalDiff) * 100; + float fIdleTime = ((cpuInfo.nIdleTimeRaw - lastCpuInfo.nIdleTimeRaw) / (float)nTotalDiff) * 100; + float fIOWait = ((cpuInfo.nIOWaitRaw - lastCpuInfo.nIOWaitRaw) / (float)nTotalDiff) * 100; + float fSteal = ((cpuInfo.nStealRaw - lastCpuInfo.nStealRaw) / (float)nTotalDiff) * 100; + float fGuest = ((cpuInfo.nGuestRaw - lastCpuInfo.nGuestRaw) / (float)nTotalDiff) * 100; + float fGuestNi = ((cpuInfo.nGuestNiced - lastCpuInfo.nGuestNiced) / (float)nTotalDiff) * 100; + + XSYNC_ATOMIC_SET(&pGenCpuInfo->nHardInterrupts, XFloatToU32(fHardInterrupts)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nSoftInterrupts, XFloatToU32(fSoftInterrupts)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nKernelSpace, XFloatToU32(fKernelSpace)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nUserSpace, XFloatToU32(fUserSpace)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nUserSpaceNiced, XFloatToU32(fUserNiced)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nIdleTime, XFloatToU32(fIdleTime)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nIOWait, XFloatToU32(fIOWait)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nStealTime, XFloatToU32(fSteal)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nGuestTime, XFloatToU32(fGuest)); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nGuestNiced, XFloatToU32(fGuestNi)); + + /* Save raw information about CPU usage for later percentage calculations */ + XSYNC_ATOMIC_SET(&pGenCpuInfo->nHardInterruptsRaw, cpuInfo.nHardInterruptsRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nSoftInterruptsRaw, cpuInfo.nSoftInterruptsRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nKernelSpaceRaw, cpuInfo.nKernelSpaceRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nUserSpaceRaw, cpuInfo.nUserSpaceRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nUserSpaceNicedRaw, cpuInfo.nUserSpaceNicedRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nIdleTimeRaw, cpuInfo.nIdleTimeRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nIOWaitRaw, cpuInfo.nIOWaitRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nStealRaw, cpuInfo.nStealRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nGuestRaw, cpuInfo.nGuestRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nGuestNicedRaw, cpuInfo.nGuestNicedRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nTotalRaw, cpuInfo.nTotalRaw); + XSYNC_ATOMIC_SET(&pGenCpuInfo->nID, cpuInfo.nID); + } + + ptr = strtok_r(NULL, "\n", &pSavePtr); + } + + XSYNC_ATOMIC_SET(&pCpuStats->nCoreCount, pCpuStats->cores.nUsed); + char sPath[XPATH_MAX]; + + if (nPID <= 0) xstrncpy(sPath, sizeof(sPath), XPROC_FILE_PIDSTAT); + else xstrncpyf(sPath, sizeof(sPath), "/proc/%d/stat", nPID); + if (XPath_Read(sPath, (uint8_t*)sBuffer, sizeof(sBuffer)) <= 0) return 0; + + xproc_info_t currCpuUsage; + sscanf(sBuffer, "%*u %*s %*c %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %lu %lu %ld %ld", + &currCpuUsage.nUserSpace, &currCpuUsage.nKernelSpace, + &currCpuUsage.nUserSpaceChilds, &currCpuUsage.nKernelSpaceChilds); + + currCpuUsage.nTotalTime = XSYNC_ATOMIC_GET(&pCpuStats->sum.nTotalRaw); + uint64_t nTotalDiff = currCpuUsage.nTotalTime - lastCpuUsage.nTotalTime; + + float nUserCPU = 100 * (((currCpuUsage.nUserSpace + currCpuUsage.nUserSpaceChilds) - + (lastCpuUsage.nUserSpace + lastCpuUsage.nUserSpaceChilds)) / (float)nTotalDiff); + + float nSystemCPU = 100 * (((currCpuUsage.nKernelSpace + currCpuUsage.nKernelSpaceChilds) - + (lastCpuUsage.nKernelSpace + lastCpuUsage.nKernelSpaceChilds)) / (float)nTotalDiff); + + XSYNC_ATOMIC_SET(&pCpuStats->usage.nUserSpaceChilds, currCpuUsage.nUserSpaceChilds); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nKernelSpaceChilds, currCpuUsage.nUserSpaceChilds); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nUserSpace, currCpuUsage.nUserSpace); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nKernelSpace, currCpuUsage.nKernelSpace); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nTotalTime, currCpuUsage.nTotalTime); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nUserSpaceUsage, XFloatToU32(nUserCPU)); + XSYNC_ATOMIC_SET(&pCpuStats->usage.nKernelSpaceUsage, XFloatToU32(nSystemCPU)); + + if (XPath_Read(XPROC_FILE_LOADAVG, (uint8_t*)sBuffer, sizeof(sBuffer)) <= 0) return 0; + float fOneMinInterval, fFiveMinInterval, fTenMinInterval; + + sscanf(sBuffer, "%f %f %f", &fOneMinInterval, &fFiveMinInterval, &fTenMinInterval); + XSYNC_ATOMIC_SET(&pCpuStats->nLoadAvg[0], XFloatToU32(fOneMinInterval)); + XSYNC_ATOMIC_SET(&pCpuStats->nLoadAvg[1], XFloatToU32(fFiveMinInterval)); + XSYNC_ATOMIC_SET(&pCpuStats->nLoadAvg[2], XFloatToU32(fTenMinInterval)); + + return 1; +} + +int XTop_UpdateStats(void* pData) +{ + xtop_stats_t *pStats = (xtop_stats_t*)pData; + XTop_UpdateCPUStats(&pStats->cpuStats, pStats->nPID); + XTop_UpdateMemoryInfo(&pStats->memInfo, pStats->nPID); + XTop_UpdateNetworkStats(pStats); + XSYNC_ATOMIC_SET(&pStats->nLoadDone, XTRUE); + return 0; +} + +int XTop_InitCPUStats(xcpu_stats_t *pStats) +{ + if (XArray_Init(&pStats->cores, 1, 0) == NULL) return 0; + pStats->cores.clearCb = XTop_ClearCb; + + memset(&pStats->usage, 0, sizeof(xproc_info_t)); + memset(&pStats->sum, 0, sizeof(xcpu_info_t)); + + pStats->nLoadAvg[0] = pStats->nLoadAvg[1] = 0; + pStats->nLoadAvg[2] = pStats->nCoreCount = 0; + + return 1; +} + +int XTop_InitStats(xtop_stats_t *pStats) +{ + if (XArray_Init(&pStats->netIfaces, 1, 0) == NULL) return XSTDERR; + pStats->netIfaces.clearCb = XTop_ClearCb; + + if (!XTop_InitCPUStats(&pStats->cpuStats)) + { + XArray_Destroy(&pStats->netIfaces); + return XSTDERR; + } + + memset(&pStats->memInfo, 0, sizeof(xmem_info_t)); + pStats->monitoring.nStatus = 0; + pStats->nIntervalU = 0; + pStats->nPID = 0; + + XSYNC_ATOMIC_SET(&pStats->nLoadDone, XFALSE); + XSync_Init(&pStats->netLock); + return XSTDOK; +} + +void XTop_DestroyStats(xtop_stats_t *pStats) +{ + XArray_Destroy(&pStats->cpuStats.cores); + XArray_Destroy(&pStats->netIfaces); + XSync_Destroy(&pStats->netLock); +} + +int XTop_StartMonitoring(xtop_stats_t *pStats, uint32_t nIntervalU, xpid_t nPID) +{ + if (nPID > 0) + { + char sPath[XPATH_MAX]; + xstrncpyf(sPath, sizeof(sPath), "/proc/%d", nPID); + if (!XPath_Exists(sPath)) return XSTDERR; + } + + pStats->nIntervalU = nIntervalU; + pStats->nPID = nPID; + + XTask_Start(&pStats->monitoring, XTop_UpdateStats, pStats, nIntervalU); + return XSYNC_ATOMIC_GET(&pStats->monitoring.nStatus);; +} + +uint32_t XTop_WaitLoad(xtop_stats_t *pStats, uint32_t nWaitUsecs) +{ + uint32_t nCheckCount = 0; + + while (XSYNC_ATOMIC_GET(&pStats->nLoadDone) != XTRUE) + { + if (nWaitUsecs < 0) break; + else if (!nWaitUsecs) continue; + + xusleep((uint32_t)nWaitUsecs); + nCheckCount++; + } + return nCheckCount * nWaitUsecs; +} + +uint32_t XTop_StopMonitoring(xtop_stats_t *pStats, uint32_t nWaitUsecs) +{ + xtask_t *pMonTask = &pStats->monitoring; + return XTask_Stop(pMonTask, nWaitUsecs); +} +#endif /* #ifndef _WIN32 */ diff --git a/src/xtop.h b/src/xtop.h new file mode 100644 index 0000000..e4ef62d --- /dev/null +++ b/src/xtop.h @@ -0,0 +1,150 @@ +/*! + * @file libxutils/src/xtop.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get process and system statistics about + * cpu usage, network usage, memory usage, etc... + */ + +#ifndef __XUTILS_XPROC_H__ +#define __XUTILS_XPROC_H__ + +#include "xstd.h" +#include "thread.h" +#include "array.h" +#include "sync.h" + +#define XNET_HWADDR_DEFAULT "00:00:00:00:00:00" +#define XNET_IPADDR_DEFAULT "0.0.0.0" + +#define XSYS_CLASS_NET "/sys/class/net" +#define XPROC_FILE_PIDSTATUS "/proc/self/status" +#define XPROC_FILE_PIDSTAT "/proc/self/stat" +#define XPROC_FILE_LOADAVG "/proc/loadavg" +#define XPROC_FILE_MEMINFO "/proc/meminfo" +#define XPROC_FILE_UPTIME "/proc/uptime" +#define XPROC_FILE_STAT "/proc/stat" + +#define XTOP_INTERVAL_USEC 1000000 +#define XMEMBERS_MAX 128 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct XNetIface { + uint64_t nPacketsReceivedPerSec; + uint64_t nBytesReceivedPerSec; + uint64_t nPacketsSentPerSec; + uint64_t nBytesSentPerSec; + uint16_t nMemberCount; + + int64_t nPacketsReceived; + int64_t nBytesReceived; + int64_t nPacketsSent; + int64_t nBytesSent; + int64_t nBandwidth; + int32_t nType; + + char sName[XNAME_MAX]; + char sHWAddr[XADDR_MAX]; + char sIPAddr[XADDR_MAX]; + char sMembers[XMEMBERS_MAX][XNAME_MAX]; +} xnet_iface_t; + +typedef struct XMemInfo { + uint64_t nResidentMemory; + uint64_t nVirtualMemory; + uint64_t nMemoryCached; + uint64_t nMemoryShared; + uint64_t nMemoryAvail; + uint64_t nMemoryTotal; + uint64_t nMemoryFree; + uint64_t nReclaimable; + uint64_t nSwapCached; + uint64_t nSwapTotal; + uint64_t nSwapFree; + uint64_t nBuffers; +} xmem_info_t; + +typedef struct XCPUInfo { + int nID; + + /* Calculated percents about CPU usage */ + uint32_t nSoftInterrupts; + uint32_t nHardInterrupts; + uint32_t nUserSpaceNiced; + uint32_t nKernelSpace; + uint32_t nUserSpace; + uint32_t nIdleTime; + uint32_t nIOWait; + uint32_t nStealTime; + uint32_t nGuestTime; + uint32_t nGuestNiced; + + /* Raw information for later percentage calculations */ + uint32_t nSoftInterruptsRaw; + uint32_t nHardInterruptsRaw; + uint32_t nUserSpaceNicedRaw; + uint32_t nKernelSpaceRaw; + uint32_t nUserSpaceRaw; + uint32_t nIdleTimeRaw; + uint32_t nIOWaitRaw; + uint32_t nStealRaw; + uint32_t nGuestRaw; + uint32_t nGuestNicedRaw; + uint64_t nTotalRaw; +} xcpu_info_t; + +typedef struct XProcInfo { + /* Calculated percents about CPU usage by this process */ + uint32_t nKernelSpaceUsage; + uint32_t nUserSpaceUsage; + + /* Raw information for later percentage calculations */ + int64_t nKernelSpaceChilds; // Amount of time that this process's waited-for children have been scheduled in kernel mode + int64_t nUserSpaceChilds; // Amount of time that this process's waited-for children have been scheduled in user mode + uint64_t nKernelSpace; // Amount of time that this process has been scheduled in kernel mode + uint64_t nUserSpace; // Amount of time that this process has been scheduled in user mode + uint64_t nTotalTime; // Total amout of time that this process has been sheduled in any mode +} xproc_info_t; + +typedef struct XCPUStats { + uint32_t nLoadAvg[3]; + uint16_t nCoreCount; + xproc_info_t usage; + xcpu_info_t sum; + xarray_t cores; +} xcpu_stats_t; + +typedef struct XTopStats { + uint32_t nIntervalU; + xsync_mutex_t netLock; + xcpu_stats_t cpuStats; + xmem_info_t memInfo; + xatomic_t nLoadDone; + xarray_t netIfaces; + xtask_t monitoring; + xpid_t nPID; +} xtop_stats_t; + +#ifndef _WIN32 +int XTop_InitStats(xtop_stats_t *pStats); +void XTop_DestroyStats(xtop_stats_t *pStats); + +int XTop_StartMonitoring(xtop_stats_t *pStats, uint32_t nIntervalU, xpid_t nPID); +uint32_t XTop_StopMonitoring(xtop_stats_t *pStats, uint32_t nWaitUsecs); +uint32_t XTop_WaitLoad(xtop_stats_t *pStats, uint32_t nWaitUsecs); + +int XTop_GetNetworkStats(xtop_stats_t *pStats, xarray_t *pIfaces); +int XTop_GetMemoryInfo(xtop_stats_t *pStats, xmem_info_t *pInfo); +int XTop_GetCPUStats(xtop_stats_t *pStats, xcpu_stats_t *pCpuStats); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XPROC_H__ */ diff --git a/src/xtype.c b/src/xtype.c new file mode 100644 index 0000000..51539c1 --- /dev/null +++ b/src/xtype.c @@ -0,0 +1,108 @@ +/*! + * @file libxutils/src/xtype.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of various + * types and converting operations. + */ + +#include "xstd.h" +#include "xstr.h" +#include "xtype.h" + +uint32_t XFloatToU32(float fValue) +{ + uint16_t nIntegral = (uint16_t)floor(fValue); + float fBalance = fValue - (float)nIntegral; + uint16_t nDecimal = (uint16_t)(fBalance * 100); + + uint32_t nRetVal; + nRetVal = (uint32_t)nIntegral; + nRetVal <<= 16; + nRetVal += (uint32_t)nDecimal; + return nRetVal; +} + +float XU32ToFloat(uint32_t nValue) +{ + uint16_t nIntegral = (uint16_t)(nValue >> 16); + uint16_t nDecimal = (uint16_t)(nValue & 0xFF); + float fBalance = (float)nDecimal / (float)100; + return (float)((float)nIntegral + fBalance); +} + +int XTypeIsAlphabet(char nChar) +{ + return ((nChar >= 'a' && nChar <= 'z') || + (nChar >= 'A' && nChar <= 'Z')) ? 1 : 0; +} + +xbool_t XTypeIsPrint(const uint8_t *pData, size_t nSize) +{ + xbool_t bPrintable = XTRUE; + size_t i; + + for (i = 0; i < nSize; i++) + { + if (pData[i] == XSTR_NUL) break; + + if (!isascii(pData[i]) && + !isprint(pData[i])) + { + bPrintable = XFALSE; + break; + } + } + + return bPrintable; +} + +size_t XBytesToUnit(char *pDst, size_t nSize, size_t nBytes, xbool_t bShort) +{ + if (bShort) + { + if (nBytes > 1073741824) + return xstrncpyf(pDst, nSize, "%.1fG", (double)nBytes / (double)1073741824); + else if (nBytes > 1048576) + return xstrncpyf(pDst, nSize, "%.1fM", (double)nBytes / (double)1048576); + else if (nBytes > 1024) + return xstrncpyf(pDst, nSize, "%.1fK", (double)nBytes / (double)1024); + + return xstrncpyf(pDst, nSize, "%zuB", nBytes); + } + + if (nBytes > 1073741824) + return xstrncpyf(pDst, nSize, "%.2f GB", (double)nBytes / (double)1073741824); + else if (nBytes > 1048576) + return xstrncpyf(pDst, nSize, "%.2f MB", (double)nBytes / (double)1048576); + else if (nBytes > 1024) + return xstrncpyf(pDst, nSize, "%.2f KB", (double)nBytes / (double)1024); + + return xstrncpyf(pDst, nSize, "%zu B", nBytes); +} + +size_t XKBToUnit(char *pDst, size_t nSize, size_t nKB, xbool_t bShort) +{ + if (bShort) + { + if (nKB > 1073741824) + return xstrncpyf(pDst, nSize, "%.1fT", (double)nKB / (double)1073741824); + else if (nKB > 1048576) + return xstrncpyf(pDst, nSize, "%.1fG", (double)nKB / (double)1048576); + else if (nKB > 1024) + return xstrncpyf(pDst, nSize, "%.1fM", (double)nKB / (double)1024); + + return xstrncpyf(pDst, nSize, "%zuK", nKB); + } + + if (nKB > 1073741824) + return xstrncpyf(pDst, nSize, "%.2f TB", (double)nKB / (double)1073741824); + else if (nKB > 1048576) + return xstrncpyf(pDst, nSize, "%.2f GB", (double)nKB / (double)1048576); + else if (nKB > 1024) + return xstrncpyf(pDst, nSize, "%.2f MB", (double)nKB / (double)1024); + + return xstrncpyf(pDst, nSize, "%zu KB", nKB); +} diff --git a/src/xtype.h b/src/xtype.h new file mode 100644 index 0000000..8ec5597 --- /dev/null +++ b/src/xtype.h @@ -0,0 +1,55 @@ +/*! + * @file libxutils/src/xtype.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Implementation of various + * types and converting operations. + */ + +#ifndef __XUTILS_XTYPE_H__ +#define __XUTILS_XTYPE_H__ + +#include "xstd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +typedef int xsocklen_t; +typedef long xatomic_t; +typedef int xmode_t; +typedef int xpid_t; +#else +typedef socklen_t xsocklen_t; +typedef uint32_t xatomic_t; +typedef mode_t xmode_t; +typedef pid_t xpid_t; +#endif + +typedef uint8_t xbool_t; +#define XTRUE 1 +#define XFALSE 0 + +#define XATOMIC volatile xatomic_t + +#define XCHAR(var,size) char var[size] = {'\0'} +#define XARG_SIZE(val) val, sizeof(val) + +uint32_t XFloatToU32(float fValue); +float XU32ToFloat(uint32_t nValue); + +size_t XBytesToUnit(char *pDst, size_t nSize, size_t nBytes, xbool_t bShort); +size_t XKBToUnit(char *pDst, size_t nSize, size_t nKB, xbool_t bShort); + +int XTypeIsAlphabet(char nChar); +xbool_t XTypeIsPrint(const uint8_t *pData, size_t nSize); + +#ifdef __cplusplus +} +#endif + + +#endif /* __XUTILS_XTYPE_H__ */ \ No newline at end of file diff --git a/src/xver.c b/src/xver.c new file mode 100644 index 0000000..ad5458c --- /dev/null +++ b/src/xver.c @@ -0,0 +1,45 @@ +/*! + * @file libxutils/src/xver.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get additional information about library + */ + +#include "xstd.h" +#include "xstr.h" +#include "sync.h" +#include "xver.h" +#include "xtype.h" + +static XATOMIC g_nHaveVerShort = 0; +static XATOMIC g_nHaveVerLong = 0; +static char g_versionShort[128]; +static char g_versionLong[256]; + +const char* XUtils_Version(void) +{ + if (!XSYNC_ATOMIC_GET(&g_nHaveVerLong)) + { + xstrncpyf(g_versionLong, sizeof(g_versionLong), "%d.%d build %d (%s)", + XUTILS_VERSION_MAX, XUTILS_VERSION_MIN, XUTILS_BUILD_NUMBER, __DATE__); + + XSYNC_ATOMIC_SET(&g_nHaveVerLong, 1); + } + + return g_versionLong; +} + +const char* XUtils_VersionShort(void) +{ + if (!XSYNC_ATOMIC_GET(&g_nHaveVerShort)) + { + xstrncpyf(g_versionShort, sizeof(g_versionShort), "%d.%d.%d", + XUTILS_VERSION_MAX, XUTILS_VERSION_MIN, XUTILS_BUILD_NUMBER); + + XSYNC_ATOMIC_SET(&g_nHaveVerShort, 1); + } + + return g_versionShort; +} diff --git a/src/xver.h b/src/xver.h new file mode 100644 index 0000000..0e88b05 --- /dev/null +++ b/src/xver.h @@ -0,0 +1,28 @@ +/*! + * @file libxutils/src/xver.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * @brief Get additional information about library + */ + +#ifndef __XUTILS_XLIBVER_H__ +#define __XUTILS_XLIBVER_H__ + +#define XUTILS_VERSION_MAX 2 +#define XUTILS_VERSION_MIN 4 +#define XUTILS_BUILD_NUMBER 3 + +#ifdef __cplusplus +extern "C" { +#endif + +const char* XUtils_Version(void); +const char* XUtils_VersionShort(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XLIBVER_H__ */