Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
6458714
COMP: Give install_linking the source directory instead of guessing it
gdevenyi Aug 15, 2026
919dcea
COMP: Show test output when the starter workflow fails
gdevenyi Aug 15, 2026
3aefe1b
COMP: Guard the CMP0169 policy setting so older CMake still configures
gdevenyi Aug 15, 2026
7d92f16
BUG: Compare NIfTI test output by content, not by compressed bytes
gdevenyi Aug 15, 2026
b6531f0
COMP: Build the optional code paths in the per-PR workflow
gdevenyi Sep 19, 2026
159d11d
ENH: Rework the clang-tidy configuration for C11
gdevenyi Aug 14, 2026
596441b
COMP: Add a shared compiler warning set, clean flags only
hjmjohnson Sep 21, 2026
77b7418
COMP: Widen CI coverage across platforms, linkage, and configurations
hjmjohnson Sep 21, 2026
063e838
COMP: Submit dashboard results over https, at the documented start time
hjmjohnson Sep 21, 2026
f578e03
COMP: Finish the migration from Travis to GitHub Actions
hjmjohnson Sep 21, 2026
4673c40
ENH: Declare the three functions that have a published counterpart
hjmjohnson Sep 21, 2026
8cdcf1a
COMP: Give the fsliolib internal functions static linkage
hjmjohnson Sep 21, 2026
54e74e1
COMP: Declare nifti_fileexists where it is defined
hjmjohnson Sep 21, 2026
0c0e66c
STYLE: Derive the aux_file bound from sizeof
hjmjohnson Sep 21, 2026
5c1665c
BUG: Bound the aux_file copy by sizeof rather than a repeated 24
gdevenyi Aug 15, 2026
ab2d71c
BUG: Free the extension data when nifti_add_extension fails
gdevenyi Aug 15, 2026
b4c6894
BUG: Free the previous filename when an ASCII header repeats the attr…
gdevenyi Aug 15, 2026
40009a9
BUG: Free the header on nifti_tool's duplicate-file failure paths
gdevenyi Aug 15, 2026
7b37e32
BUG: Fix signed/unsigned comparisons in the nifti tools
gdevenyi Aug 15, 2026
3147438
COMP: Install the tools the analysis jobs actually invoke
hjmjohnson Sep 22, 2026
7476a12
BUG: Stop loc_strnlen reading one byte past the buffer it is given
gdevenyi Aug 15, 2026
91d1bb9
STYLE: Add the missing newline at end of nifti1_tool.h
gdevenyi Aug 15, 2026
5e32002
BUG: Check dim[0] before using it to index dim[] in the NIFTI-2 conve…
gdevenyi Aug 15, 2026
66c76c2
BUG: Refuse a header whose dimensions overflow the voxel count
gdevenyi Aug 15, 2026
147a07a
BUG: Guard the voxel count in the NIFTI-1 library too
gdevenyi Sep 19, 2026
16a6b67
BUG: Swap a nifti_1_header as a NIFTI-1 header, not as whatever its m…
gdevenyi Aug 15, 2026
522e624
BUG: Swap the tool's headers at their own width too
gdevenyi Sep 19, 2026
0ddf192
BUG: Fix the ambiguous-filename path in nifti_findhdrname
gdevenyi Sep 19, 2026
b8d5246
BUG: Fix an out-of-bounds indirect call in axio_show_mim_summary
gdevenyi Aug 15, 2026
06cb5a6
ENH: Cover the MatrixIndicesMap dispatch in axio_show_mim_summary
hjmjohnson Sep 22, 2026
e96d1e9
BUG: Free the XML tree and image that cifti_tool parses
hjmjohnson Sep 22, 2026
f79d234
BUG: Check the header converted to nifti_image before reading its dat…
hjmjohnson Sep 22, 2026
2f6742c
ENH: Cover the NIFTI-2 dim[0] range check
hjmjohnson Sep 22, 2026
e3990de
ENH: Cover the swap width of a byte-swapped nifti_1_header
hjmjohnson Sep 22, 2026
3deb735
ENH: Cover the swap width of the tool's own header copies
hjmjohnson Sep 22, 2026
01ce2fa
ENH: Cover the voxel-count and volume-size overflow guards
hjmjohnson Sep 22, 2026
329e869
ENH: Cover the voxel-count guards in the NIFTI-1 library
hjmjohnson Sep 22, 2026
d0b9413
ENH: Cover the repeated-attribute path in the ASCII header reader
hjmjohnson Sep 22, 2026
457f8bf
ENH: Cover the loc_strnlen bound on an unterminated extension
hjmjohnson Sep 22, 2026
7a0b98b
ENH: Cover the ambiguous-filename path in nifti_findhdrname
hjmjohnson Sep 22, 2026
c051fd8
ENH: Reach nifti_tool's header-modification failure paths
hjmjohnson Sep 22, 2026
b5023c5
ENH: Self-test the NIfTI content comparison helper
hjmjohnson Sep 22, 2026
1abbd85
COMP: Give the pigz writers internal linkage
hjmjohnson Sep 22, 2026
b4876bf
COMP: Make an undeclared external function fail the build
hjmjohnson Sep 22, 2026
2505325
COMP: Build and test on Windows
hjmjohnson Sep 22, 2026
63b361a
COMP: Export the afni_xml half of the cifti library
hjmjohnson Sep 22, 2026
57a61a7
BUG: Bound the formatted write in znzprintf, and end the va_list
gdevenyi Aug 15, 2026
ac7ec09
BUG: Report a znzprintf failure as a failure
gdevenyi Sep 19, 2026
0350b30
ENH: Cover the znzprintf truncation path
hjmjohnson Sep 22, 2026
8449a1f
COMP: Declare the two znzlib functions that no header declares
hjmjohnson Sep 22, 2026
923e869
BUG: Check the allocations whose result is used immediately
gdevenyi Aug 15, 2026
0685f7f
BUG: Report the attribute failure instead of discarding it
gdevenyi Sep 19, 2026
c6410f5
ENH: Cover the ASCII path through the NIFTI-2 header reader
hjmjohnson Sep 22, 2026
b344fe4
BUG: Stop returning -1 from functions that return size_t
gdevenyi Aug 15, 2026
9b31abf
ENH: Test that a truncated image is rejected rather than accepted
hjmjohnson Sep 22, 2026
6ddc454
BUG: Keep the XML skip depth at the element that started the skip
gdevenyi Aug 15, 2026
d34f28c
BUG: Do not assume a popped element filled its stack slot
gdevenyi Sep 19, 2026
0e0033d
ENH: Cover XML nesting past the afni_xml stack limit
hjmjohnson Sep 22, 2026
db038e9
COMP: Detect a workflow that can never be scheduled
hjmjohnson Sep 22, 2026
c2d181e
BUG: Check the allocation in nifti_intent_code before copying into it
seanm Jan 14, 2026
f5f3766
STYLE: Drop the unreachable returns after FSLIOERR
seanm Jan 14, 2026
7387fd5
COMP: Parenthesize some macro parameters
seanm Jan 18, 2025
6d06faa
BUG: Widen some multiplications that are stored into size_t
seanm Jan 19, 2025
d93153c
BUG: Fix cifti_tool's CIFTI extension search, which never advanced
gdevenyi Aug 15, 2026
2405d09
ENH: Cover a CIFTI extension that is not first in the list
hjmjohnson Sep 22, 2026
5c63a01
BUG: Fix cdfbin's argument range check, which could never fire
gdevenyi Aug 15, 2026
493a317
ENH: Cover cdfbin's out-of-range selector return
hjmjohnson Sep 22, 2026
70fa3a6
BUG: Do not use sscanf's output when sscanf matched nothing
gdevenyi Aug 15, 2026
eb4f831
BUG: Use memcpy instead of casting to over-aligned pointer types
gdevenyi Aug 15, 2026
0a9aa9c
BUG: Stop casting away const in fslio and cifti
gdevenyi Aug 15, 2026
a2d3b5c
ENH: Give the cifti tools' gopt internal linkage
gdevenyi Aug 15, 2026
36976a2
DOC: Describe the CMake build and how to run the memory checks
gdevenyi Aug 15, 2026
1371c71
ENH: Make the integer conversions in allocations, copies and reads ex…
gdevenyi Aug 15, 2026
77e495f
ENH: Make the remaining integer conversions explicit
gdevenyi Aug 15, 2026
2696f66
ENH: Make the last implicit sign conversions explicit
gdevenyi Aug 15, 2026
7569506
ENH: Replace strcpy, strcat and strncpy with bounded variants
seanm Jan 2, 2026
129a637
ENH: Add strlcpy and strlcat for platforms whose libc lacks them
seanm Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 98 additions & 15 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,46 +1,129 @@
# clang-tidy configuration for nifti_clib.
#
# This is a C11 codebase that ships a stable ABI. Two consequences shape
# the list below:
#
# * Checks written for C++ idioms are noise here and are turned off
# rather than left to accumulate.
# * Any check that wants to change linkage or a declaration in an
# installed header is off, and must stay off. See the ABI section.
#
# Run it with:
# cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# run-clang-tidy -p build -j "$(nproc)" -quiet
#
Checks: '-*,
android-*,
-android-cloexec-fopen,

