Page:
n__str_8c
Pages
Examples
Files
Home
Module AABB3D
Module ACCEPT_POOL
Module ALLEGRO4
Module ALLEGRO5
Module ANIM_LIB
Module ASTAR
Module ATLAS_LIB
Module AVRO
Module CLOCK_SYNC
Module COMMONS
Module CONFIGFILE
Module CYPHER_BASE64
Module CYPHER_VIGENERE
Module DEAD_RECKONING
Module ENUMS
Module EXCEPTIONS
Module GAME_ENV
Module GIT
Module GUI
Module HASH_TABLE
Module ISOMETRIC_ENGINE
Module LIST
Module LOG
Module LOGNODUP
Module LZ4
Module NETWORKING
Module NETWORK_MSG
Module N_COOKIES
Module N_DIFF
Module N_DIGEST
Module N_DISK_CACHE
Module N_DNS
Module N_ECC
Module N_ENTROPY
Module N_FILES
Module N_FLUIDS
Module N_GUI_COLOR
Module N_GUI_COLORWHEEL
Module N_GUI_CURVE
Module N_GUI_GRID
Module N_GUI_POPUP
Module N_HEX
Module N_HTTP3
Module N_HTTP_COOKIE
Module N_HTTP_FORM
Module N_HTTP_MIME
Module N_HTTP_REQ
Module N_HTTP_RESP
Module N_HTTP_ROUTER
Module N_HTTP_SERVER
Module N_ICC
Module N_JSON
Module N_KAFKA
Module N_LINALG
Module N_MODULE
Module N_OPENSSL_COMPAT
Module N_PRETTY
Module N_PTH
Module N_QUERY
Module N_RANDOM
Module N_SHA256
Module N_STR
Module N_TIFF
Module N_TIME
Module N_USER
Module N_X509
Module N_ZIP
Module PARTICLES
Module PCRE
Module PHYSICS
Module SIGNALS
Module STACK
Module THREADS
Module TRAJECTORY
Module TREE
Module ZLIB
Modules
Pages
Struct A4_BMP
Struct A4_TIMER
Struct AABB3D
Struct ANIM_DATA
Struct ANIM_FRAME
Struct ANIM_GFX
Struct ANIM_LIB
Struct ASTAR_BOX
Struct ASTAR_CELL
Struct ASTAR_GRID
Struct ASTAR_HEAP
Struct ASTAR_HEAP_NODE
Struct ASTAR_NODE
Struct ASTAR_PATH
Struct AVRO_FIELD
Struct AVRO_READER
Struct AVRO_SCHEMA
Struct BITMAP
Struct CALLBACK_DATA
Struct CA_CTX
Struct CLIENT_JOB
Struct CLIENT_STATE
Struct CONFIG_FILE
Struct CONFIG_FILE_SECTION
Struct CYPHER_PARAM
Struct DATA
Struct DEMO_PANEL
Struct DICTIONARY_DEFINITION
Struct DICTIONARY_ENTRY
Struct DIFF_OUT
Struct DIFF_TEXT
Struct DRAW_STATE
Struct DR_ENTITY
Struct DR_STATE
Struct DR_VEC3
Struct ExceptionContextList
Struct FONT
Struct GAME_ENV
Struct H2BUF
Struct H2_DYN_ENTRY
Struct H2_STREAM
Struct HASH_NODE
Struct HASH_TABLE
Struct ISO_DRAW_ENTRY
Struct ISO_MAP
Struct ISO_PROJECTION
Struct ISO_TILE_SEGMENT
Struct ISO_TILE_SEGMENTS
Struct ISO_VISIBLE_EDGES
Struct JS_STATE
Struct LEX_ITEM
Struct LIST
Struct LIST_NODE
Struct LOG_LEVELS
Struct LexMenu
Struct MEMBER
Struct MIDI
Struct NESTED
Struct NETWORK
Struct NETWORK_HTTP_INFO
Struct NETWORK_POOL
Struct NETWORK_SSL_THREAD_PARAMS
Struct NETW_ACCEPT_POOL
Struct NETW_ACCEPT_POOL_STATS
Struct NETW_MSG
Struct NETW_SNI_ARG
Struct NODE_DATA
Struct N_ATLAS
Struct N_ATLAS_PAGE
Struct N_CLOCK_SYNC
Struct N_COOKIE
Struct N_COOKIE_JAR
Struct N_DIFF_LINE
Struct N_DIFF_RESULT
Struct N_DISK_CACHE
Struct N_ENTROPY_BIT
Struct N_ENTROPY_POS
Struct N_FILE_INFO
Struct N_FLUID
Struct N_FLUID_THREAD_PARAMS
Struct N_FORM_FIELD
Struct N_GIT_COMMIT_INFO
Struct N_GIT_REMOTE_AUTH
Struct N_GIT_REPO
Struct N_GIT_STATUS_ENTRY
Struct N_GUI_BUTTON_DATA
Struct N_GUI_CHECKBOX_DATA
Struct N_GUI_COLOR_PICKER
Struct N_GUI_COMBOBOX_DATA
Struct N_GUI_CTX
Struct N_GUI_CUSTOM_DATA
Struct N_GUI_DATAGRID_COL
Struct N_GUI_DATAGRID_DATA
Struct N_GUI_DROPMENU_DATA
Struct N_GUI_DROPMENU_ENTRY
Struct N_GUI_GRID_CALLBACKS
Struct N_GUI_HEXVIEW_DATA
Struct N_GUI_IMAGE_DATA
Struct N_GUI_KVTABLE
Struct N_GUI_KV_CONTENT
Struct N_GUI_KV_ROW
Struct N_GUI_LABEL_DATA
Struct N_GUI_LISTBOX_DATA
Struct N_GUI_LISTITEM
Struct N_GUI_MARKDOWNVIEW_DATA
Struct N_GUI_MD_FONTS
Struct N_GUI_MD_STYLE
Struct N_GUI_MEAS_ENTRY
Struct N_GUI_MOUSE_EVENT
Struct N_GUI_PENDING_GEOM
Struct N_GUI_PENDING_WIDGET
Struct N_GUI_POPUP
Struct N_GUI_PROGRESSBAR_DATA
Struct N_GUI_RADIOLIST_DATA
Struct N_GUI_SB_TRACK
Struct N_GUI_SCROLLBAR_DATA
Struct N_GUI_SECTION
Struct N_GUI_SECTIONLIST
Struct N_GUI_SLIDER_DATA
Struct N_GUI_SPLITPANE_DATA
Struct N_GUI_STYLE
Struct N_GUI_SUGGEST_SINK
Struct N_GUI_SYNTAXVIEW_DATA
Struct N_GUI_TAB_PANEL
Struct N_GUI_TB_BUTTONS
Struct N_GUI_TEXTAREA_DATA
Struct N_GUI_TEXT_DIMS
Struct N_GUI_THEME
Struct N_GUI_TREE
Struct N_GUI_TREE_NODE
Struct N_GUI_TREE_STATE
Struct N_GUI_WIDGET
Struct N_GUI_WINDOW
Struct N_H2_FRAME
Struct N_H2_HEADER
Struct N_H2_HPACK
Struct N_H2_REQUEST
Struct N_H2_SERVER
Struct N_H2_SETTING
Struct N_HTML_ATTR_
Struct N_HTML_FORM
Struct N_HTML_TAG_
Struct N_HTTP3_RESPONSE
Struct N_HTTP_FIELD
Struct N_HTTP_FORM
Struct N_HTTP_FORM_MULTIPART
Struct N_HTTP_FORM_PART
Struct N_HTTP_MULTIPART_PART
Struct N_HTTP_REQUEST
Struct N_HTTP_RESP
Struct N_HTTP_RESPONSE
Struct N_HTTP_SERVER
Struct N_HTTP_SERVER_CONFIG
Struct N_HTTP_SERVER_REQUEST
Struct N_HTTP_SERVER_STATS
Struct N_HTTP_SET_COOKIE
Struct N_HTTP_STREAM
Struct N_ISO_CAMERA
Struct N_ISO_OBJECT
Struct N_KAFKA
Struct N_KAFKA_EVENT
Struct N_MOCK_SERVER
Struct N_MODULE
Struct N_PARALLEL_SLICE
Struct N_PARALLEL_TEAM
Struct N_PARALLEL_WORKER
Struct N_PCRE
Struct N_PROXY_CFG
Struct N_PTH
Struct N_PTH_TENSOR
Struct N_QUERY
Struct N_ROUTE
Struct N_ROUTER
Struct N_ROUTE_MATCH
Struct N_SHA256
Struct N_SOCKET
Struct N_SPAWN_PIPE
Struct N_SSE_CONN
Struct N_SSE_EVENT
Struct N_STR
Struct N_TIFF
Struct N_TIFF_ENTRY
Struct N_TIME
Struct N_URL
Struct N_URL_PARAM
Struct N_USER
Struct N_USERLIST
Struct N_WS_CONN
Struct N_WS_FRAME
Struct N_WS_MESSAGE
Struct N_ZIP_MEMBER
Struct OCTREE
Struct OCTREE_NODE
Struct PARSER
Struct PARTICLE
Struct PARTICLE_EMITTER
Struct PARTICLE_SYSTEM
Struct PEER
Struct PHYSICS
Struct PK_OBJ
Struct PK_VM
Struct POINT2D
Struct POINT3D
Struct PRETTY_OUT
Struct QNODE
Struct QUADTREE
Struct QUADTREE_NODE
Struct REC
Struct RGB
Struct SAMPLE
Struct SEEN
Struct SHARER
Struct SRV
Struct SRV_ARG
Struct STACK
Struct STACK_ITEM
Struct SYNTAX_RUN
Struct THREAD_POOL
Struct THREAD_POOL_NODE
Struct THREAD_WAITING_PROC
Struct TRAJECTORY
Struct TRAJECTORY_POINT
Struct TREE
Struct TREE_NODE
Struct TS_LOG
Struct WAYPOINT3D
Struct WORKER_ARG
Struct ZIP_SINK
Struct _N_DC_ENTRY
Struct _N_DC_GROUP_ENTRY
Struct _N_GUI_COLORWHEEL
Struct _N_GUI_COLORWHEEL_BLOCK
Struct _N_GUI_COLORWHEEL_MOUSE
Struct _N_GUI_CURVE
Struct _N_GUI_CURVE_BLOCK
Struct _N_GUI_CURVE_MOUSE
Struct _N_GUI_GRID
Struct _N_GUI_POPUP_ENTRY
Struct _N_GUI_POPUP_LEVEL
Struct _N_GUI_SB_SKIN
Struct _N_GUI_TA_STEP
Struct _N_GUI_THUMB
Struct _N_GUI_WRAP_ACC
Struct _compiled
Struct _conn
Struct _ip_slot
Struct _mp_param
Struct _n_civil
Struct _seg
Struct client
Struct collector
Struct date_vector
Struct md_atom
Struct md_cells
Struct md_hit
Struct md_line_ctx
Struct mline
Struct n_http_req_header
Struct n_http_req_limits
Struct n_http_request
Struct n_markdown_block
Struct n_markdown_doc
Struct n_markdown_inline
Struct n_markdown_span
Struct n_reactor_stats
Struct n_url_safe
Struct n_url_safe_head
Struct n_wrap_item
Struct producer_state
Struct rects
Struct reply
Struct runner
Struct sink
Struct tbuf
Struct wrap_hit
Struct wrap_span
Structures
Union COORD_VALUE
Union HASH_DATA
Union NODE_DATA_TYPES
Union STACK_DATA
dir_310dcdd4703e877a6b99d5dc3fc54d27
dir_68267d1309a1af8e8297ef4c3efbcdba
dir_d28a4824dc47e487b107a5db32ef43c4
dir_d44c64559bbebec7f509842c48db8b23
ex__accept__pool__client_8c
ex__accept__pool__server_8c
ex__allegro__4_8c
ex__atlas_8c
ex__avro_8c
ex__base64_8c
ex__bind__exclusive_8c
ex__clipboard_8c
ex__clock__sync_8c
ex__common_8c
ex__configfile_8c
ex__cookies_8c
ex__crypto_8c
ex__diff_8c
ex__digest_8c
ex__disk__cache_8c
ex__dns_8c
ex__ecc_8c
ex__entropy_8c
ex__exceptions_8c
ex__file_8c
ex__file__copy_8c
ex__fluid_8c
ex__fluid__config_8c
ex__fluid__config_8h
ex__git_8c
ex__gui_8c
ex__gui__bench_8c
ex__gui__button__keys_8c
ex__gui__checkbox__label_8c
ex__gui__checkbox__release_8c
ex__gui__color__picker_8c
ex__gui__colorwheel_8c
ex__gui__combobox__items_8c
ex__gui__combobox__wheel_8c
ex__gui__cursor_8c
ex__gui__curve_8c
ex__gui__custom_8c
ex__gui__custom__mouse_8c
ex__gui__datagrid_8c
ex__gui__datagrid__keys_8c
ex__gui__datagrid__sort_8c
ex__gui__detach_8c
ex__gui__dictionary_8c
ex__gui__display__fallback_8c
ex__gui__double__click_8c
ex__gui__dropmenu_8c
ex__gui__frameless__drag_8c
ex__gui__global__scroll_8c
ex__gui__glyph_8c
ex__gui__grid_8c
ex__gui__hexview_8c
ex__gui__hover_8c
ex__gui__isometric_8c
ex__gui__kvtable_8c
ex__gui__label__fits_8c
ex__gui__label__lines_8c
ex__gui__layout__persist_8c
ex__gui__listbox_8c
ex__gui__md_8c
ex__gui__multiwin_8c
ex__gui__network_8c
ex__gui__particles_8c
ex__gui__placeholder_8c
ex__gui__popup_8c
ex__gui__progressbar_8c
ex__gui__reentrant_8c
ex__gui__round_8c
ex__gui__sectionlist_8c
ex__gui__slider__default_8c
ex__gui__slider__handle_8c
ex__gui__slider__step_8c
ex__gui__splitpane_8c
ex__gui__suggest_8c
ex__gui__syntaxview_8c
ex__gui__text__shorten_8c
ex__gui__textarea__selection_8c
ex__gui__tooltip_8c
ex__gui__tree__lazy_8c
ex__gui__tree__order_8c
ex__gui__tree__state_8c
ex__gui__window__rect_8c
ex__gui__zorder_8c
ex__hash_8c
ex__hex_8c
ex__html_8c
ex__http2_8c
ex__http2__server_8c
ex__http3_8c
ex__http__cookie_8c
ex__http__form_8c
ex__http__mime_8c
ex__http__req_8c
ex__http__response_8c
ex__http__router_8c
ex__http__server_8c
ex__icc_8c
ex__iso__astar_8c
ex__json_8c
ex__kafka_8c
ex__linalg_8c
ex__list_8c
ex__log_8c
ex__markdown_8c
ex__module_8c
ex__monolith_8c
ex__network_8c
ex__network_8h
ex__network__accept_8c
ex__network__alpn_8c
ex__network__connect__abort_8c
ex__network__mock_8c
ex__network__proxy_8c
ex__network__reactor_8c
ex__network__sni_8c
ex__network__sse_8c
ex__network__ssl_8c
ex__network__ssl__hardened_8c
ex__network__ssl__hostname_8c
ex__network__ssl__nonblock_8c
ex__network__tls__defaults_8c
ex__network__ws_8c
ex__network__ws__limits_8c
ex__nstr_8c
ex__parallel__for_8c
ex__parallel__team_8c
ex__pcre_8c
ex__pretty_8c
ex__pth_8c
ex__query_8c
ex__random_8c
ex__sha256_8c
ex__signals_8c
ex__spawn_8c
ex__ssrf_8c
ex__stack_8c
ex__thread__pool__discard_8c
ex__thread__pool__wait_8c
ex__threads_8c
ex__tiff_8c
ex__time__formats_8c
ex__trajectory_8c
ex__trees_8c
ex__url_8c
ex__url__safe_8c
ex__wrap_8c
ex__x509_8c
ex__zip_8c
ex__zlib_8c
ex_accept_pool_client_8c example
ex_accept_pool_server_8c example
ex_allegro_4_8c example
ex_atlas_8c example
ex_base64_8c example
ex_clock_sync_8c example
ex_common_8c example
ex_configfile_8c example
ex_cookies_8c example
ex_crypto_8c example
ex_diff_8c example
ex_digest_8c example
ex_disk_cache_8c example
ex_dns_8c example
ex_ecc_8c example
ex_entropy_8c example
ex_exceptions_8c example
ex_file_8c example
ex_file_copy_8c example
ex_fluid_8c example
ex_fluid_config_8c example
ex_fluid_config_8h example
ex_git_8c example
ex_gui_8c example
ex_gui_bench_8c example
ex_gui_button_keys_8c example
ex_gui_checkbox_label_8c example
ex_gui_checkbox_release_8c example
ex_gui_color_picker_8c example
ex_gui_colorwheel_8c example
ex_gui_combobox_items_8c example
ex_gui_combobox_wheel_8c example
ex_gui_cursor_8c example
ex_gui_curve_8c example
ex_gui_custom_8c example
ex_gui_custom_mouse_8c example
ex_gui_datagrid_8c example
ex_gui_datagrid_keys_8c example
ex_gui_datagrid_sort_8c example
ex_gui_detach_8c example
ex_gui_dictionary_8c example
ex_gui_display_fallback_8c example
ex_gui_double_click_8c example
ex_gui_dropmenu_8c example
ex_gui_frameless_drag_8c example
ex_gui_global_scroll_8c example
ex_gui_glyph_8c example
ex_gui_grid_8c example
ex_gui_hexview_8c example
ex_gui_hover_8c example
ex_gui_isometric_8c example
ex_gui_kvtable_8c example
ex_gui_label_fits_8c example
ex_gui_label_lines_8c example
ex_gui_layout_persist_8c example
ex_gui_listbox_8c example
ex_gui_md_8c example
ex_gui_multiwin_8c example
ex_gui_network_8c example
ex_gui_particles_8c example
ex_gui_placeholder_8c example
ex_gui_popup_8c example
ex_gui_progressbar_8c example
ex_gui_reentrant_8c example
ex_gui_round_8c example
ex_gui_sectionlist_8c example
ex_gui_slider_default_8c example
ex_gui_slider_handle_8c example
ex_gui_slider_step_8c example
ex_gui_splitpane_8c example
ex_gui_suggest_8c example
ex_gui_syntaxview_8c example
ex_gui_text_shorten_8c example
ex_gui_textarea_selection_8c example
ex_gui_tooltip_8c example
ex_gui_tree_lazy_8c example
ex_gui_tree_order_8c example
ex_gui_tree_state_8c example
ex_gui_window_rect_8c example
ex_gui_zorder_8c example
ex_hash_8c example
ex_hex_8c example
ex_html_8c example
ex_http2_8c example
ex_http2_server_8c example
ex_http_cookie_8c example
ex_http_form_8c example
ex_http_mime_8c example
ex_http_req_8c example
ex_http_response_8c example
ex_http_router_8c example
ex_http_server_8c example
ex_icc_8c example
ex_iso_astar_8c example
ex_json_8c example
ex_kafka_8c example
ex_linalg_8c example
ex_list_8c example
ex_log_8c example
ex_markdown_8c example
ex_module_8c example
ex_monolith_8c example
ex_network_8c example
ex_network_8h example
ex_network_accept_8c example
ex_network_alpn_8c example
ex_network_connect_abort_8c example
ex_network_mock_8c example
ex_network_proxy_8c example
ex_network_reactor_8c example
ex_network_sni_8c example
ex_network_sse_8c example
ex_network_ssl_8c example
ex_network_ssl_hardened_8c example
ex_network_ssl_hostname_8c example
ex_network_ssl_nonblock_8c example
ex_network_tls_defaults_8c example
ex_network_ws_8c example
ex_network_ws_limits_8c example
ex_nstr_8c example
ex_parallel_for_8c example
ex_parallel_team_8c example
ex_pcre_8c example
ex_pretty_8c example
ex_pth_8c example
ex_query_8c example
ex_random_8c example
ex_sha256_8c example
ex_signals_8c example
ex_spawn_8c example
ex_ssrf_8c example
ex_stack_8c example
ex_threads_8c example
ex_tiff_8c example
ex_time_formats_8c example
ex_trajectory_8c example
ex_trees_8c example
ex_url_8c example
ex_url_safe_8c example
ex_wrap_8c example
ex_x509_8c example
ex_zip_8c example
ex_zlib_8c example
mainpage_8dox
n__3d_8c
n__3d_8h
n__aabb_8c
n__aabb_8h
n__allegro5_8c
n__allegro5_8h
n__allegro__4_8c
n__allegro__4_8h
n__anim_8c
n__anim_8h
n__astar_8c
n__astar_8h
n__atlas_8c
n__atlas_8h
n__avro_8c
n__avro_8h
n__base64_8c
n__base64_8h
n__clipboard_8c
n__clipboard_8h
n__clock__sync_8c
n__clock__sync_8h
n__common_8c
n__common_8h
n__config__file_8c
n__config__file_8h
n__cookies_8c
n__cookies_8h
n__crypto_8c
n__crypto_8h
n__dead__reckoning_8c
n__dead__reckoning_8h
n__diff_8c
n__diff_8h
n__digest_8c
n__digest_8h
n__disk__cache_8c
n__disk__cache_8h
n__dns_8c
n__dns_8h
n__ecc_8c
n__ecc_8h
n__entropy_8c
n__entropy_8h
n__enum_8h
n__exceptions_8c
n__exceptions_8h
n__files_8c
n__files_8h
n__fluids_8c
n__fluids_8h
n__games_8c
n__games_8h
n__git_8c
n__git_8h
n__gui_8c
n__gui_8h
n__gui__color_8c
n__gui__color_8h
n__gui__colorwheel_8c
n__gui__colorwheel_8h
n__gui__curve_8c
n__gui__curve_8h
n__gui__grid_8c
n__gui__grid_8h
n__gui__md_8c
n__gui__md_8h
n__gui__popup_8c
n__gui__popup_8h
n__hash_8c
n__hash_8h
n__hex_8c
n__hex_8h
n__html_8c
n__html_8h
n__http2_8c
n__http2_8h
n__http2__server_8c
n__http2__server_8h
n__http3_8c
n__http3_8h
n__http__cookie_8c
n__http__cookie_8h
n__http__form_8c
n__http__form_8h
n__http__mime_8c
n__http__mime_8h
n__http__req_8c
n__http__req_8h
n__http__response_8c
n__http__response_8h
n__http__router_8c
n__http__router_8h
n__http__server_8c
n__http__server_8h
n__icc_8c
n__icc_8h
n__iso__engine_8c
n__iso__engine_8h
n__json_8c
n__json_8h
n__kafka_8c
n__kafka_8h
n__linalg_8c
n__linalg_8h
n__list_8c
n__list_8h
n__log_8c
n__log_8h
n__lz4_8c
n__lz4_8h
n__markdown_8c
n__markdown_8h
n__module_8c
n__module_8h
n__network_8c
n__network_8h
n__network__accept__pool_8c
n__network__accept__pool_8h
n__network__msg_8c
n__network__msg_8h
n__nodup__log_8c
n__nodup__log_8h
n__openssl__compat_8h
n__particles_8c
n__particles_8h
n__pcre_8c
n__pcre_8h
n__pretty_8c
n__pretty_8h
n__pth_8c
n__pth_8h
n__query_8c
n__query_8h
n__random_8c
n__random_8h
n__reactor_8c
n__reactor_8h
n__sha256_8c
n__sha256_8h
n__signals_8c
n__signals_8h
n__ssrf_8c
n__ssrf_8h
n__stack_8c
n__stack_8h
n__str_8c
n__str_8h
n__thread__pool_8c
n__thread__pool_8h
n__tiff_8c
n__tiff_8h
n__time_8c
n__time_8h
n__trajectory_8c
n__trajectory_8h
n__trees_8c
n__trees_8h
n__url__safe_8c
n__url__safe_8h
n__user_8c
n__user_8h
n__windows_8h
n__wrap_8c
n__wrap_8h
n__x509_8c
n__x509_8h
n__zip_8c
n__zip_8h
n__zlib_8c
n__zlib_8h
nilorea_8h
No results
8
n__str_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00
Table of Contents
src/n_str.c
String functions, everything you need to use string is here. More...
Detailed Description
String functions, everything you need to use string is here.
Author: Castagnier Mickael
Version: 1.0
Date: 01/04/05
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef NO_NSTR
#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_str.h"
#include "nilorea/n_hash.h"
#include <errno.h>
#include <pthread.h>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <dirent.h>
#ifdef __windows__
const char* strcasestr(const char* s1, const char* s2) {
__n_assert(s1, return NULL);
__n_assert(s2, return NULL);
size_t n = strlen(s2);
while (*s1) {
if (!strnicmp(s1++, s2, n))
return (s1 - 1);
}
return NULL;
} /* strcasestr */
#endif
void free_nstr_ptr(void* ptr) {
N_STR* strptr = (N_STR*)ptr;
if (ptr && strptr) {
FreeNoLog(strptr->data);
FreeNoLog(strptr);
}
} /* free_nstr_ptr( ... ) */
int _free_nstr(N_STR** ptr) {
__n_assert(ptr && (*ptr), return FALSE);
FreeNoLog((*ptr)->data);
FreeNoLog((*ptr));
return TRUE;
} /* free_nstr( ... ) */
int free_nstr_nolog(N_STR** ptr) {
if ((*ptr)) {
FreeNoLog((*ptr)->data);
FreeNoLog((*ptr));
}
return TRUE;
} /* free_nstr( ... ) */
void free_nstr_ptr_nolog(void* ptr) {
N_STR* strptr = (N_STR*)ptr;
if (strptr) {
FreeNoLog(strptr->data);
FreeNoLog(strptr);
}
} /* free_nstr_ptr_nolog( ... ) */
char* trim_nocopy(char* s) {
__n_assert(s, return NULL);
if (strlen(s) == 0)
return s;
char* start = s;
/* skip spaces at start */
while (*start && isspace((unsigned char)*start))
start++;
char* end = s + strlen(s) - 1;
/* iterate over the rest remebering last non-whitespace */
while (*end && isspace((unsigned char)*end) && end > s) {
end--;
}
end++;
/* write the terminating zero after last non-whitespace */
*end = 0;
return start;
} /* trim_nocopy */
char* trim(const char* s) {
__n_assert(s, return NULL);
char* copy = strdup(s);
__n_assert(copy, return NULL);
const char* trimmed = trim_nocopy(copy);
char* result = strdup(trimmed);
free(copy);
return result;
} /* trim */
char* nfgets(char* buffer, NSTRBYTE size, FILE* stream) {
__n_assert(buffer, return NULL);
__n_assert(stream, return NULL);
if (size >= INT_MAX) {
n_log(LOG_ERR, "size of %zu too big, >INT_MAX for buffer %p", size, buffer);
return NULL;
}
if (!fgets(buffer, (int)size, stream)) {
return NULL;
}
return buffer;
} /* nfgets(...) */
int empty_nstr(N_STR* nstr) {
__n_assert(nstr, return FALSE);
__n_assert(nstr->data, return FALSE);
nstr->written = 0;
memset(nstr->data, 0, nstr->length);
return TRUE;
}
N_STR* new_nstr(NSTRBYTE size) {
N_STR* str = NULL;
if (size == (NSTRBYTE)-1) {
n_log(LOG_ERR, "new_nstr: size %zu would wrap the terminator allocation", (size_t)size);
return NULL;
}
Malloc(str, N_STR, 1);
__n_assert(str, return NULL);
str->written = 0;
if (size == 0) {
str->data = NULL;
str->length = 0;
} else {
Malloc(str->data, char, size + 1);
__n_assert(str->data, Free(str); return NULL);
str->length = size;
}
return str;
} /* new_nstr(...) */
int char_to_nstr_ex(const char* from, NSTRBYTE nboct, N_STR** to) {
if ((*to)) {
n_log(LOG_ERR, "destination N_STR **str is not NULL (%p), it contain (%s). You must provide an empty destination.", (*to), ((*to) && (*to)->data) ? (*to)->data : "DATA_IS_NULL");
n_log(LOG_ERR, "Data to copy: %s", _str(from));
return FALSE;
};
(*to) = new_nstr(nboct + 1);
__n_assert(to && (*to), return FALSE);
/* added a sizeof( void * ) to add a consistant and secure padding at the end */
__n_assert((*to)->data, Free((*to)); return FALSE);
memcpy((*to)->data, from, nboct);
(*to)->written = nboct;
return TRUE;
} /* char_to_nstr(...) */
N_STR* char_to_nstr(const char* src) {
__n_assert(src, return NULL);
N_STR* strptr = NULL;
size_t length = strlen(src);
char_to_nstr_ex(src, length, &strptr);
return strptr;
} /* char_to_str(...) */
N_STR* char_to_nstr_nocopy(char* src) {
__n_assert(src, return NULL);
N_STR* to = NULL;
to = new_nstr(0);
__n_assert(to, return NULL);
to->data = src;
to->written = strlen(src);
to->length = to->written + 1; // include src end of string
return to;
} /* char_to_str_nocopy(...) */
N_STR* file_to_nstr(char* filename) {
N_STR* tmpstr = NULL;
struct stat filestat;
FILE* in = NULL;
int error = 0;
__n_assert(filename, n_log(LOG_ERR, "Unable to make a string from NULL filename"); return NULL);
int fd = open(filename, O_RDONLY);
error = errno;
if (fd == -1) {
n_log(LOG_ERR, "Unable to open %s for reading. Errno: %s", filename, strerror(error));
return NULL;
}
if (fstat(fd, &filestat) != 0) {
error = errno;
#ifdef __linux__
if (error == EOVERFLOW) {
n_log(LOG_ERR, "%s size is too big ,EOVERFLOW)", filename);
} else {
#endif
n_log(LOG_ERR, "Couldn't stat %s. Errno: %s", filename, strerror(error));
#ifdef __linux__
}
#endif
close(fd);
return NULL;
}
if ((uint64_t)filestat.st_size >= (uint64_t)UINT32_MAX) {
n_log(LOG_ERR, "file size >= 4GB is not possible yet. %s is %lld oct", filename, (long long)filestat.st_size);
close(fd);
return NULL;
}
n_log(LOG_DEBUG, "%s file size is: %lld", filename, (long long)filestat.st_size);
in = fdopen(fd, "rb");
if (!in) {
n_log(LOG_ERR, "fdopen failed on %s (fd=%d)", filename, fd);
close(fd);
return NULL;
}
tmpstr = new_nstr((size_t)filestat.st_size + 1);
__n_assert(tmpstr, fclose(in); n_log(LOG_ERR, "Unable to get a new nstr of %lld octets", (long long)filestat.st_size + 1); return NULL);
tmpstr->written = (size_t)filestat.st_size;
/* an empty file (0 bytes) is a valid read, not an error: fread would be asked to
read 0 bytes and return 0, which is not a failure. Return the empty (calloc'd,
NUL-terminated) string so callers treat an empty file as empty content instead
of a read error. Only read when there is at least one byte. */
if (tmpstr->written > 0) {
if (fread(tmpstr->data, sizeof(char), tmpstr->written, in) == 0) {
n_log(LOG_ERR, "Couldn't read %s, fread return 0", filename);
free_nstr(&tmpstr);
fclose(in);
return NULL;
}
if (ferror(in)) {
n_log(LOG_ERR, "There were some errors when reading %s", filename);
free_nstr(&tmpstr);
fclose(in);
return NULL;
}
}
fclose(in);
return tmpstr;
} /*file_to_nstr */
/* Write a whole N_STR into a file */
int nstr_to_fd(N_STR* str, FILE* out, int lock) {
#ifdef __linux__
struct flock out_lock;
#endif
__n_assert(out, return FALSE);
__n_assert(str, return FALSE);
int ret = TRUE;
if (lock == 1) {
#ifdef __linux__
memset(&out_lock, 0, sizeof(out_lock));
out_lock.l_type = F_WRLCK;
/* Place a write lock on the file. */
fcntl(fileno(out), F_SETLKW, &out_lock);
#else
// cppcheck-suppress uselessAssignmentArg ; intentional: suppress unused parameter warning on non-POSIX
lock = 2;
#endif
}
size_t written_to_file = 0;
if ((written_to_file = fwrite(str->data, sizeof(char), str->written, out)) != (size_t)str->written) {
n_log(LOG_ERR, "Couldn't write file, fwrite %zu of %zu octets", written_to_file, str->written);
ret = FALSE;
}
if (ferror(out)) {
n_log(LOG_ERR, "There were some errors when writing to %d", fileno(out));
ret = FALSE;
}
if (lock == 1) {
#ifdef __linux__
memset(&out_lock, 0, sizeof(out_lock));
out_lock.l_type = F_WRLCK;
/* Place a write lock on the file. */
fcntl(fileno(out), F_SETLKW, &out_lock);
#else
// cppcheck-suppress uselessAssignmentArg
// cppcheck-suppress unreadVariable
lock = 2; /* compiler warning suppressor for non-POSIX */
#endif
}
return ret;
} /* nstr_to_fd( ... ) */
int nstr_to_file(N_STR* str, char* filename) {
__n_assert(str, return FALSE);
__n_assert(filename, return FALSE);
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600); // Secure permissions: rw-------
if (fd == -1) {
n_log(LOG_ERR, "Couldn't open %s for writing (0600). Errno: %s", _str(filename), strerror(errno));
return FALSE;
}
FILE* out = NULL;
out = fdopen(fd, "wb");
if (!out) {
n_log(LOG_ERR, "fdopen failed for %s (fd=%d). Errno: %s", _str(filename), fd, strerror(errno));
close(fd);
return FALSE;
}
int ret = nstr_to_fd(str, out, 1);
fclose(out);
n_log(LOG_DEBUG, "%s file size is: %lld", _str(filename), (long long)str->written);
return ret;
} /* nstr_to_file(...) */
int str_to_int_ex(const char* s, NSTRBYTE start, NSTRBYTE end, int* i, const int base) {
char* tmpstr = NULL;
char* endstr = NULL;
long int l = 0;
__n_assert(s, return FALSE);
if (start > end) return FALSE;
Malloc(tmpstr, char, sizeof(int) + end - start + 8);
__n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
memcpy(tmpstr, s + start, end - start);
errno = 0;
l = strtol(tmpstr, &endstr, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
n_log(LOG_ERR, "OVERFLOW reached when converting %s to int", tmpstr);
Free(tmpstr);
return FALSE;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
n_log(LOG_ERR, "UNDERFLOW reached when converting %s to int", tmpstr);
Free(tmpstr);
return FALSE;
}
if (*endstr != '\0' && *endstr != '\n') {
n_log(LOG_ERR, "Impossible conversion for %s", tmpstr);
Free(tmpstr);
return FALSE;
}
Free(tmpstr);
*i = (int)l;
return TRUE;
} /* str_to_int_ex( ... ) */
int str_to_int_nolog(const char* s, NSTRBYTE start, NSTRBYTE end, int* i, const int base, N_STR** infos) {
char* tmpstr = NULL;
char* endstr = NULL;
long int l = 0;
__n_assert(s, return FALSE);
if (start > end) return FALSE;
Malloc(tmpstr, char, sizeof(int) + end - start + 8);
__n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
memcpy(tmpstr, s + start, end - start);
errno = 0;
l = strtol(tmpstr, &endstr, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
nstrprintf((*infos), "OVERFLOW reached when converting %s to int", tmpstr);
Free(tmpstr);
return FALSE;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
nstrprintf((*infos), "UNDERFLOW reached when converting %s to int", tmpstr);
Free(tmpstr);
return FALSE;
}
if (*endstr != '\0' && *endstr != '\n') {
nstrprintf((*infos), "Impossible conversion for %s", tmpstr);
Free(tmpstr);
return FALSE;
}
Free(tmpstr);
*i = (int)l;
return TRUE;
} /* str_to_int_nolog( ... ) */
int str_to_int(const char* s, int* i, const int base) {
int ret = FALSE;
if (s) {
ret = str_to_int_ex(s, 0, strlen(s), i, base);
}
return ret;
} /* str_to_int(...) */
int str_to_long_ex(const char* s, NSTRBYTE start, NSTRBYTE end, long int* i, const int base) {
char* tmpstr = NULL;
char* endstr = NULL;
long l = 0;
__n_assert(s, return FALSE);
if (start > end) return FALSE;
Malloc(tmpstr, char, sizeof(int) + end - start + 8);
__n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
memcpy(tmpstr, s + start, end - start);
errno = 0;
l = strtol(tmpstr, &endstr, base);
int error = errno;
/* test return to number and errno values */
if (tmpstr == endstr) {
n_log(LOG_ERR, " number : %ld invalid (no digits found, 0 returned)", l);
Free(tmpstr);
return FALSE;
} else if (error == ERANGE && l == LONG_MIN) {
n_log(LOG_ERR, " number : %ld invalid (underflow occurred)", l);
Free(tmpstr);
return FALSE;
} else if (error == ERANGE && l == LONG_MAX) {
n_log(LOG_ERR, " number : %ld invalid (overflow occurred)", l);
Free(tmpstr);
return FALSE;
} else if (error == EINVAL) /* not in all c99 implementations - gcc OK */
{
n_log(LOG_ERR, " number : %ld invalid (base contains unsupported value)", l);
Free(tmpstr);
return FALSE;
} else if (error != 0 && l == 0) {
n_log(LOG_ERR, " number : %ld invalid (unspecified error occurred)", l);
Free(tmpstr);
return FALSE;
} else if (error == 0 && !*endstr) {
n_log(LOG_DEBUG, " number : %ld valid (and represents all characters read)", l);
} else if (error == 0 && *endstr != 0) {
n_log(LOG_DEBUG, " number : %ld valid (but additional characters remain", l);
}
Free(tmpstr);
*i = l;
return TRUE;
} /* str_to_long_ex( ... ) */
int str_to_long_long_ex(const char* s, NSTRBYTE start, NSTRBYTE end, long long int* i, const int base) {
char* tmpstr = NULL;
char* endstr = NULL;
long long l = 0;
__n_assert(s, return FALSE);
if (start > end) return FALSE;
Malloc(tmpstr, char, sizeof(int) + end - start + 8);
__n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
memcpy(tmpstr, s + start, end - start);
errno = 0;
l = strtoll(tmpstr, &endstr, base);
int error = errno;
/* test return to number and errno values */
if (tmpstr == endstr) {
n_log(LOG_ERR, "number: '%s' invalid (no digits found, 0 returned)", s);
Free(tmpstr);
return FALSE;
} else if (error == ERANGE && l == LLONG_MIN) {
n_log(LOG_ERR, "number: '%s' invalid (underflow occurred)", s);
Free(tmpstr);
return FALSE;
} else if (error == ERANGE && l == LLONG_MAX) {
n_log(LOG_ERR, "number: '%s' invalid (overflow occurred)", s);
Free(tmpstr);
return FALSE;
} else if (error == EINVAL) /* not in all c99 implementations - gcc OK */
{
n_log(LOG_ERR, "number: '%s' invalid (base contains unsupported value)", s);
Free(tmpstr);
return FALSE;
} else if (error != 0 && l == 0) {
n_log(LOG_ERR, "number: '%s' invalid (unspecified error occurred)", s);
Free(tmpstr);
return FALSE;
} else if (error == 0 && !*endstr) {
n_log(LOG_DEBUG, "number : '%llu' valid (and represents all characters read)", l);
} else if (error == 0 && *endstr != 0) {
n_log(LOG_DEBUG, " number : '%llu' valid (remaining characters: '%s')", l, s);
}
Free(tmpstr);
*i = l;
return TRUE;
} /* str_to_long_long_ex( ... ) */
int str_to_long(const char* s, long int* i, const int base) {
int ret = FALSE;
if (s) {
ret = str_to_long_ex(s, 0, strlen(s), i, base);
}
return ret;
} /* str_to_long(...) */
int str_to_long_long(const char* s, long long int* i, const int base) {
int ret = FALSE;
if (s) {
ret = str_to_long_long_ex(s, 0, strlen(s), i, base);
}
return ret;
} /* str_to_long(...) */
N_STR* nstrdup(N_STR* str) {
N_STR* new_str = NULL;
__n_assert(str, return NULL);
__n_assert(str->data, return NULL);
new_str = new_nstr(str->length);
if (new_str) {
if (new_str->data) {
memcpy(new_str->data, str->data, str->written);
new_str->length = str->length;
new_str->written = str->written;
} else {
Free(new_str);
n_log(LOG_ERR, "Error duplicating N_STR %p -> data", str);
}
} else {
n_log(LOG_ERR, "Error duplicating N_STR %p", str);
}
return new_str;
} /* nstrdup(...) */
int skipw(const char* string, char toskip, NSTRBYTE* iterator, int inc) {
int error_flag = 0;
__n_assert(string, return FALSE);
NSTRBYTE slen = (NSTRBYTE)strlen(string);
if (toskip == ' ') {
while (*iterator <= slen && isspace((unsigned char)string[*iterator])) {
if (inc < 0 && *iterator == 0) {
error_flag = 1;
break;
} else {
if (inc > 0)
*iterator = *iterator + (NSTRBYTE)inc;
else
*iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
}
}
} else {
while (*iterator <= slen && string[*iterator] == toskip) {
if (inc < 0 && *iterator == 0) {
error_flag = 1;
break;
} else {
if (inc > 0)
*iterator = *iterator + (NSTRBYTE)inc;
else
*iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
}
}
}
if (error_flag == 1 || *iterator > slen) {
return FALSE;
}
return TRUE;
} /*skipw(...)*/
int skipu(const char* string, char toskip, NSTRBYTE* iterator, int inc) {
int error_flag = 0;
__n_assert(string, return FALSE);
NSTRBYTE slen = (NSTRBYTE)strlen(string);
if (toskip == ' ') {
while (*iterator <= slen && !isspace((unsigned char)string[*iterator])) {
if (inc < 0 && *iterator == 0) {
error_flag = 1;
break;
} else {
if (inc > 0)
*iterator = *iterator + (NSTRBYTE)inc;
else
*iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
}
}
} else {
while (*iterator <= slen && string[*iterator] != toskip) {
if (inc < 0 && *iterator == 0) {
error_flag = 1;
break;
} else {
if (inc > 0)
*iterator = *iterator + (NSTRBYTE)inc;
else
*iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
}
}
}
if (error_flag == 1 || *iterator > slen) {
return FALSE;
}
return TRUE;
} /*Skipu(...)*/
int strup(const char* string, char* dest) {
NSTRBYTE it = 0;
__n_assert(string, return FALSE);
__n_assert(dest, return FALSE);
for (it = 0; it < (NSTRBYTE)strlen(string); it++)
dest[it] = (char)toupper(string[it]);
dest[strlen(string)] = '\0';
return TRUE;
} /*strup(...)*/
int strlo(const char* string, char* dest) {
NSTRBYTE it = 0;
__n_assert(string, return FALSE);
__n_assert(dest, return FALSE);
for (it = 0; it < (NSTRBYTE)strlen(string); it++)
dest[it] = (char)tolower(string[it]);
dest[strlen(string)] = '\0';
return TRUE;
} /*strlo(...)*/
int strcpy_u(const char* from, char* to, NSTRBYTE to_size, char delim, NSTRBYTE* it) {
NSTRBYTE _it = 0;
__n_assert(from, return FALSE);
__n_assert(to, return FALSE);
__n_assert(it, return FALSE);
while (_it < to_size && from[(*it)] != '\0' && from[(*it)] != delim) {
to[_it] = from[(*it)];
(*it) = (*it) + 1;
_it = _it + 1;
}
if (_it == to_size) {
_it--;
to[_it] = '\0';
n_log(LOG_DEBUG, "strcpy_u: not enough space to write %zu octet to dest (%zu max) , %s: %d", _it, to_size, __FILE__, __LINE__);
return FALSE;
}
to[_it] = '\0';
if (from[(*it)] != delim) {
n_log(LOG_DEBUG, "strcpy_u: delim value not found, written %zu octet to dest (%zu max) , %s: %d", _it, to_size, __FILE__, __LINE__);
return FALSE;
}
return TRUE;
} /* strcpy_u(...) */
char** split(const char* str, const char* delim, int empty) {
char** tab = NULL; /* result array */
const char* ptr = NULL; /* tmp pointer: it only ever walks the CONST input */
char** tmp = NULL; /* temporary pointer for realloc */
size_t sizeStr = 0; /* string token size */
size_t sizeTab = 0; /* array size */
const char* largestring = NULL; /* pointer to start of string */
__n_assert(str, return NULL);
__n_assert(delim, return NULL);
size_t sizeDelim = strlen(delim);
largestring = str;
while ((ptr = strstr(largestring, delim)) != NULL) {
sizeStr = (size_t)(ptr - largestring);
if (empty == 1 || sizeStr != 0) {
sizeTab++;
tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
__n_assert(tmp, goto error);
tab = tmp;
Malloc(tab[sizeTab - 1], char, sizeof(char) * (sizeStr + 1));
__n_assert(tab[sizeTab - 1], goto error);
memcpy(tab[sizeTab - 1], largestring, sizeStr);
tab[sizeTab - 1][sizeStr] = '\0';
}
ptr = ptr + sizeDelim;
largestring = ptr;
}
/* adding last part if any */
if (strlen(largestring) != 0) {
sizeStr = strlen(largestring);
sizeTab++;
tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
__n_assert(tmp, goto error);
tab = tmp;
Malloc(tab[sizeTab - 1], char, sizeof(char) * (sizeStr + 1));
__n_assert(tab[sizeTab - 1], goto error);
memcpy(tab[sizeTab - 1], largestring, sizeStr);
tab[sizeTab - 1][sizeStr] = '\0';
} else {
if (empty == 1) {
sizeTab++;
tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
__n_assert(tmp, goto error);
tab = tmp;
Malloc(tab[sizeTab - 1], char, (int)(sizeof(char) * 1));
__n_assert(tab[sizeTab - 1], goto error);
tab[sizeTab - 1][0] = '\0';
}
}
/* on ajoute une case a null pour finir le tableau */
sizeTab++;
tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
__n_assert(tmp, goto error);
tab = tmp;
tab[sizeTab - 1] = NULL;
return tab;
error:
free_split_result(&tab);
return NULL;
} /* split( ... ) */
int split_count(char** split_result) {
__n_assert(split_result, return -1);
__n_assert(split_result[0], return -1);
int it = 0;
while (split_result[it]) {
it++;
}
return it;
} /* split_count(...) */
int free_split_result(char*** tab) {
if (!(*tab))
return FALSE;
int it = 0;
while ((*tab)[it]) {
// cppcheck-suppress identicalInnerCondition ; Free() macro rechecks pointer internally
Free((*tab)[it]);
it++;
}
Free((*tab));
return TRUE;
} /* free_split_result(...)*/
char* join(char** splitresult, const char* delim) {
size_t delim_length = 0;
if (delim)
delim_length = strlen(delim);
size_t total_length = 0;
int it = 0;
while (splitresult[it]) {
// n_log( LOG_DEBUG , "split entry %d: %s" , it , splitresult[ it ] );
size_t entry_length = strlen(splitresult[it]);
if (entry_length >= SIZE_MAX - total_length) {
n_log(LOG_ERR, "join: total_length overflow accumulating entry %d", it);
return NULL;
}
total_length += entry_length;
// if there is a delimitor and 'it' isn't the last entry
if (delim && splitresult[it + 1]) {
if (delim_length >= SIZE_MAX - total_length) {
n_log(LOG_ERR, "join: total_length overflow accumulating delimiter at entry %d", it);
return NULL;
}
total_length += delim_length;
}
it++;
}
char* result = NULL;
Malloc(result, char, total_length + 1);
__n_assert(result, return NULL);
size_t position = 0;
it = 0;
while (splitresult[it]) {
size_t copy_size = strlen(splitresult[it]);
memcpy(&result[position], splitresult[it], copy_size);
position += copy_size;
// if there is a delimitor and 'it' isn't the last entry
if (delim && splitresult[it + 1]) {
memcpy(&result[position], delim, delim_length);
position += delim_length;
}
it++;
}
result[position] = '\0';
return result;
} /* join */
N_STR* nstrcat_ex(N_STR** dest, void* src, NSTRBYTE size, int resize_flag) {
char* ptr = NULL;
__n_assert(src, return NULL);
/* guard against size + 1 overflow (also protects new_nstr's internal +1). Must come before any arithmetic using size */
if (size >= SIZE_MAX - 1) {
n_log(LOG_ERR, "size too large in nstrcat_ex: %zu", size);
return NULL;
}
if (resize_flag == 0) {
if ((*dest)) {
/* check for written + size + 1 overflow before evaluating the expression */
if (size > SIZE_MAX - (*dest)->written - 1) {
n_log(LOG_ERR, "integer overflow in nstrcat_ex size calculation");
return NULL;
}
if (((*dest)->written + size + 1) > (*dest)->length) {
n_log(LOG_ERR, "%p to %p: not enough space. Resize forbidden. %zu needed, %zu available", src, (*dest), (*dest)->written + size + 1, (*dest)->length);
return NULL;
}
} else {
n_log(LOG_ERR, "%p to %p: not enough space. Resize forbidden. %zu needed, destination is NULL", src, (*dest), size + 1);
return NULL;
}
}
if (!(*dest)) {
(*dest) = new_nstr(size + 1);
if (!(*dest)) {
n_log(LOG_ERR, "could not allocate new N_STR of size %zu", size + 1);
return NULL;
}
}
/* check for integer overflow before computing new size */
if (size > SIZE_MAX - (*dest)->written - 1) {
n_log(LOG_ERR, "integer overflow in nstrcat_ex size calculation");
return NULL;
}
if ((*dest)->length < (*dest)->written + size + 1) {
(*dest)->length = (*dest)->written + size + 1;
if (Reallocz((*dest)->data, char, (*dest)->written, (*dest)->length) == FALSE) {
free_nstr(dest);
return NULL;
}
}
ptr = (*dest)->data + (*dest)->written;
memcpy(ptr, src, size);
(*dest)->written += size;
(*dest)->data[(*dest)->written] = '\0';
return (*dest);
} /* nstrcat_ex( ... ) */
N_STR* nstrcat_bytes_ex(N_STR** dest, void* data, NSTRBYTE size) {
__n_assert(dest, return NULL);
if (size == 0) {
n_log(LOG_ERR, "Could not copy 0 or less (%zu) octet!", size);
return NULL;
}
return nstrcat_ex(dest, data, size, 1);
} /* nstrcat_bytes_ex( ... )*/
int write_and_fit_ex(char** dest, NSTRBYTE* size, NSTRBYTE* written, const char* src, NSTRBYTE src_size, NSTRBYTE additional_padding) {
if (src_size == 0) return TRUE;
NSTRBYTE needed_size = (*written) + src_size + 1;
NSTRBYTE alloc_size = needed_size + additional_padding;
/* unsigned overflow guard: also documents alloc_size >= needed_size >= 2
* to the static analyzer (src_size >= 1 here, so needed_size >= 2). */
if (alloc_size < needed_size) return FALSE;
/* realloc if needed, also if destination is not allocated */
if ((needed_size >= (*size)) || !(*dest)) {
if (!(*dest)) {
(*written) = 0;
(*size) = 0;
}
if (Reallocz((*dest), char, (*size), alloc_size) == FALSE) {
n_log(LOG_ERR, "reallocation error !!!!");
return FALSE;
}
/* *size tracks total allocation (including padding) so the next
* appends that fit in the over-allocation skip the realloc. */
(*size) = alloc_size;
}
char* ptr = (*dest) + (*written);
memcpy(ptr, src, src_size);
(*written) += src_size;
(*dest)[(*written)] = '\0';
return TRUE;
} /* write_and_fit_ex( ...) */
int write_and_fit(char** dest, NSTRBYTE* size, NSTRBYTE* written, const char* src) {
return write_and_fit_ex(dest, size, written, src, strlen(src), 8);
} /* write_and_fit( ...) */
int scan_dir(const char* dir, LIST* result, const int recurse) {
return scan_dir_ex(dir, "*", result, recurse, 0);
}
int scan_dir_ex(const char* dir, const char* pattern, LIST* result, const int recurse, const int mode) {
DIR* dp = NULL;
struct dirent* entry = NULL;
struct stat statbuf;
if (!result)
return FALSE;
if ((dp = opendir(dir)) == NULL) {
n_log(LOG_ERR, "cannot open directory: %s", dir);
return FALSE;
}
N_STR* newname = NULL;
while ((entry = readdir(dp)) != NULL) {
if (!nstrprintf(newname, "%s/%s", dir, entry->d_name)) {
n_log(LOG_ERR, "could not allocate newname for %s/%s", dir, entry->d_name);
free_nstr(&newname);
continue;
}
if (stat(newname->data, &statbuf) >= 0) {
if (S_ISDIR(statbuf.st_mode) != 0) {
if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) {
free_nstr(&newname);
continue;
}
/* Recurse */
if (recurse != FALSE) {
if (scan_dir_ex(newname->data, pattern, result, recurse, mode) != TRUE) {
n_log(LOG_ERR, "scan_dir_ex( %s , %s , %p , %d , %d ) returned FALSE !", newname->data, pattern, result, recurse, mode);
}
}
free_nstr(&newname);
continue;
} else if (S_ISREG(statbuf.st_mode) != 0) {
if (wildmatcase(newname->data, pattern) == TRUE) {
if (mode == 0) {
char* file = strdup(newname->data);
if (file) {
list_push(result, file, &free);
} else {
n_log(LOG_ERR, "Error adding %s/%s to list", dir, entry->d_name);
}
} else if (mode == 1) {
list_push(result, newname, &free_nstr_ptr);
newname = NULL;
}
}
}
}
if (newname)
free_nstr(&newname);
}
closedir(dp);
return TRUE;
} /*scan_dir(...) */
int wildmat(register const char* text, register const char* p) {
register int last = 0;
register int matched = 0;
register int reverse = 0;
for (; *p; text++, p++) {
if (*text == '\0' && *p != '*')
return WILDMAT_ABORT;
switch (*p) {
case '\\':
/* Literal match with following character. */
p++;
/* FALLTHROUGH */
default:
if (*text != *p)
return FALSE;
continue;
case '?':
/* Match anything. */
continue;
case '*':
while (*++p == '*')
/* Consecutive stars act just like one. */
;
if (*p == '\0')
/* Trailing star matches everything. */
return TRUE;
while (*text)
if ((matched = wildmat(text++, p)) != FALSE)
return matched;
return WILDMAT_ABORT;
case '[':
reverse = p[1] == WILDMAT_NEGATE_CLASS ? TRUE : FALSE;
if (reverse)
/* Inverted character class. */
p++;
matched = FALSE;
if (p[1] == ']' || p[1] == '-')
if (*++p == *text)
matched = TRUE;
for (last = *p; *++p && *p != ']'; last = *p)
/* This next line requires a good C compiler. */
if (*p == '-' && p[1] != ']'
? *text <= *++p && *text >= last
: *text == *p)
matched = TRUE;
if (matched == reverse)
return FALSE;
continue;
}
}
#ifdef WILDMAT_MATCH_TAR_PATTERN
if (*text == '/')
return TRUE;
#endif /* MATCH_TAR_ATTERN */
return *text == '\0';
} /* wildmatch(...) */
int wildmatcase(register const char* text, register const char* p) {
register int last;
register int matched;
register int reverse;
for (; *p; text++, p++) {
if (*text == '\0' && *p != '*')
return WILDMAT_ABORT;
switch (*p) {
case '\\':
/* Literal match with following character. */
p++;
/* FALLTHROUGH */
default:
if (toupper(*text) != toupper(*p))
return FALSE;
continue;
case '?':
/* Match anything. */
continue;
case '*':
while (*++p == '*')
/* Consecutive stars act just like one. */
;
if (*p == '\0')
/* Trailing star matches everything. */
return TRUE;
while (*text)
if ((matched = wildmatcase(text++, p)) != FALSE)
return matched;
return WILDMAT_ABORT;
case '[':
reverse = p[1] == WILDMAT_NEGATE_CLASS ? TRUE : FALSE;
if (reverse)
/* Inverted character class. */
p++;
matched = FALSE;
if (p[1] == ']' || p[1] == '-')
if (toupper(*++p) == toupper(*text))
matched = TRUE;
for (last = toupper(*p); *++p && *p != ']'; last = toupper(*p))
if (*p == '-' && p[1] != ']'
? toupper(*text) <= toupper(*++p) && toupper(*text) >= last
: toupper(*text) == toupper(*p))
matched = TRUE;
if (matched == reverse)
return FALSE;
continue;
}
}
#ifdef WILDMAT_MATCH_TAR_PATTERN
if (*text == '/')
return TRUE;
#endif /* MATCH_TAR_ATTERN */
return *text == '\0';
} /* wildmatcase(...) */
char* str_replace(const char* string, const char* substr, const char* replacement) {
size_t string_len = 0, substr_len = 0, replacement_len = 0, count = 0, result_len = 0;
const char* scan = NULL;
char* result = NULL;
char* out = NULL;
if (!string) return NULL;
if (!substr || !substr[0] || !replacement) return strdup(string);
string_len = strlen(string);
substr_len = strlen(substr);
replacement_len = strlen(replacement);
for (scan = strstr(string, substr); scan; scan = strstr(scan + substr_len, substr)) count++;
/* string_len - count * substr_len + count * replacement_len + 1, each
* step checked: count * substr_len cannot exceed string_len. */
result_len = string_len - count * substr_len;
if (replacement_len && count > (SIZE_MAX - 1 - result_len) / replacement_len) {
n_log(LOG_ERR, "str_replace: the result would be too large (%zu occurrences of %zu bytes)", count,
replacement_len);
return NULL;
}
result_len += count * replacement_len;
Malloc(result, char, result_len + 1);
if (!result) {
n_log(LOG_ERR, "str_replace: could not allocate %zu bytes", result_len + 1);
return NULL;
}
out = result;
for (scan = string; count > 0; count--) {
const char* found = strstr(scan, substr);
size_t before = (size_t)(found - scan);
memcpy(out, scan, before);
out += before;
memcpy(out, replacement, replacement_len);
out += replacement_len;
scan = found + substr_len;
}
memcpy(out, scan, strlen(scan) + 1);
return result;
}
int str_natncasecmp(const char* a, size_t alen, const char* b, size_t blen) {
const unsigned char* pa = (const unsigned char*)a;
const unsigned char* pb = (const unsigned char*)b;
const unsigned char* const enda = pa + alen;
const unsigned char* const endb = pb + blen;
int zeros = 0, c = 0;
size_t common = 0;
if (!a || !b) return (a == b) ? 0 : (!a ? -1 : 1);
while (pa < enda && pb < endb) {
if (*pa >= '0' && *pa <= '9' && *pb >= '0' && *pb <= '9') {
const unsigned char* sa = pa;
const unsigned char* sb = pb;
const unsigned char* ea = NULL;
const unsigned char* eb = NULL;
while (sa < enda && *sa == '0') sa++;
while (sb < endb && *sb == '0') sb++;
for (ea = sa; ea < enda && *ea >= '0' && *ea <= '9'; ea++);
for (eb = sb; eb < endb && *eb >= '0' && *eb <= '9'; eb++);
/* Without leading zeros, the longer run is the larger number. */
if (ea - sa != eb - sb) return (ea - sa) < (eb - sb) ? -1 : 1;
c = memcmp(sa, sb, (size_t)(ea - sa));
if (c) return c < 0 ? -1 : 1;
/* Same value: remember the first difference in padding, used
* only if nothing after it tells the strings apart. */
if (!zeros && (sa - pa) != (sb - pb)) zeros = (sa - pa) < (sb - pb) ? -1 : 1;
pa = ea;
pb = eb;
} else {
unsigned char ca = *pa, cb = *pb;
if (ca >= 'A' && ca <= 'Z') ca = (unsigned char)(ca - 'A' + 'a');
if (cb >= 'A' && cb <= 'Z') cb = (unsigned char)(cb - 'A' + 'a');
if (ca != cb) return ca < cb ? -1 : 1;
pa++;
pb++;
}
}
if (pa < enda) return 1;
if (pb < endb) return -1;
if (zeros) return zeros;
common = alen < blen ? alen : blen;
c = common ? memcmp(a, b, common) : 0;
if (c) return c < 0 ? -1 : 1;
return alen == blen ? 0 : (alen < blen ? -1 : 1);
}
int str_natcasecmp(const char* a, const char* b) {
return str_natncasecmp(a, a ? strlen(a) : 0, b, b ? strlen(b) : 0);
}
int str_sanitize_ex(char* string, const NSTRBYTE string_len, const char* mask, const NSTRBYTE masklen, const char replacement) {
__n_assert(string, return FALSE);
__n_assert(mask, return FALSE);
NSTRBYTE it = 0;
for (it = 0; it < string_len; it++) {
NSTRBYTE mask_it = 0;
while (mask_it < masklen && mask[mask_it] != '\0') {
if (string[it] == mask[mask_it])
string[it] = replacement;
mask_it++;
}
}
return TRUE;
}
int str_sanitize(char* string, const char* mask, const char replacement) {
return str_sanitize_ex(string, strlen(string), mask, strlen(mask), replacement);
}
int resize_nstr(N_STR* nstr, size_t size) {
__n_assert(nstr, return FALSE);
if (size == 0) {
// Free the current buffer and reset fields
Free(nstr->data);
nstr->written = 0;
nstr->length = 0;
return TRUE;
}
if (!nstr->data) {
Malloc(nstr->data, char, size);
__n_assert(nstr->data, return FALSE);
} else {
if (Reallocz(nstr->data, char, nstr->length, size) == FALSE) {
return FALSE;
}
}
nstr->length = size;
return TRUE;
}
N_STR* nstrprintf_ex(N_STR** nstr_var, const char* format, ...) {
__n_assert(format, return NULL);
va_list args;
va_list args_copy;
int needed_size = 0;
// Calculate the required size for the formatted string
va_start(args, format);
va_copy(args_copy, args); // Copy args for reuse
needed_size = vsnprintf(NULL, 0, format, args);
va_end(args);
if (needed_size < 0) {
va_end(args_copy);
n_log(LOG_ERR, "there was an error while computing the new size according to format \"%s\" for N_STR %p", format, (*nstr_var));
return NULL;
}
size_t needed = (size_t)(needed_size + 1); // Include null-terminator
// Allocate or resize the N_STR object as needed
if (!(*nstr_var)) {
(*nstr_var) = new_nstr(needed);
if (!(*nstr_var) || !(*nstr_var)->data) {
va_end(args_copy);
n_log(LOG_ERR, "could not allocate N_STR of size %zu", needed);
return NULL;
}
} else if (needed > (*nstr_var)->length) {
if (resize_nstr((*nstr_var), needed) == FALSE) {
va_end(args_copy);
n_log(LOG_ERR, "could not resize N_STR %p to size %zu", (*nstr_var), needed);
return NULL;
}
}
// Write the formatted string into the buffer
vsnprintf((*nstr_var)->data, needed, format, args_copy);
va_end(args_copy);
(*nstr_var)->written = (size_t)needed_size;
return (*nstr_var);
}
N_STR* nstrprintf_cat_ex(N_STR** nstr_var, const char* format, ...) {
__n_assert(format, return NULL);
va_list args;
va_start(args, format);
// duplicate va_list
va_list args_copy;
va_copy(args_copy, args);
// compute needed size
int needed_size = vsnprintf(NULL, 0, format, args_copy);
va_end(args_copy);
if (needed_size < 0) {
n_log(LOG_ERR, "vsnprintf size estimation failed for format \"%s\"", format);
va_end(args);
return NULL;
}
size_t needed = (size_t)(needed_size + 1); // +1 for '\0'
// initialize if needed
if (!(*nstr_var)) {
(*nstr_var) = new_nstr(needed);
if (!(*nstr_var)) {
va_end(args);
return NULL;
}
}
// check for overflow before computing total_needed
if (needed > SIZE_MAX - (*nstr_var)->written) {
va_end(args);
n_log(LOG_ERR, "integer overflow computing total_needed in nstrprintf_cat_ex");
return NULL;
}
// resize if needed
size_t total_needed = (*nstr_var)->written + needed;
if (total_needed > (*nstr_var)->length) {
if (resize_nstr((*nstr_var), total_needed) == FALSE) {
va_end(args);
n_log(LOG_ERR, "could not resize N_STR %p to size %zu", (*nstr_var), total_needed);
return NULL;
}
}
// append formatted string
char* write_ptr = (*nstr_var)->data + (*nstr_var)->written;
int written_now = vsnprintf(write_ptr, needed, format, args);
va_end(args);
if (written_now < 0) {
n_log(LOG_ERR, "vsnprintf failed while writing at offset %zu in N_STR %p", (*nstr_var)->written, (*nstr_var));
return NULL;
}
(*nstr_var)->written += (size_t)written_now;
(*nstr_var)->data[(*nstr_var)->written] = '\0'; // ensure final \0
return *nstr_var;
}
N_STR* n_str_template_expand(const char* tmpl, HASH_TABLE* vars) {
if (!tmpl) return NULL;
N_STR* result = new_nstr(strlen(tmpl) + 256);
if (!result) return NULL;
const char* p = tmpl;
while (*p) {
if (p[0] == '{' && p[1] == '{') {
const char* end = strstr(p + 2, "}}");
if (end) {
size_t klen = (size_t)(end - (p + 2));
char key[256];
if (klen < sizeof(key)) {
memcpy(key, p + 2, klen);
key[klen] = '\0';
char* val = NULL;
if (vars && ht_get_string(vars, key, &val) == TRUE && val) {
nstrprintf_cat(result, "%s", val);
} else {
nstrprintf_cat(result, "{{%s}}", key);
}
p = end + 2;
continue;
}
}
}
char ch[2] = {*p, '\0'};
nstrprintf_cat(result, "%s", ch);
p++;
}
return result;
}
N_STR* n_str_url_encode(const char* src) {
if (!src) return NULL;
size_t in_len = strlen(src);
/* worst case: every byte expands to "%HH" -> 3x */
if (in_len > (SIZE_MAX - 1) / 3) return NULL;
N_STR* out = new_nstr(in_len * 3 + 1);
if (!out) return NULL;
for (const unsigned char* p = (const unsigned char*)src; *p; p++) {
unsigned char c = *p;
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '_' || c == '.' || c == '~') {
char ch[2] = {(char)c, '\0'};
if (!nstrprintf_cat(out, "%s", ch)) {
free_nstr(&out);
return NULL;
}
} else {
if (!nstrprintf_cat(out, "%%%02X", c)) {
free_nstr(&out);
return NULL;
}
}
}
return out;
}
#endif /* #ifndef NOSTR */
int n_str_utf8_valid(const char* s, size_t len) {
const unsigned char* p = (const unsigned char*)s;
size_t i = 0;
if (!s)
return len == 0 ? TRUE : FALSE;
while (i < len) {
unsigned char c = p[i];
size_t need;
unsigned long cp;
if (c < 0x80) {
i++;
continue;
}
if (c >= 0xC2 && c <= 0xDF) {
need = 1;
cp = c & 0x1Fu;
} else if (c >= 0xE0 && c <= 0xEF) {
need = 2;
cp = c & 0x0Fu;
} else if (c >= 0xF0 && c <= 0xF4) {
need = 3;
cp = c & 0x07u;
} else {
return FALSE; /* stray continuation byte, C0/C1 overlong lead, or above F4 */
}
if (len - i - 1 < need)
return FALSE;
for (size_t k = 1; k <= need; k++) {
if ((p[i + k] & 0xC0u) != 0x80u)
return FALSE;
cp = (cp << 6) | (p[i + k] & 0x3Fu);
}
if ((need == 2 && cp < 0x800) || (need == 3 && cp < 0x10000) ||
(cp >= 0xD800 && cp <= 0xDFFF) || cp > 0x10FFFF)
return FALSE;
i += need + 1;
}
return TRUE;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB