From 1ea07d6e0a1263aaa549a8253db619e87af1da34 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 6 Aug 2026 22:52:45 +0200 Subject: [PATCH] CI: filter all .[ch] files before doing typo checks The filter only leaves comments and strings in all files using .c or .h extensions. It is important that the filter runs after all the other checks are done on these files as this process destroys the files. Fixes #22508 Closes #22509 --- .github/scripts/c-comments | 191 +++++++++++++++++++++++++++++++++ .github/scripts/c-strip | 7 ++ .github/workflows/checksrc.yml | 32 +++--- 3 files changed, 216 insertions(+), 14 deletions(-) create mode 100755 .github/scripts/c-comments create mode 100755 .github/scripts/c-strip diff --git a/.github/scripts/c-comments b/.github/scripts/c-comments new file mode 100755 index 0000000000..f8c30cf0f9 --- /dev/null +++ b/.github/scripts/c-comments @@ -0,0 +1,191 @@ +#!/usr/bin/env perl +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +# Output all the C comments and double-quoted strings in the given source +# files. All other contents should be blanked out. Output the text at the same +# horizontal position as in the original file. +# +# Ignores strings for the preprocessor. +# +## States +# +# 0 - default, initial state +# 1 - there was a slash +# 2 - quoted string +# 3 - // comment +# 4 - /* comment +# 5 - asterisk found within a /* comment +# 6 - #include line +# 7 - backslash in a string +# 8 - backslash in plain code +# 9 - single quote in plain code +# +## Flags +# +# 1 - include preprocessor line, ignore strings + +sub scanline { + my ($col, $state, $flags, $l) = @_; + my $line; + + if($state == 3) { + # // ended on the prev line, go back to init + $state = 0; + } + + if(($state == 0) && ($l =~ /^ *\# *include/)) { + # preprocessor include line + $flags |= 1; + } + else { + # not preprocessor + $flags &= ~1; + } + + my @c = split(//, $l); + + # state machine this line + for my $c (@c) { + if($state == 1) { + # we had a slash + if($c eq "/") { + # // confirmed, the rest of the line is a comment + $line .= "//"; + $state = 3; + } + elsif($c eq "*") { + # /* confirmed + $state = 4; + $line .= "/*"; + } + else { + # back to normal + $line .= " "; + $state = 0; + } + } + elsif($state == 2) { + # a string + if($c eq "\\") { + $line .= "\\"; + $state = 7; + } + elsif($c eq "\"") { + # end of the string + $line .= "\""; + $state = 0; + } + else { + $line .= $c; + } + } + elsif($state == 3) { + # a // comment + $line .= $c; + } + elsif($state == 4) { + # an ongoing /* comment + if($c eq "*") { + # could a comment close + $state = 5; + } + else { + $line .= $c; + } + } + elsif($state == 5) { + if($c eq "/") { + # a /* */ comment ended here */ + $line .= "*/"; + $state = 0; + } + else { + # the /* comment continues + $line .= "*$c"; + $state = 4; + } + } + elsif($state == 7) { + # the prev was a backslash in a string + $line .= $c; + # switch back to normal string + $state = 2; + } + elsif($state == 8) { + # the prev was a backslash in code + if($c eq "\n") { + $line .= $c; + } + else { + #$line .= " "; + } + # switch back to plain code + $state = 0; + } + elsif($state == 9) { + # the prev was a single quote in code + if($c eq "\n") { + $line .= $c; + # switch back to plain code + $state = 0; + } + elsif($c eq "\\") { + # a backslash followed the quote + $line .= " "; + $state = 8; + } + else { + # switch back to plain code + $state = 0; + } + } + else { + if($c eq "\\") { + # got a backslash + $line .= " "; + $state = 8 + } + elsif($c eq "\'") { + # got a single quote + $line .= " "; + $state = 9 + } + if($c eq "/") { + $state = 1; # got a slash + } + elsif(($c eq "\"") && !($flags & 1)) { + # start of a string, not within a preprocessor line + $line .= "\""; + $state = 2; + } + elsif($c eq "\n") { + $line .= "\n"; + } + else { + $line .= " "; + } + } + } + # strip trailing space + $line =~ s/( +)\n/\n/; + return $state, $flags, $line; +} + +sub strip { + my ($f) = @_; + my $state = 0; + my $flags = 0; + open(F, "<$f") || die "can't open $f"; + while() { + my $l = $_; + ($state, $flags, $line) = scanline(0, $state, $flags, $l); + print "$line"; + } + close(F); +} + +for my $f (@ARGV) { + strip($f); +} diff --git a/.github/scripts/c-strip b/.github/scripts/c-strip new file mode 100755 index 0000000000..6b5cfbf863 --- /dev/null +++ b/.github/scripts/c-strip @@ -0,0 +1,7 @@ +#!/bin/sh +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +./.github/scripts/c-comments "$1" > temp +mv temp "$1" diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index a5d18356db..f6a2ec84e5 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -66,20 +66,6 @@ jobs: source ~/venv/bin/activate reuse lint - - name: 'codespell' - run: | - source ~/venv/bin/activate - codespell --version - .github/scripts/codespell.sh - - - name: 'typos' - timeout-minutes: 2 - run: | - /home/linuxbrew/.linuxbrew/bin/brew install typos-cli - eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - typos --version - .github/scripts/typos.sh - - name: 'cmakelint' run: | source ~/venv/bin/activate @@ -96,6 +82,24 @@ jobs: source ~/venv/bin/activate scripts/pythonlint.sh + - name: filter off code from all C and H files + run: | + git ls-files -z '**.[ch]' | xargs -0 -r -n1 ./.github/scripts/c-strip + + - name: 'codespell' + run: | + source ~/venv/bin/activate + codespell --version + .github/scripts/codespell.sh + + - name: 'typos' + timeout-minutes: 2 + run: | + /home/linuxbrew/.linuxbrew/bin/brew install typos-cli + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + typos --version + .github/scripts/typos.sh + pytype: name: 'pytype' runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12