bugprone-*,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-assignment-in-if-condition,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-not-null-terminated-result,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-assignment-in-if-condition,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-not-null-terminated-result,
-bugprone-signed-char-misuse,

cert-*,
-cert-str34-c,
-cert-err34-c,
-cert-err33-c,
-cert-err34-c,
-cert-flp30-c,
-cert-str34-c,

clang-analyzer-*,
-clang-analyzer-alpha.clone.CloneChecker,
-clang-analyzer-alpha.deadcode.UnreachableCode,
-clang-analyzer-security.insecureAPI.strcpy,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-clang-analyzer-security.FloatLoopCounter,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-clang-analyzer-security.insecureAPI.strcpy,

google-*,
-google-readability-casting,
-google-readability-braces-around-statements,
-google-readability-casting,
-google-readability-function-size,

misc-*,
-misc-confusable-identifiers,
-misc-include-cleaner,
-misc-no-recursion,
-misc-use-internal-linkage,

performance-*,
-performance-type-promotion-in-math-fn,

readability-*,
-readability-magic-numbers,
-readability-avoid-nested-conditional-operator,
-readability-avoid-unconditional-preprocessor-if,
-readability-braces-around-statements,
-readability-else-after-return,
-readability-identifier-length,
-readability-inconsistent-ifelse-braces,
-readability-isolate-declaration,
-readability-braces-around-statements,
-readability-magic-numbers,
-readability-math-missing-parentheses,
-readability-misleading-indentation,
-readability-named-parameter,
-readability-non-const-parameter,
-readability-misleading-indentation'
-readability-redundant-parentheses'

