From 6512f9dd8fd139a1a5166342601f7463a5a1f266 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 28 Nov 2025 15:03:13 +0100 Subject: [PATCH 1/6] feat: compile abort buffers as separate shared library --- f90wrap/abort_buffers.c | 18 ++++++++++++++++++ f90wrap/meson.build | 7 +++++++ f90wrap/scripts/f2py_f90wrap.py | 13 +++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 f90wrap/abort_buffers.c diff --git a/f90wrap/abort_buffers.c b/f90wrap/abort_buffers.c new file mode 100644 index 00000000..149b6f5b --- /dev/null +++ b/f90wrap/abort_buffers.c @@ -0,0 +1,18 @@ +/* custom abort handler - James Kermode */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#define ABORT_BUFFER_SIZE 1024 +jmp_buf environment_buffer; +char abort_message[ABORT_BUFFER_SIZE]; + +#ifdef __cplusplus +} +#endif + +/* end of custom abort handler */ diff --git a/f90wrap/meson.build b/f90wrap/meson.build index f70b557b..84c0e616 100644 --- a/f90wrap/meson.build +++ b/f90wrap/meson.build @@ -35,6 +35,13 @@ py3.extension_module( link_args: link_args, ) +shared_library( + '_abort_buffers', + 'abort_buffers.c', + install: true, + install_dir: py3.get_install_dir() / 'f90wrap', +) + py3.install_sources( 'build.py', 'codegen.py', diff --git a/f90wrap/scripts/f2py_f90wrap.py b/f90wrap/scripts/f2py_f90wrap.py index ebcfe641..7f4122f1 100644 --- a/f90wrap/scripts/f2py_f90wrap.py +++ b/f90wrap/scripts/f2py_f90wrap.py @@ -84,8 +84,6 @@ def main(): #include #include - jmp_buf environment_buffer; - char abort_message[ABORT_BUFFER_SIZE]; void f90wrap_abort_(char *message, int len_message) { @@ -259,6 +257,17 @@ def persistant_callbacks(var): sys.argv.insert(1, '--backend') sys.argv.insert(2, 'meson') + # _abort_buffers shared library contains environment_buffer and abort_message variables allocation + # Those variables are used for error handling with setjmp/longjmp mechanism + # Those variables should be allocated once for every library generated with f90wrap + if "-c" in sys.argv: + import os + import f90wrap + f90wrap_path = os.path.dirname(f90wrap.__file__) + sys.argv.append(f"-L{f90wrap_path}") + sys.argv.append("-l_abort_buffers") + os.environ['LDFLAGS'] = os.environ.get('LDFLAGS', '') + f" -Wl,-rpath,{f90wrap_path}" + # Monkey-patch numpy's meson backend to fix include and library paths # for separate build directories when using --build-dir import os From 522d5877f862f3c5dbfc3ab3980c8491e7dc432e Mon Sep 17 00:00:00 2001 From: daniel Date: Tue, 20 Jan 2026 17:56:32 +0100 Subject: [PATCH 2/6] test: add example for shared library abort buffers --- examples/CMakeLists.txt | 1 + examples/Makefile | 3 +- examples/test_abort_buffer/Makefile | 59 +++++++++++++++++++++++ examples/test_abort_buffer/Makefile.meson | 2 + examples/test_abort_buffer/main_A.f90 | 14 ++++++ examples/test_abort_buffer/main_B.f90 | 14 ++++++ examples/test_abort_buffer/tests.py | 21 ++++++++ 7 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 examples/test_abort_buffer/Makefile create mode 100644 examples/test_abort_buffer/Makefile.meson create mode 100644 examples/test_abort_buffer/main_A.f90 create mode 100644 examples/test_abort_buffer/main_B.f90 create mode 100644 examples/test_abort_buffer/tests.py diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f11532f6..16986bb6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -71,6 +71,7 @@ list(APPEND tests type_bn type_check f2f_kind + test_abort_buffer ) foreach(test ${tests}) diff --git a/examples/Makefile b/examples/Makefile index 1f116c8b..22329169 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -68,7 +68,8 @@ EXAMPLES = \ subroutine_contains_issue101 \ type_bn \ type_check \ - f2f_kind + f2f_kind \ + test_abort_buffer # Append callback_print_function_issue93 only if Python >= 3.11 # This test is known not to work with older python version diff --git a/examples/test_abort_buffer/Makefile b/examples/test_abort_buffer/Makefile new file mode 100644 index 00000000..abaf884f --- /dev/null +++ b/examples/test_abort_buffer/Makefile @@ -0,0 +1,59 @@ +#======================================================================= +# define the compiler names +#======================================================================= +include ../make.inc + +UNAME = $(shell uname) + +PY_MOD = pywrapper +WRAPFLAGS = -v +F2PY = f2py-f90wrap +F90_SRC = main_A.f90 main_B.f90 +F90WRAP_SRC = $(addprefix f90wrap_,${F90_SRC}) +OBJ = $(F90_SRC:.f90=.o) +F90WRAP_OBJ = $(F90WRAP_SRC:.f90=.o) +SRC_LIB = src +F90WRAP_LIB = f90wrap +ifeq ($(UNAME),Darwin) + LDFLAGS = -Wl,-undefined,dynamic_lookup +endif + +.PHONY: all clean + +all: test + +clean: + rm -rf *.mod *.smod *.o lib*.a lib*.so f90wrap*.f90 ${PY_MOD}.py _${PY_MOD}*.so __pycache__/ .f2py_f2cmap build ${PY_MOD}* signatures_* + +# This can be done in parallel as previously +%.o: %.f90 + ${F90} ${F90FLAGS} -c $< -o $@ + +# This can be done in parallel as previously +f90wrap_%.f90: %.o + ${F90WRAP} -m ${PY_MOD}_$(patsubst %.o,%,$<) ${WRAPFLAGS} $(patsubst %.o,%.f90,$<) + +# Using 2 f2py-f90wrap steps enables to compile f90wrap_*.f90 separately rather than compiling them through f2py-f90wrap invocation. Compiling them separatly enables to compile them in parallel. +# This can also be done in parallel but probably not critical +signatures_%.pyf: f90wrap_%.f90 + ${F2PY} $< -m _${PY_MOD}_$(patsubst f90wrap_%.f90,%,$<) -h $@ + +# This only compile c interfaces no fortran interfaces +# This can now be done in parallel +f2py_%: signatures_%.pyf lib${SRC_LIB}.so lib${F90WRAP_LIB}.so + ${F2PY} ${F2PYFLAGS} -c $< -L. -l${SRC_LIB} -l${F90WRAP_LIB} + +# Compile f90wrap generated interfaces +# This can now be done in parallel +# This also ease support for fortran compiler other than gfortran +f90wrap_%.o: f90wrap_%.f90 + ${F90} ${F90FLAGS} -c $< -o $@ + +lib${SRC_LIB}.so: ${OBJ} + ${F90} ${F90FLAGS} -shared -o lib${SRC_LIB}.so ${OBJ} ${LDFLAGS} + +lib${F90WRAP_LIB}.so: ${F90WRAP_OBJ} lib${SRC_LIB}.so + ${F90} ${F90FLAGS} -shared -o lib${F90WRAP_LIB}.so ${F90WRAP_OBJ} -L. -l${SRC_LIB} ${LDFLAGS} + +test: f2py_main_A f2py_main_B + LD_LIBRARY_PATH=. ${PYTHON} tests.py diff --git a/examples/test_abort_buffer/Makefile.meson b/examples/test_abort_buffer/Makefile.meson new file mode 100644 index 00000000..e8e19cd9 --- /dev/null +++ b/examples/test_abort_buffer/Makefile.meson @@ -0,0 +1,2 @@ +# Just include the regular makefile for now +include Makefile diff --git a/examples/test_abort_buffer/main_A.f90 b/examples/test_abort_buffer/main_A.f90 new file mode 100644 index 00000000..55ddc4b0 --- /dev/null +++ b/examples/test_abort_buffer/main_A.f90 @@ -0,0 +1,14 @@ +module m_test_A + implicit none + private + + public :: calling_abort_A +contains + + subroutine calling_abort_A() + + call f90wrap_abort("Aborting from A") + + end subroutine + +end module m_test_A diff --git a/examples/test_abort_buffer/main_B.f90 b/examples/test_abort_buffer/main_B.f90 new file mode 100644 index 00000000..fffa5800 --- /dev/null +++ b/examples/test_abort_buffer/main_B.f90 @@ -0,0 +1,14 @@ +module m_test_B + implicit none + private + + public :: calling_abort_B +contains + + subroutine calling_abort_B() + + call f90wrap_abort("Aborting from B") + + end subroutine + +end module m_test_B diff --git a/examples/test_abort_buffer/tests.py b/examples/test_abort_buffer/tests.py new file mode 100644 index 00000000..fb21be5c --- /dev/null +++ b/examples/test_abort_buffer/tests.py @@ -0,0 +1,21 @@ +import unittest + +# The import order is important here +# If pywrapper_main_A is imported first everything is good +# If pywrapper_main_B is imported first a segmentation fault occurs +from pywrapper_main_B import m_test_b +from pywrapper_main_A import m_test_a + +class TestCallAbort(unittest.TestCase): + + def test_call_abort_a(self): + with self.assertRaises(RuntimeError): + m_test_a.calling_abort_a() + + def test_call_abort_b(self): + with self.assertRaises(RuntimeError): + m_test_b.calling_abort_b() + +if __name__ == '__main__': + + unittest.main() From 6aed85a9b7103fac8d49bdd93e9949fb496445df Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 21 Jan 2026 16:32:29 +0100 Subject: [PATCH 3/6] build: update meson template to link _abort_buffers and set rpaths --- examples/meson.build.template | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/meson.build.template b/examples/meson.build.template index 88b5bf08..0ced9820 100644 --- a/examples/meson.build.template +++ b/examples/meson.build.template @@ -15,12 +15,18 @@ endif link_args = [] if ff.get_id() == 'nvidia_hpc' - link_args = ['-lnvf', '-lrt'] + link_args += ['-lnvf', '-lrt'] endif py = import('python').find_installation(pure: false) py_dep = py.dependency() +dir_f90wrap = run_command(py, + ['-c', 'import os; import f90wrap; print(os.path.dirname(f90wrap.__file__))'], + check : true +).stdout().strip() +link_args += ['-l_abort_buffers', '-L'+dir_f90wrap] + incdir_numpy = run_command(py, ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'], check : true @@ -48,4 +54,6 @@ py.extension_module(name, dependencies : [py_dep, quadmath_dep], install : true, link_args: link_args, + install_rpath: dir_f90wrap, + build_rpath: dir_f90wrap, ) From 34dc4081a0411eab21e0ea1693b297b5a25ba5e5 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 23 Feb 2026 13:49:10 +0100 Subject: [PATCH 4/6] refactor: move f90wrap_abort implementation to unified library --- f90wrap/abort_buffers.c | 23 +++++++++++++++++++++++ f90wrap/scripts/f2py_f90wrap.py | 25 +------------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/f90wrap/abort_buffers.c b/f90wrap/abort_buffers.c index 149b6f5b..1c7112df 100644 --- a/f90wrap/abort_buffers.c +++ b/f90wrap/abort_buffers.c @@ -6,11 +6,34 @@ extern "C" #endif #include +#include +#include #define ABORT_BUFFER_SIZE 1024 jmp_buf environment_buffer; char abort_message[ABORT_BUFFER_SIZE]; +void f90wrap_abort_(char *message, int len_message) +{ + strncpy(abort_message, message, ABORT_BUFFER_SIZE); + abort_message[ABORT_BUFFER_SIZE-1] = '\0'; + longjmp(environment_buffer, 0); +} + +// copy of f90wrap_abort_ with a second underscore +void f90wrap_abort__(char *message, int len_message) +{ + strncpy(abort_message, message, ABORT_BUFFER_SIZE); + abort_message[ABORT_BUFFER_SIZE-1] = '\0'; + longjmp(environment_buffer, 0); +} + +void f90wrap_abort_int_handler(int signum) +{ + char message[] = "Interrupt occured"; + f90wrap_abort_(message, strlen(message)); +} + #ifdef __cplusplus } #endif diff --git a/f90wrap/scripts/f2py_f90wrap.py b/f90wrap/scripts/f2py_f90wrap.py index 7f4122f1..1652049a 100644 --- a/f90wrap/scripts/f2py_f90wrap.py +++ b/f90wrap/scripts/f2py_f90wrap.py @@ -80,34 +80,11 @@ def main(): extern char abort_message[ABORT_BUFFER_SIZE]; void f90wrap_abort_(char *message, int len); void f90wrap_abort_int_handler(int signum); + void f90wrap_abort__(char *message, int len_message); #include #include - - void f90wrap_abort_(char *message, int len_message) - { - strncpy(abort_message, message, ABORT_BUFFER_SIZE); - abort_message[ABORT_BUFFER_SIZE-1] = '\\0'; - longjmp(environment_buffer, 0); - } - - // copy of f90wrap_abort_ with a second underscore - // void (*f90wrap_abort__)(char *, int) = &f90wrap_abort_; - void f90wrap_abort__(char *message, int len_message) - { - strncpy(abort_message, message, ABORT_BUFFER_SIZE); - abort_message[ABORT_BUFFER_SIZE-1] = '\\0'; - longjmp(environment_buffer, 0); - } - - - void f90wrap_abort_int_handler(int signum) - { - char message[] = "Interrupt occured"; - f90wrap_abort_(message, strlen(message)); - } - /* end of custom abort handler */ """ From e1c6e103ed1e705e2f258aadbfd05f6de0baf9a0 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 23 Feb 2026 13:49:59 +0100 Subject: [PATCH 5/6] refactor: update meson monkey patching for external abort library --- f90wrap/scripts/f2py_f90wrap.py | 256 ++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 92 deletions(-) diff --git a/f90wrap/scripts/f2py_f90wrap.py b/f90wrap/scripts/f2py_f90wrap.py index 1652049a..cf6e1e37 100644 --- a/f90wrap/scripts/f2py_f90wrap.py +++ b/f90wrap/scripts/f2py_f90wrap.py @@ -44,6 +44,9 @@ from __future__ import print_function import sys +import os +import re +from pathlib import Path from numpy.f2py.auxfuncs import * # __all__ = [] @@ -234,22 +237,42 @@ def persistant_callbacks(var): sys.argv.insert(1, '--backend') sys.argv.insert(2, 'meson') - # _abort_buffers shared library contains environment_buffer and abort_message variables allocation - # Those variables are used for error handling with setjmp/longjmp mechanism - # Those variables should be allocated once for every library generated with f90wrap + # Determine which backend will be used (after the forced insertion above) + use_meson = ('--backend' in sys.argv and + sys.argv[sys.argv.index('--backend') + 1] == 'meson') + + # ------------------------------------------------------------------------- + # Link the _abort_buffers shared library + # ------------------------------------------------------------------------- + # _abort_buffers contains environment_buffer and abort_message variable + # allocations used for error handling with the setjmp/longjmp mechanism. + # These variables must be allocated once for every extension module + # generated with f90wrap. + # + # Linking strategy differs by backend: + # - distutils: pass -L, -l, and -Wl,-rpath via sys.argv and LDFLAGS + # - meson: inject fc.find_library() + abort_buffers_dep + # directly into the generated meson.build (monkey-patch) + # ------------------------------------------------------------------------- + f90wrap_path = None if "-c" in sys.argv: - import os import f90wrap f90wrap_path = os.path.dirname(f90wrap.__file__) - sys.argv.append(f"-L{f90wrap_path}") - sys.argv.append("-l_abort_buffers") - os.environ['LDFLAGS'] = os.environ.get('LDFLAGS', '') + f" -Wl,-rpath,{f90wrap_path}" - - # Monkey-patch numpy's meson backend to fix include and library paths - # for separate build directories when using --build-dir - import os - from pathlib import Path + if not use_meson: + # distutils: pass linker flags directly via sys.argv and LDFLAGS + sys.argv.append(f"-L{f90wrap_path}") + sys.argv.append("-l_abort_buffers") + os.environ['LDFLAGS'] = (os.environ.get('LDFLAGS', '') + f" -Wl,-rpath,{f90wrap_path}") + + # ------------------------------------------------------------------------- + # Monkey-patch numpy's meson backend + # ------------------------------------------------------------------------- + # When using the meson backend we need to patch the generated meson.build: + # 1. Always: inject install_rpath/build_rpath for the _abort_buffers lib + # 2. When --build-dir is a separate directory: fix include paths, library + # search paths, and translate .o files to Fortran sources + # ------------------------------------------------------------------------- build_dir_to_patch = None if '--build-dir' in sys.argv: build_dir_idx = sys.argv.index('--build-dir') + 1 @@ -259,94 +282,143 @@ def persistant_callbacks(var): if build_dir != '.': build_dir_to_patch = build_dir - if build_dir_to_patch: - # Monkey-patch the meson backend's write_meson_build method - try: - from numpy.f2py._backends import _meson - original_write_meson_build = _meson.MesonBackend.write_meson_build - - def patched_write_meson_build(self, build_dir): - # Call original method to generate meson.build - original_write_meson_build(self, build_dir) - - # Now patch the generated file - meson_build = Path(build_dir) / 'meson.build' - if meson_build.exists(): - content = meson_build.read_text() - modified = False - - # Add include path for parent directory (for .mod files) - if 'inc_parent = include_directories' not in content: - content = content.replace( - "inc_np = include_directories(incdir_numpy, incdir_f2py)", - "inc_np = include_directories(incdir_numpy, incdir_f2py)\ninc_parent = include_directories('..')" + if use_meson: + from numpy.f2py._backends import _meson + original_write_meson_build = _meson.MesonBackend.write_meson_build + + def patched_write_meson_build(self, build_dir): + # Call original method to generate meson.build + original_write_meson_build(self, build_dir) + + # Now patch the generated file + meson_build = Path(build_dir) / 'meson.build' + if not meson_build.exists(): + return + + content = meson_build.read_text() + modified = False + + # --- _abort_buffers library + rpath (always needed for meson) --- + if f90wrap_path: + if 'abort_buffers_dep' not in content: + # Build the block to insert before py.extension_module. + # NumPy 1.26 templates do not declare 'fc', so we + # conditionally add it when missing. + decl_lines = [] + if 'fc = meson.get_compiler' not in content: + decl_lines.append( + "fc = meson.get_compiler('fortran')" + ) + decl_lines.append( + f"abort_buffers_dep = fc.find_library(" + f"'_abort_buffers', dirs: ['{f90wrap_path}'])" + ) + abort_buf_block = '\n'.join(decl_lines) + '\n\n' + + # Insert before py.extension_module( + ext_mod_pattern = r"(py\.extension_module\()" + if re.search(ext_mod_pattern, content): + content = re.sub( + ext_mod_pattern, + abort_buf_block + r'\1', + content, ) modified = True - # Also add inc_parent to the include_directories list in py.extension_module - # Look for the include_directories list and add inc_parent if not already there - import re - # Find the include_directories section in extension_module - pattern = r'(include_directories:\s*\[\s*inc_np,)' - if re.search(pattern, content): - content = re.sub(pattern, r'\1\n inc_parent,', content) - modified = True - - # Replace '''.''' with inc_parent in include_directories list (for -I. flag) - if "'''.'''," in content: - content = content.replace("'''.''',", "inc_parent,") + # Add abort_buffers_dep to the dependencies list + dep_pattern = r'(dependencies\s*:\s*\[)' + if re.search(dep_pattern, content): + content = re.sub( + dep_pattern, + r'\1\n abort_buffers_dep,', + content, + ) modified = True - # Fix library search path to point to parent directory - if "lib_dir_0 = declare_dependency(link_args : ['''-L.'''])" in content: - content = content.replace( - "lib_dir_0 = declare_dependency(link_args : ['''-L.'''])", - "lib_dir_0 = declare_dependency(link_args : ['''-L../..'''])" + # --- Patches for separate build directories --- + if build_dir_to_patch: + # Add include path for parent directory (for .mod files) + if 'inc_parent = include_directories' not in content: + content = content.replace( + "inc_np = include_directories(incdir_numpy, incdir_f2py)", + "inc_np = include_directories(incdir_numpy, incdir_f2py)\n" + "inc_parent = include_directories('..')" + ) + modified = True + + # Add inc_parent to the include_directories list in + # py.extension_module + pattern = r'(include_directories:\s*\[\s*inc_np,)' + if re.search(pattern, content): + content = re.sub( + pattern, + r'\1\n inc_parent,', + content, ) modified = True - # Add Fortran source files corresponding to .o files - # Meson doesn't handle .o files properly, need to compile from source - # Collect .o files from command line - fortran_obj_files = [] - for arg in sys.argv: - if arg.endswith('.o'): - fortran_obj_files.append(arg) - - if fortran_obj_files: - import re - # Convert .o files to .f90 files and add them to py.extension_module sources - additional_sources = [] - for obj_file in fortran_obj_files: - # Try .f90, .F90, and .f extensions - for ext in ['.f90', '.F90', '.f']: - f90_file = obj_file.replace('.o', ext) - if os.path.exists(f90_file): - # Get relative path from build directory to source - rel_path = os.path.join('..', os.path.basename(f90_file)) - additional_sources.append(f" '''{rel_path}''',") - break - - if additional_sources: - # Find the sources list in py.extension_module and add our files - # Look for the pattern: py.extension_module('name', [ ... fortranobject_c ], ...) - # We want to insert before fortranobject_c - pattern = r'(py\.extension_module\([^,]+,\s*\[[^\]]*)(fortranobject_c)' - match = re.search(pattern, content, re.DOTALL) - if match: - # Insert additional sources before fortranobject_c - new_content = match.group(1) + '\n'.join(additional_sources) + '\n ' + match.group(2) - content = content[:match.start()] + new_content + content[match.end():] - modified = True - - if modified: - meson_build.write_text(content) - print(f"\nPatched {meson_build} for separate build directory") - - _meson.MesonBackend.write_meson_build = patched_write_meson_build - except (ImportError, AttributeError): - # numpy doesn't have meson backend (older version), no patching needed - pass + # Replace '''.''' with inc_parent in include_directories list + if "'''.'''," in content: + content = content.replace("'''.''',", "inc_parent,") + modified = True + + # Fix library search path to point to parent directory + if "lib_dir_0 = declare_dependency(link_args : ['''-L.'''])" in content: + content = content.replace( + "lib_dir_0 = declare_dependency(link_args : ['''-L.'''])", + "lib_dir_0 = declare_dependency(link_args : ['''-L../..'''])", + ) + modified = True + + # Add Fortran source files corresponding to .o files + # (meson doesn't handle .o files properly) + fortran_obj_files = [ + arg for arg in sys.argv if arg.endswith('.o') + ] + if fortran_obj_files: + additional_sources = [] + for obj_file in fortran_obj_files: + for ext in ['.f90', '.F90', '.f']: + f90_file = obj_file.replace('.o', ext) + if os.path.exists(f90_file): + rel_path = os.path.join( + '..', os.path.basename(f90_file) + ) + additional_sources.append( + f" '''{rel_path}'''," + ) + break + + if additional_sources: + pattern = ( + r'(py\.extension_module\([^,]+,\s*\[[^\]]*)' + r'(fortranobject_c)' + ) + match = re.search(pattern, content, re.DOTALL) + if match: + new_content = ( + match.group(1) + + '\n'.join(additional_sources) + + '\n ' + + match.group(2) + ) + content = ( + content[:match.start()] + + new_content + + content[match.end():] + ) + modified = True + + if modified: + meson_build.write_text(content) + detail = ( + " for separate build directory" + if build_dir_to_patch + else " with rpath" + ) + print(f"\nPatched {meson_build}{detail}") + + _meson.MesonBackend.write_meson_build = patched_write_meson_build numpy.f2py.main() From d4669dac418644e3900db6d7e102dfabf5a1e9d8 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 5 Mar 2026 15:05:14 +0100 Subject: [PATCH 6/6] fix: fix string length copy --- f90wrap/abort_buffers.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/f90wrap/abort_buffers.c b/f90wrap/abort_buffers.c index 1c7112df..f2aa0368 100644 --- a/f90wrap/abort_buffers.c +++ b/f90wrap/abort_buffers.c @@ -15,16 +15,18 @@ char abort_message[ABORT_BUFFER_SIZE]; void f90wrap_abort_(char *message, int len_message) { - strncpy(abort_message, message, ABORT_BUFFER_SIZE); - abort_message[ABORT_BUFFER_SIZE-1] = '\0'; + int copy_len = len_message < ABORT_BUFFER_SIZE - 1 ? len_message : ABORT_BUFFER_SIZE - 1; + strncpy(abort_message, message, copy_len); + abort_message[copy_len] = '\0'; longjmp(environment_buffer, 0); } // copy of f90wrap_abort_ with a second underscore void f90wrap_abort__(char *message, int len_message) { - strncpy(abort_message, message, ABORT_BUFFER_SIZE); - abort_message[ABORT_BUFFER_SIZE-1] = '\0'; + int copy_len = len_message < ABORT_BUFFER_SIZE - 1 ? len_message : ABORT_BUFFER_SIZE - 1; + strncpy(abort_message, message, copy_len); + abort_message[copy_len] = '\0'; longjmp(environment_buffer, 0); }