# ---------------------------------------------------------------------------
# ABI: checks that must never be enabled
#
# misc-use-internal-linkage Wants `static` on every function without a
# visible external use. It flags 29 distinct functions here, and 13 of
# them are symbols currently exported by libniftiio, libfslio or
# libcifti -- FslFileType, FslGetHdrImgNames, fsl_fileexists,
# axml_recur_find_xml and the rest. Applying the fix-it deletes them
# from the shared libraries, silently breaking anyone linking against
# them. Most are also the functions -Wmissing-prototypes complains
# about; the fix for those is a file-local prototype, not `static`.
# Never enable this check without an ABI review.
#
# readability-non-const-parameter Wants `const` added to pointer
# parameters. Harmless inside a .c file, but its fix-it does not know
# which functions are declared in an installed header, and adding const
# there changes the published prototype.
#
# ---------------------------------------------------------------------------
# Turned off as noise, with counts from the run that introduced this file
#
# readability-math-missing-parentheses 623. Wants parentheses around
# every * inside a +. This is a house-style opinion, not a defect
# class, and the arithmetic here is conventional.
# misc-include-cleaner 620. An include-what-you-use
# tool. It flags every transitively-included declaration, including
# ones from nifti1_io.h that exist precisely so callers need one header.
# readability-inconsistent-ifelse-braces 246. clang-format owns brace
# placement here and is configured with InsertBraces: false.
# readability-named-parameter 14. Omitting parameter names in
# a prototype is idiomatic C.
# readability-redundant-parentheses 29. Directly at odds with
# bugprone-macro-parentheses, which is enabled and is the one that
# catches real defects.
#
# ---------------------------------------------------------------------------
# Notable checks that ARE enabled
#
# bugprone-macro-parentheses was disabled; re-enabled here.
# The macros in nifti_tool.h and the QSTR/NT_FILL families expand their
# arguments without parentheses, so any caller passing an expression
# gets silent precedence bugs.
# bugprone-unchecked-string-to-number-conversion
# bugprone-suspicious-realloc-usage leaks the original block when
# realloc returns NULL.
# bugprone-misplaced-widening-cast cast applied after the overflow
# rather than before it.
# bugprone-multi-level-implicit-pointer-conversion
#
# cert-err33-c stays off for now: it flags every unchecked printf and fseek
# as well as the allocations that matter, which buries the signal. The
# allocation cases are covered by clang-analyzer-unix.Malloc instead.
#
CheckOptions:
- key: readability-function-cognitive-complexity.IgnoreMacros
value: 1
# A ratchet, not a target. Several functions here (nifti_image_read,
# nifti_tool's act_* dispatchers) are large by design. This value is set
# just above today's worst so that nothing may get worse; lower it as
# functions are split, never raise it.
- key: readability-function-cognitive-complexity.Threshold
value: 428
110 changes: 110 additions & 0 deletions .github/check_workflow_triggers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Fail if a workflow's push or pull_request trigger can never match.

A branch filter naming a branch that does not exist leaves the workflow
configured but never scheduled, which looks identical to a workflow that
runs and passes: no red check appears, because no check appears at all.

Only filters made entirely of literal names are judged. A filter holding
any glob is left alone, since whether it can match depends on branches
that may not exist yet.

usage: check_workflow_triggers.py [workflow-dir]
"""
import glob
import os
import subprocess
import sys

import yaml

GLOB_CHARS = set('*?[]!+@')


def existing_branches():
"""Branch names on the remote, falling back to local refs."""
for cmd in (['git', 'ls-remote', '--heads', 'origin'],
['git', 'for-each-ref', '--format=%(refname)', 'refs/heads']):
out = subprocess.run(cmd, capture_output=True, text=True)
if out.returncode == 0 and out.stdout.strip():
names = set()
for line in out.stdout.split('\n'):
ref = line.split('refs/heads/')[-1].strip()
if ref:
names.add(ref)
if names:
return names
return set()


def default_branch():
env = os.environ.get('DEFAULT_BRANCH')
if env:
return env.strip()
out = subprocess.run(['git', 'symbolic-ref', '--short', 'refs/remotes/origin/HEAD'],
capture_output=True, text=True)
if out.returncode == 0 and out.stdout.strip():
return out.stdout.strip().split('/')[-1]
return None


def triggers(doc):
"""The `on:` mapping. PyYAML reads an unquoted `on` key as True."""
for key in (True, 'on', 'On', 'ON'):
if isinstance(doc, dict) and key in doc:
return doc[key]
return None


def main(argv):
where = argv[1] if len(argv) > 1 else '.github/workflows'
branches = existing_branches()
default = default_branch()
if not branches:
print('could not determine the repository branches; nothing checked')
return 0
print('branches on the remote: %d, default: %s'
% (len(branches), default or 'unknown'))

problems = []
for path in sorted(glob.glob(os.path.join(where, '*.yml'))
+ glob.glob(os.path.join(where, '*.yaml'))):
try:
doc = yaml.safe_load(open(path))
except yaml.YAMLError as exc:
problems.append('%s: cannot parse: %s' % (path, exc))
continue
on = triggers(doc)
if not isinstance(on, dict):
continue
for event in ('push', 'pull_request', 'pull_request_target'):
spec = on.get(event)
if not isinstance(spec, dict):
continue
names = spec.get('branches')
if not names:
continue
if any(GLOB_CHARS & set(n) for n in names):
continue
live = [n for n in names if n in branches]
if not live:
problems.append(
'%s: %s.branches names only %s, and no such branch exists.\n'
' This workflow can never be scheduled.%s'
% (path, event, ', '.join(repr(n) for n in names),
('\n The default branch is %r.' % default) if default else ''))
elif default and default not in names:
print('note: %s: %s.branches does not include the default '
'branch %r' % (path, event, default))

if problems:
print('\nWorkflow trigger check failed:\n')
for p in problems:
print(' ' + p)
return 1
print('every push and pull_request trigger can match an existing branch')
return 0


if __name__ == '__main__':
sys.exit(main(sys.argv))
14 changes: 7 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Build and Test

on:
push:
branches: [ main ]
branches: [ master ]
pull_request:
branches: [ main ]
branches: [ master ]

jobs:
build:
Expand Down Expand Up @@ -68,7 +68,7 @@ jobs:
shell_tests: "ON"

- name: rel-clang-macos
os: macos-11
os: macos-latest
compiler: clang
scanbuild: ""
cflags: "-O3 -Wall -Wextra -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wno-deprecated -Wwrite-strings"
Expand All @@ -86,7 +86,7 @@ jobs:
AGENT_BUILDDIRECTORY: ${{ github.workspace }}/NIFTIworkspace
BUILD_SOURCESDIRECTORY: ${{ github.workspace }}
SYSTEM_PULLREQUEST_SOURCEBRANCH: ${{ github.head_ref }}
BUILD_SOURCEBRANCHNAME: ${{ github.head_ref }}
BUILD_SOURCEBRANCHNAME: ${{ github.head_ref || github.ref_name }}
BUILD_BUILDID: ${{ github.run_id }}
SYSTEM_PULLREQUEST_PULLREQUESTNUMBER: ${{ github.event.pull_request.number }}
CTEST_SCRIPT_DIRECTORY: ${{ github.workspace }}/cmake
Expand All @@ -98,12 +98,12 @@ jobs:
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y cmake valgrind help2man
sudo apt-get install -y cmake valgrind help2man clang-tools

- name: Install Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew install cmake sed help2man
brew install cmake gnu-sed help2man

- name: Set Compiler and Flags
run: |
Expand Down Expand Up @@ -131,5 +131,5 @@ jobs:

- name: Run CTest
run: |
eval $SCANBUILD_EXE ctest -S ${CTEST_SCRIPT_DIRECTORY}/travis_dashboard.cmake -V -j 4
eval $SCANBUILD_EXE ctest -S ${CTEST_SCRIPT_DIRECTORY}/github_dashboard.cmake -V -j 4

Loading
Loading