diff options
Diffstat (limited to 'gdb/contrib')
| -rw-r--r-- | gdb/contrib/ari/create-web-ari-in-src.sh | 4 | ||||
| -rwxr-xr-x | gdb/contrib/ari/gdb_ari.sh | 6 | ||||
| -rw-r--r-- | gdb/contrib/ari/gdb_find.sh | 2 | ||||
| -rw-r--r-- | gdb/contrib/ari/update-web-ari.sh | 10 | ||||
| -rwxr-xr-x | gdb/contrib/cc-with-tweaks.sh | 7 | ||||
| -rwxr-xr-x[-rw-r--r--] | gdb/contrib/check-gnu-style-pre-commit.sh (renamed from gdb/contrib/common-misspellings.txt) | 38 | ||||
| -rwxr-xr-x | gdb/contrib/check-whitespace-pre-commit.py | 52 | ||||
| -rw-r--r-- | gdb/contrib/codespell-dictionary.txt | 1 | ||||
| -rw-r--r-- | gdb/contrib/codespell-ignore-words.txt | 2 | ||||
| -rwxr-xr-x | gdb/contrib/codespell-log.sh | 95 | ||||
| -rwxr-xr-x | gdb/contrib/dwarf-to-dwarf-assembler.py | 654 | ||||
| -rw-r--r-- | gdb/contrib/expect-read1.c | 2 | ||||
| -rwxr-xr-x | gdb/contrib/expect-read1.sh | 2 | ||||
| -rwxr-xr-x | gdb/contrib/gdb-add-index.sh | 2 | ||||
| -rwxr-xr-x | gdb/contrib/license-check-new-files.sh | 149 | ||||
| -rw-r--r-- | gdb/contrib/setup.cfg | 19 | ||||
| -rwxr-xr-x | gdb/contrib/spellcheck.sh | 536 | ||||
| -rw-r--r-- | gdb/contrib/test_pubnames_and_indexes.py | 2 | ||||
| -rwxr-xr-x | gdb/contrib/words.sh | 2 |
19 files changed, 1018 insertions, 567 deletions
diff --git a/gdb/contrib/ari/create-web-ari-in-src.sh b/gdb/contrib/ari/create-web-ari-in-src.sh index 64d3c73..b479ce5 100644 --- a/gdb/contrib/ari/create-web-ari-in-src.sh +++ b/gdb/contrib/ari/create-web-ari-in-src.sh @@ -2,7 +2,7 @@ # GDB script to create web ARI page directly from within gdb/ari directory. # -# Copyright (C) 2012-2024 Free Software Foundation, Inc. +# Copyright (C) 2012-2025 Free Software Foundation, Inc. # # This file is part of GDB. # @@ -50,7 +50,7 @@ if [ -z "${tempdir}" ] ; then fi fi -# Default location of generate index.hmtl web page. +# Default location of generated index.html web page. if [ -z "${webdir}" ] ; then # Use 'branch' subdir name if Tag contains branch if [ -f "${srcdir}/gdb/CVS/Tag" ] ; then diff --git a/gdb/contrib/ari/gdb_ari.sh b/gdb/contrib/ari/gdb_ari.sh index 5ed7d61..e10bbe0 100755 --- a/gdb/contrib/ari/gdb_ari.sh +++ b/gdb/contrib/ari/gdb_ari.sh @@ -2,7 +2,7 @@ # GDB script to list of problems using awk. # -# Copyright (C) 2002-2024 Free Software Foundation, Inc. +# Copyright (C) 2002-2025 Free Software Foundation, Inc. # # This file is part of GDB. # @@ -60,7 +60,7 @@ Options: -Werror Treat all problems as errors. -Wall Report all problems. -Wari Report problems that should be fixed in new code. - -WCATEGORY Report problems in the specifed category. The category + -WCATEGORY Report problems in the specified category. The category can be prefixed with "no-". Valid categories are: ${all} EOF @@ -159,7 +159,7 @@ BEGIN { PWD = "'`pwd`'" } -# Print the error message for BUG. Append SUPLEMENT if non-empty. +# Print the error message for BUG. Append SUPPLEMENT if non-empty. function print_bug(file,line,prefix,category,bug,doc,supplement, suffix,idx) { if (print_idx) { idx = bug ": " diff --git a/gdb/contrib/ari/gdb_find.sh b/gdb/contrib/ari/gdb_find.sh index f0aa6d1..b6bd3f9 100644 --- a/gdb/contrib/ari/gdb_find.sh +++ b/gdb/contrib/ari/gdb_find.sh @@ -2,7 +2,7 @@ # GDB script to create list of files to check using gdb_ari.sh. # -# Copyright (C) 2003-2024 Free Software Foundation, Inc. +# Copyright (C) 2003-2025 Free Software Foundation, Inc. # # This file is part of GDB. # diff --git a/gdb/contrib/ari/update-web-ari.sh b/gdb/contrib/ari/update-web-ari.sh index 75ab029..117c0ba 100644 --- a/gdb/contrib/ari/update-web-ari.sh +++ b/gdb/contrib/ari/update-web-ari.sh @@ -2,7 +2,7 @@ # GDB script to create GDB ARI web page. # -# Copyright (C) 2001-2024 Free Software Foundation, Inc. +# Copyright (C) 2001-2025 Free Software Foundation, Inc. # # This file is part of GDB. # @@ -176,7 +176,7 @@ fi # THIS HAS SUFFERED BIT ROT if ${check_indent_p} && test -d "${srcdir}" then - printf "Analizing file indentation:" 1>&2 + printf "Analyzing file indentation:" 1>&2 ( cd "${srcdir}" && /bin/sh ${aridir}/gdb_find.sh ${project} | while read f do if /bin/sh ${aridir}/gdb_indent.sh < ${f} 2>/dev/null | cmp -s - ${f} @@ -550,7 +550,7 @@ function print_heading (nb_file, where, bug_i) { for (bug_i = 1; bug_i <= nr_bug; bug_i++) { bug = i2bug[bug_i]; printf "<th>" - # The title names are offset by one. Otherwize, when the browser + # The title names are offset by one. Otherwise, when the browser # jumps to the name it leaves out half the relevant column. #printf "<a name=\",%s\"> </a>", bug printf "<a name=\",%s\"> </a>", i2bug[bug_i-1] @@ -762,7 +762,7 @@ END { print "bugs/oks: " bugs "/" oks print bugs "/ (" oks "+" legacy "+" deprecated ")" } - # This value should be as low as possible + # This value should be as low as possible print bugs / ( oks + legacy + deprecated ) } ' ${wwwdir}/ari.doc` @@ -851,7 +851,7 @@ EOF print_toc 0 -1 deprecate Deprecate <<EOF Mechanisms that are a candidate for being made obsolete. Once core GDB no longer depends on these mechanisms and/or there is a -replacement available, these mechanims can be deprecated (adding the +replacement available, these mechanisms can be deprecated (adding the deprecated prefix) obsoleted (put into category obsolete) or deleted. See obsolete and deprecated. EOF diff --git a/gdb/contrib/cc-with-tweaks.sh b/gdb/contrib/cc-with-tweaks.sh index 4214b92..930ba5c 100755 --- a/gdb/contrib/cc-with-tweaks.sh +++ b/gdb/contrib/cc-with-tweaks.sh @@ -2,7 +2,7 @@ # Wrapper around gcc to tweak the output in various ways when running # the testsuite. -# Copyright (C) 2010-2024 Free Software Foundation, Inc. +# Copyright (C) 2010-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -42,6 +42,7 @@ # -Z invoke objcopy --compress-debug-sections # -z compress using dwz # -m compress using dwz -m +# -5 compress using dwz -m -5 # -i make an index (.gdb_index) # -c make an index (currently .gdb_index) in a cache dir # -n make a dwarf5 index (.debug_names) @@ -88,6 +89,7 @@ want_index=false index_options="" want_index_cache=false want_dwz=false +dwz_5flag= want_multi=false want_dwp=false want_objcopy_compress=false @@ -101,6 +103,7 @@ while [ $# -gt 0 ]; do -n) want_index=true; index_options=-dwarf-5;; -c) want_index_cache=true ;; -m) want_multi=true ;; + -5) want_multi=true; dwz_5flag=-5 ;; -p) want_dwp=true ;; -l) want_gnu_debuglink=true ;; *) break ;; @@ -269,7 +272,7 @@ elif [ "$want_multi" = true ]; then rm -f "$dwz_file" cp "$output_file" "${output_file}.alt" - $DWZ -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null + $DWZ $dwz_5flag -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null rm -f "${output_file}.alt" # Validate dwz's work by checking if the expected output file exists. diff --git a/gdb/contrib/common-misspellings.txt b/gdb/contrib/check-gnu-style-pre-commit.sh index 5772f66..ab4b5e7 100644..100755 --- a/gdb/contrib/common-misspellings.txt +++ b/gdb/contrib/check-gnu-style-pre-commit.sh @@ -1,4 +1,6 @@ -# Copyright (C) 2024 Free Software Foundation, Inc. +#!/bin/sh + +# Copyright (C) 2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -12,19 +14,29 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# This file contains additions to and overrides for -# wikipedia-common-misspellings.txt. +set -e + +scriptdir=$(cd "$(dirname "$0")" || exit 1; pwd -P) + +tmp="" + +cleanup() +{ + if [ "$tmp" != "" ]; then + rm -f "$tmp" + fi +} -# Common spelling mistakes. +# Schedule cleanup. +trap cleanup EXIT -inbetween->between, in between, in-between -sofar->so far -doens't->doesn't -behavour->behavior -behaviour->behavior -arrithemetic->arithmetic -electricaly->electrically +# Get temporary file. +tmp=$(mktemp) -# Identity rules. +# Generate patch. +git diff --staged "$@" \ + > "$tmp" -thru->thru +# Verify patch. Ignore exit status. +"$scriptdir"/../../contrib/check_GNU_style.py "$tmp" \ + || true diff --git a/gdb/contrib/check-whitespace-pre-commit.py b/gdb/contrib/check-whitespace-pre-commit.py new file mode 100755 index 0000000..9d82f77 --- /dev/null +++ b/gdb/contrib/check-whitespace-pre-commit.py @@ -0,0 +1,52 @@ +#! /usr/bin/env python3 + +# Copyright (C) 2025 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import re +import subprocess +import sys + +re_clean = re.compile( + "(^(gdb/testsuite/|gdbsupport/|gdbserver/)|[.](m4|ac|def|[chly])$|NEWS)" +) +re_clean_exclude = re.compile("^(gdb/symfile.c|gdb/cli/cli-cmds.c)$") + +clean = [] +other = [] +for f in sys.argv[1:]: + m = re_clean.search(f) + if m and re_clean_exclude.search(f): + m = None + if m: + clean.append(f) + else: + other.append(f) + +if other: + cmd = ["git", "--no-pager", "diff", "--staged", "--check"] + other + res = subprocess.run(cmd) + if res.returncode != 0: + sys.exit(res.returncode) + +if clean: + cmd = ["git", "hash-object", "-t", "tree", "/dev/null"] + res = subprocess.run(cmd, capture_output=True, text=True) + if res.returncode != 0: + sys.exit(res.returncode) + null_tree = res.stdout.rstrip("\n") + + cmd = ["git", "diff-index", "--cached", "--check", null_tree] + clean + res = subprocess.run(cmd) + sys.exit(res.returncode) diff --git a/gdb/contrib/codespell-dictionary.txt b/gdb/contrib/codespell-dictionary.txt new file mode 100644 index 0000000..09bc309 --- /dev/null +++ b/gdb/contrib/codespell-dictionary.txt @@ -0,0 +1 @@ +gdbsever->gdbserver diff --git a/gdb/contrib/codespell-ignore-words.txt b/gdb/contrib/codespell-ignore-words.txt index 2d6e13a..c07df0c 100644 --- a/gdb/contrib/codespell-ignore-words.txt +++ b/gdb/contrib/codespell-ignore-words.txt @@ -1,2 +1,4 @@ configury SME +Synopsys +aranges diff --git a/gdb/contrib/codespell-log.sh b/gdb/contrib/codespell-log.sh new file mode 100755 index 0000000..10780f8 --- /dev/null +++ b/gdb/contrib/codespell-log.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Copyright (C) 2025 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Script to be used as pre-commit commit-msg hook to spell-check the commit +# log using codespell. +# +# Using codespell directly as a pre-commit commit-msg hook has the drawback +# that: +# - if codespell fails, the commit fails +# - if the commit log mentions a typo correction, it'll require a +# codespell:ignore annotation. +# +# This script works around these problems by treating codespell output as a +# hint, and ignoring codespell exit status. +# +# Implementation note: rather than using codespell directly, this script uses +# pre-commit to call codespell, because it allows us to control the codespell +# version that is used. + +# Exit on error. +set -e + +# Initialize temporary file names. +cfg="" +output="" + +cleanup() +{ + for f in "$cfg" "$output"; do + if [ "$f" != "" ]; then + rm -f "$f" + fi + done +} + +# Schedule cleanup. +trap cleanup EXIT + +# Create temporary files. +cfg=$(mktemp) +output=$(mktemp) + +gen_cfg () +{ + cat > "$1" <<EOF +repos: +- repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + name: codespell-log-internal + stages: [manual] + args: [--config, gdb/contrib/setup.cfg] +EOF +} + +# Generate pre-commit configuration file. +gen_cfg "$cfg" + +# Setup pre-commit command to run. +cmd=(pre-commit \ + run \ + -c "$cfg" \ + codespell \ + --hook-stage manual \ + --files "$@") + +# Run pre-commit command. +if "${cmd[@]}" \ + > "$output" \ + 2>&1; then + # Command succeeded quietly, we're done. + exit 0 +fi + +# Command failed quietly, now show the output. +# +# Simply doing "cat $output" doesn't produce colored output, so we just +# run the command again, that should be fast enough. +# +# Ignore codespell exit status. +"${cmd[@]}" || true diff --git a/gdb/contrib/dwarf-to-dwarf-assembler.py b/gdb/contrib/dwarf-to-dwarf-assembler.py new file mode 100755 index 0000000..37bd1ba --- /dev/null +++ b/gdb/contrib/dwarf-to-dwarf-assembler.py @@ -0,0 +1,654 @@ +#!/usr/bin/env python3 +# pyright: strict + +# Copyright 2024, 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Due to the pyelftools dependency, this script requires Python version +# 3.10 or greater to run. + +"""A utility to convert ELF files with DWARF info to Dwarf::assemble code. + +Usage: + python ./asm_to_dwarf_assembler.py <path/to/elf/file> + +Dependencies: + Python >= 3.10 + pyelftools >= 0.31 + +Notes: +- Line tables are not currently supported. +- Non-contiguous subprograms are not currently supported. +- If you want to use $srcfile or similar, you must edit the references to the + file name manually, including DW_AT_name attributes on compile units. +- If run with binaries generated by make check-gdb, it may include an + additional compile_unit before and after the actual compile units. This is + an artifact of the normal compilation process, as these CUs are indeed in + the generated DWARF in some cases. +""" + +import errno +import re +import sys +from copy import copy +from functools import cache +from io import BytesIO, IOBase +from logging import getLogger +from typing import Annotated, Optional + +from elftools.dwarf.compileunit import CompileUnit as RawCompileUnit +from elftools.dwarf.die import DIE as RawDIE +from elftools.dwarf.die import AttributeValue +from elftools.dwarf.enums import ENUM_DW_ATE, ENUM_DW_LANG +from elftools.elf.elffile import ELFFile + +logger = getLogger(__file__) + + +# While these aren't supported, their detection is important for replacing them +# with SPECIAL_expr and for writing the placeholder {MANUAL} expr list. +EXPR_ATTRIBUTE_FORMS = [ + "DW_FORM_exprloc", + "DW_FORM_block", + "DW_FORM_block1", + "DW_FORM_block2", + "DW_FORM_block4", +] + +# Map from language number to name. +LANG_NAME = {v: k for k, v in ENUM_DW_LANG.items()} +# Map from encoding number to name. +ATE_NAME = {v: k for k, v in ENUM_DW_ATE.items()} + +# Workaround for my editor not to freak out over unclosed braces. +lbrace, rbrace = "{", "}" + + +@cache +def get_indent_str(indent_count: int) -> str: + """Get whitespace string to prepend to another for indenting.""" + indent = (indent_count // 2) * "\t" + if indent_count % 2 == 1: + indent += " " + return indent + + +def indent(line: str, indent_count: int) -> str: + """Indent line by indent_count levels.""" + return get_indent_str(indent_count) + line + + +def labelify_str(s: str) -> str: + """Make s appropriate for a label name.""" + # Replace "*" with the literal word "ptr". + s = s.replace("*", "ptr") + + # Replace any non-"word" characters by "_". + s = re.sub(r"\W", "_", s) + + # Remove consecutive "_"s. + s = re.sub(r"__+", "_", s) + + return s + + +class DWARFAttribute: + """Storage unit for a single DWARF attribute. + + All its values are strings that are usually passed on + directly to format. The exceptions to this are attributes + with int values with DW_FORM_ref4 or DW_FORM_ref_addr form. + Their values are interpreted as the global offset of the DIE + being referenced, which are looked up dynamically to fetch + their labels. + """ + + def __init__( + self, + die_offset: int, + name: str, + value: str | bytes | int | bool, + form=None, + ): + self.die_offset = die_offset + self.name = name + self.value = value + self.form = form + + def _format_expr_value(self) -> str: + self.form = "SPECIAL_expr" + return "{ MANUAL: Fill expr list }" + + def _needs_escaping(self, str_value: str) -> bool: + charset = set(str_value) + return bool(charset.intersection({"{", "}", " ", "\t"})) + + def _format_str(self, str_value: str) -> str: + if self._needs_escaping(str_value): + escaped_str = str(str_value) + # Replace single escape (which is itself escaped because of regex) + # with a double escape (which doesn't mean anything to regex so + # it doesn't need escaping). + escaped_str = re.sub(r"\\", r"\\", escaped_str) + escaped_str = re.sub("([{}])", r"\\\1", escaped_str) + return "{" + escaped_str + "}" + else: + return str_value + + def _format_value( + self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0 + ) -> str: + if self.form in EXPR_ATTRIBUTE_FORMS: + return self._format_expr_value() + elif isinstance(self.value, bool): + return str(int(self.value)) + elif isinstance(self.value, int): + if self.form == "DW_FORM_ref4": + # ref4-style referencing label. + die = offset_die_lookup[self.value] + return ":$" + die.tcl_label + elif self.form == "DW_FORM_ref_addr": + # ref_addr-style referencing label. + die = offset_die_lookup[self.value] + return "%$" + die.tcl_label + else: + return str(self.value) + elif isinstance(self.value, bytes): + return self._format_str(self.value.decode("ascii")) + elif isinstance(self.value, str): + return self._format_str(self.value) + else: + raise NotImplementedError(f"Unknown data type: {type(self.value)}") + + def format( + self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0 + ) -> str: + """Format the attribute in the form {name value form}. + + If form is DW_FORM_exprloc or DW_FORM_block, see next section on + DWARFOperations. + + If it isn't, value is formatted as follows: + If bool, use "1" if True, "0" if False. + If int: + If form is DW_FORM_ref4, use ":$label" where label is the + tcl_label of the DWARFDIE at offset "value". + If form is DW_FORM_ref_addr, use "%$label" where label is + the tcl_label of the DWARFDIE at offset "value". + Else, use value directly. + If bytes, use value.decode("ascii") + If str, use value directly. + Any other type results in a NotImplementedError being raised. + + Regarding DW_FORM_exprloc and DW_FORM_block: + The form is replaced with SPECIAL_expr. + The entries in the value are interpreted and decoded using the + dwarf_operations dictionary, and replaced with their names where + applicable. + """ + s = "" + if isinstance(self.name, int): + s += "DW_AT_" + hex(self.name) + else: + s += self.name + s += " " + + if self.name == "DW_AT_language" and isinstance(self.value, int): + s += "@" + LANG_NAME[self.value] + elif self.name == "DW_AT_encoding" and isinstance(self.value, int): + s += "@" + ATE_NAME[self.value] + else: + s += self._format_value(offset_die_lookup) + + # Only explicitly state form if it's not a reference. + if self.form not in [None, "DW_FORM_ref4", "DW_FORM_ref_addr"]: + s += " " + self.form + + return indent(s, indent_count) + + +class DWARFDIE: + """This script's parsed version of a RawDIE.""" + + def __init__( + self, + offset: int, + tag: str, + attrs: dict[str, DWARFAttribute], + tcl_label: Optional[str] = None, + ): + self.offset: Annotated[int, "Global offset of the DIE."] = offset + self.tag: Annotated[str, "DWARF tag for this DIE."] = tag + self.attrs: Annotated[ + dict[str, DWARFAttribute], "Dict of attributes for this DIE." + ] = copy(attrs) + self.children: Annotated[list[DWARFDIE], "List of child DIEs of this DIE."] = [] + self.tcl_label: Annotated[ + str, + "Label used by the Tcl code to reference this DIE, if any. These " + 'take the form of "label: " before the actual DIE definition.', + ] = tcl_label + + def format_lines( + self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0 + ) -> list[str]: + """Get the list of lines that represent this DIE in Dwarf assembler.""" + die_lines = [] + + # Prepend label to first line, if it's set. + if self.tcl_label: + first_line_start = self.tcl_label + ": " + else: + first_line_start = "" + + # First line, including label. + first_line = indent(first_line_start + self.tag + " " + lbrace, indent_count) + die_lines.append(first_line) + + # Format attributes, if any. + if self.attrs: + for attr_name, attr in self.attrs.items(): + attr_line = attr.format( + offset_die_lookup, indent_count=indent_count + 1 + ) + die_lines.append(attr_line) + die_lines.append(indent(rbrace, indent_count)) + else: + # Don't create a new line, just append and immediately close the + # brace on the last line. + die_lines[-1] += rbrace + + # Format children, if any. + if self.children: + # Only open a new brace if there are any children for the + # current DIE. + die_lines[-1] += " " + lbrace + for child in self.children: + child_lines = child.format_lines( + offset_die_lookup, indent_count=indent_count + 1 + ) + die_lines.extend(child_lines) + die_lines.append(indent(rbrace, indent_count)) + + return die_lines + + def format( + self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0 + ) -> str: + """Join result from format_lines into a single str.""" + return "\n".join(self.format_lines(offset_die_lookup, indent_count)) + + def name(self) -> Optional[str]: + """Get DW_AT_name (if present) decoded as ASCII.""" + raw_value = self.attrs.get("DW_AT_name") + if raw_value is None: + return None + else: + return raw_value.value.decode("ascii") + + def type_name(self) -> str: + """Name of Dwarf tag, with the "DW_TAG_" prefix removed.""" + return re.sub("DW_TAG_", "", self.tag) + + +class DWARFCompileUnit(DWARFDIE): + """Wrapper subclass for CU DIEs. + + This is necessary due to the special format CUs take in Dwarf::assemble. + + Instead of simply: + DW_TAG_compile_unit { + <attributes> + } { + <children> + } + + CUs are formatted as: + cu { <cu_special_vars> } { + DW_TAG_compile_unit { + <attributes> + } { + <children> + } + } + """ + + # Default value for parameter is_64 defined in dwarf.exp line 1553. + # This value is converted to 0/1 automatically when emitting + # Dwarf::assemble code. + default_is_64 = False + + # Default value for parameter dwarf_version defined in dwarf.exp line 1552. + default_dwarf_version = 4 + + # Default value for parameter is_fission defined in dwarf.exp line 1556. + # Currently not implemented, see comment below. + # default_is_fission = False + + # Tag that signifies a DIE is a compile unit. + compile_unit_tag = "DW_TAG_compile_unit" + + def __init__( + self, + raw_die: RawDIE, + raw_cu: RawCompileUnit, + attrs: dict[str, DWARFAttribute], + ): + """Initialize additional instance variables for CU encoding. + + The additional instance variables are: + - is_64_bit: bool + Whether this CU is 64 bit or not. + - dwarf_version: int + default DWARFCompileUnit.default_dwarf_version + Version of DWARF this CU is using. + - addr_size: Optional[int] + default None + Size of an address in bytes. + + These variables are used to configure the first parameter of the cu + proc (which contains calls to the compile_unit proc in the body of + Dwarf::assemble). + """ + super().__init__(raw_die.offset, DWARFCompileUnit.compile_unit_tag, attrs) + self.raw_cu = raw_cu + self.dwarf_version: int = raw_cu.header.get( + "version", DWARFCompileUnit.default_dwarf_version + ) + self.addr_size: Optional[int] = raw_cu.header.get("address_size") + self.is_64_bit: bool = raw_cu.dwarf_format() == 64 + + # Fission is not currently implemented because I don't know where to + # fetch this information from. + # self.is_fission: bool = self.default_is_fission + + # CU labels are not currently implemented because I haven't found where + # pyelftools exposes this information. + # self.cu_label: Optional[str] = None + + def format_lines( + self, + offset_die_lookup: dict[int, DWARFDIE], + indent_count: int = 0, + ) -> list[str]: + lines = [] + lines.append(self._get_header(indent_count)) + inner_lines = super().format_lines(offset_die_lookup, indent_count + 1) + lines += inner_lines + lines.append(indent(rbrace, indent_count)) + return lines + + def _get_header(self, indent_count: int = 0) -> str: + """Assemble the first line of the surrounding 'cu {} {}' proc call.""" + header = indent("cu " + lbrace, indent_count) + cu_params = [] + + if self.is_64_bit != DWARFCompileUnit.default_is_64: + # Convert from True/False to 1/0. + param_value = int(self.is_64_bit) + cu_params += ["is_64", str(param_value)] + + if self.dwarf_version != DWARFCompileUnit.default_dwarf_version: + cu_params += ["version", str(self.dwarf_version)] + + if self.addr_size is not None: + cu_params += ["addr_size", str(self.addr_size)] + + # Fission is not currently implemented, see comment above. + # if self.is_fission != DWARFCompileUnit.default_is_fission: + # # Same as is_64_bit conversion, True/False -> 1/0. + # param_value = int(self.is_fission) + # cu_params += ["fission", str(param_value)] + + # CU labels are not currently implemented, see commend above. + # if self.cu_label is not None: + # cu_params += ["label", self.cu_label] + + if cu_params: + header += " ".join(cu_params) + + header += rbrace + " " + lbrace + return header + + +class DWARFParser: + """Converter from pyelftools's DWARF representation to this script's.""" + + def __init__(self, elf_file: IOBase): + """Init parser with file opened in binary mode. + + File can be closed after this function is called. + """ + self.raw_data = BytesIO(elf_file.read()) + self.elf_data = ELFFile(self.raw_data) + self.dwarf_info = self.elf_data.get_dwarf_info() + self.offset_to_die: dict[int, DWARFDIE] = {} + self.label_to_die: dict[str, DWARFDIE] = {} + self.referenced_offsets: Annotated[ + set[int], "The set of all offsets that were referenced by some DIE." + ] = set() + self.raw_cu_list: list[RawCompileUnit] = [] + self.top_level_dies: list[DWARFDIE] = [] + self.subprograms: list[DWARFDIE] = [] + self.taken_labels: set[str] = set() + + self._read_all_cus() + self._create_necessary_labels() + + def _read_all_cus(self): + """Populate self.raw_cu_list with all CUs in self.dwarf_info.""" + for cu in self.dwarf_info.iter_CUs(): + self._read_cu(cu) + + def _read_cu(self, raw_cu: RawCompileUnit): + """Read a compile_unit into self.cu_list.""" + self.raw_cu_list.append(raw_cu) + for raw_die in raw_cu.iter_DIEs(): + if not raw_die.is_null(): + self._parse_die(raw_cu, raw_die) + + def _parse_die(self, die_cu: RawCompileUnit, raw_die: RawDIE) -> DWARFDIE: + """Process a single DIE and add it to offset_to_die. + + Look for DW_FORM_ref4 and DWD_FORM_ref_addr form attributes and replace + them with the global offset of the referenced DIE, and adding the + referenced DIE to a set. This will be used later to assign and use + labels only to DIEs that need it. + + In case the DIE is a top-level DIE, add it to self.top_level_dies. + + In case the DIE is a subprogram, add it to self.subprograms and call + self._use_vars_for_low_and_high_pc_attr with it. + """ + processed_attrs = {} + attr_value: AttributeValue + for attr_name, attr_value in raw_die.attributes.items(): + actual_value = attr_value.value + if attr_value.form in ("DW_FORM_ref4", "DW_FORM_ref_addr"): + referenced_die = raw_die.get_DIE_from_attribute(attr_name) + actual_value = referenced_die.offset + self.referenced_offsets.add(referenced_die.offset) + + processed_attrs[attr_name] = DWARFAttribute( + raw_die.offset, attr_name, actual_value, attr_value.form + ) + + if raw_die.tag == DWARFCompileUnit.compile_unit_tag: + processed_die = DWARFCompileUnit(raw_die, die_cu, processed_attrs) + else: + processed_die = DWARFDIE(raw_die.offset, raw_die.tag, processed_attrs, None) + + if raw_die.get_parent() is None: + # Top level DIE + self.top_level_dies.append(processed_die) + else: + # Setting the parent here assumes the parent was already processed + # prior to this DIE being found. + # As far as I'm aware, this is always true in DWARF. + processed_parent = self.offset_to_die[raw_die.get_parent().offset] + processed_parent.children.append(processed_die) + + if processed_die.tag == "DW_TAG_subprogram": + self.subprograms.append(processed_die) + self._use_vars_for_low_and_high_pc_attr(processed_die) + + self.offset_to_die[processed_die.offset] = processed_die + return processed_die + + def _create_necessary_labels(self): + """Create labels to DIEs that were referenced by others.""" + for offset in self.referenced_offsets: + die = self.offset_to_die[offset] + self._create_label_for_die(die) + + def _use_vars_for_low_and_high_pc_attr(self, subprogram: DWARFDIE) -> None: + """Replace existing PC attributes with Tcl variables. + + If DW_AT_low_pc exists for this DIE, replace it with accessing the + variable whose name is given by self.subprogram_start_var(subprogram). + + If DW_AT_high_pc exists for this DIE, replace it with accessing the + variable whose name is given by self.subprogram_end_var(subprogram). + """ + low_pc_attr_name = "DW_AT_low_pc" + if low_pc_attr_name in subprogram.attrs: + start = self.subprogram_start_var(subprogram) + subprogram.attrs[low_pc_attr_name].value = start + + high_pc_attr_name = "DW_AT_high_pc" + if high_pc_attr_name in subprogram.attrs: + end = self.subprogram_end_var(subprogram) + subprogram.attrs[high_pc_attr_name].value = end + + def _create_label_for_die(self, die: DWARFDIE) -> None: + """Set tcl_label to a unique string among other DIEs for this parser. + + As a first attempt, use labelify(die.name()). If the DIE does not have + a name, use labelify(die.type_name()). + + If the chosen initial label is already taken, try again appending "_2". + While the attempt is still taken, try again replacing it with "_3", then + "_4", and so on. + + This function also creates an entry on self.label_to_die. + """ + if die.tcl_label is not None: + return + + label = labelify_str(die.name() or die.type_name()) + + # Deduplicate label in case of collision + if label in self.taken_labels: + suffix_nr = 2 + + # Walrus operator to prevent writing the assembled label_suffix + # string literal twice. This could be rewritten by copying the + # string literal to the line after the end of the while loop, + # but I deemed it would be too frail in case one of them needs + # to be changed and the other is forgotten. + while (new_label := f"{label}_{suffix_nr}") in self.taken_labels: + suffix_nr += 1 + label = new_label + + die.tcl_label = label + self.label_to_die[label] = die + self.taken_labels.add(label) + + def subprogram_start_var(self, subprogram: DWARFDIE) -> str: + """Name of the Tcl variable that holds the low PC for a subprogram.""" + return f"${subprogram.name()}_start" + + def subprogram_end_var(self, subprogram: DWARFDIE) -> str: + """Name of the Tcl variable that holds the high PC for a subprogram.""" + return f"${subprogram.name()}_end" + + def all_labels(self) -> set[str]: + """Get a copy of the set of all labels known to the parser so far.""" + return copy(self.taken_labels) + + +class DWARFAssemblerGenerator: + """Class that generates Dwarf::assemble code out of a DWARFParser.""" + + def __init__(self, dwarf_parser: DWARFParser, output=sys.stdout): + self.dwarf_parser = dwarf_parser + self.output = output + + def emit(self, line: str, indent_count: int) -> None: + """Print a single line indented indent_count times to self.output. + + If line is empty, it will always print an empty line, even with nonzero + indent_count. + """ + if line: + line = get_indent_str(indent_count) + line + print(line, file=self.output) + + def generate_die(self, die: DWARFDIE, indent_count: int): + """Generate the lines that represent a DIE.""" + die_lines = die.format(self.dwarf_parser.offset_to_die, indent_count) + self.emit(die_lines, 0) + + def generate(self): + indent_count = 0 + + self.emit("Dwarf::assemble $asm_file {", indent_count) + + # Begin Dwarf::assemble body. + indent_count += 1 + self.emit("global srcdir subdir srcfile", indent_count) + + all_labels = self.dwarf_parser.all_labels() + if all_labels: + self.emit("declare_labels " + " ".join(all_labels), indent_count) + + self.emit("", 0) + for subprogram in self.dwarf_parser.subprograms: + self.emit(f"get_func_info {subprogram.name()}", indent_count) + + for die in self.dwarf_parser.top_level_dies: + self.generate_die(die, indent_count) + + # TODO: line table, if it's within scope (it probably isn't). + + # End Dwarf::assemble body. + indent_count -= 1 + self.emit(rbrace, indent_count) + + +def main(argv): + try: + filename = argv[1] + except IndexError: + print("Usage:", file=sys.stderr) + print("python ./dwarf-to-dwarf-assembler.py FILE", file=sys.stderr) + sys.exit(errno.EOPNOTSUPP) + + try: + with open(filename, "rb") as elf_file: + parser = DWARFParser(elf_file) + except Exception as e: + print( + "Error parsing ELF file. Does it contain DWARF information?", + file=sys.stderr, + ) + print(str(e), file=sys.stderr) + sys.exit(errno.ENODATA) + generator = DWARFAssemblerGenerator(parser) + generator.generate() + + +if __name__ == "__main__": + main(sys.argv) diff --git a/gdb/contrib/expect-read1.c b/gdb/contrib/expect-read1.c index 454ecda..bb7317b 100644 --- a/gdb/contrib/expect-read1.c +++ b/gdb/contrib/expect-read1.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013-2024 Free Software Foundation, Inc. +/* Copyright (C) 2013-2025 Free Software Foundation, Inc. This file is part of GDB. diff --git a/gdb/contrib/expect-read1.sh b/gdb/contrib/expect-read1.sh index 82c638c..e8589a2 100755 --- a/gdb/contrib/expect-read1.sh +++ b/gdb/contrib/expect-read1.sh @@ -1,7 +1,7 @@ #! /bin/sh # runtest wrapper to reliably reproduce racy incomplete reads in the testsuite. -# Copyright (C) 2013-2024 Free Software Foundation, Inc. +# Copyright (C) 2013-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh index b299f83..d2c523f 100755 --- a/gdb/contrib/gdb-add-index.sh +++ b/gdb/contrib/gdb-add-index.sh @@ -2,7 +2,7 @@ # Add a .gdb_index section to a file. -# Copyright (C) 2010-2024 Free Software Foundation, Inc. +# Copyright (C) 2010-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gdb/contrib/license-check-new-files.sh b/gdb/contrib/license-check-new-files.sh new file mode 100755 index 0000000..710afa1 --- /dev/null +++ b/gdb/contrib/license-check-new-files.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2025 Free Software Foundation, Inc. +# +# This file is part of GDB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This program requires the python modules GitPython (git) and scancode-toolkit. +# It builds a list of all the newly added files to the repository and scans +# each file for a license, printing it to the terminal. If "--skip" is used, +# it will only output non-"common" licenses, e.g., omitting "GPL-3.0-or-later". +# This makes it a little bit easier to detect any possible new licenses. +# +# Example: +# bash$ cd /path/to/binutils-gdb/gdb +# bash$ ./contrib/license-check-new-files.sh -s gdb-15-branchpoint gdb-16-branchpoint +# Scanning directories gdb*/... +# gdb/contrib/common-misspellings.txt: no longer in repo? +# gdb/contrib/spellcheck.sh: no longer in repo? +# gdbsupport/unordered_dense.h: MIT + +import os +import sys +import argparse +from pathlib import PurePath +from git import Repo +from scancode import api + +# A list of "common" licenses. If "--skip" is used, any file +# with a license in this list will be omitted from the output. +COMMON_LICENSES = ["GPL-2.0-or-later", "GPL-3.0-or-later"] + +# Default list of directories to scan. Default scans are limited to +# gdb-specific git directories because much of the rest of binutils-gdb +# is actually owned by other projects/packages. +DEFAULT_SCAN_DIRS = "gdb*" + + +# Get the commit object associated with the string commit CSTR +# from the git repository REPO. +# +# Returns the object or prints an error and exits. +def get_commit(repo, cstr): + try: + return repo.commit(cstr) + except: + print(f'unknown commit "{cstr}"') + sys.exit(2) + + +# Uses scancode-toolkit package to scan FILE's licenses. +# Returns the full license dict from scancode on success or +# propagates any exceptions. +def get_licenses_for_file(file): + return api.get_licenses(file) + + +# Helper function to print FILE to the terminal if skipping +# common licenses. +def skip_print_file(skip, file): + if skip: + print(f"{file}: ", end="") + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument("from_commit") + parser.add_argument("to_commit") + parser.add_argument( + "-s", "--skip", help="skip common licenses in output", action="store_true" + ) + parser.add_argument( + "-p", + "--paths", + help=f'paths to scan (default is "{DEFAULT_SCAN_DIRS}")', + type=str, + default=DEFAULT_SCAN_DIRS, + ) + args = parser.parse_args() + + # Commit boundaries to search for new files + from_commit = args.from_commit + to_commit = args.to_commit + + # Get the list of new files from git. Try the current directory, + # looping up to the root attempting to find a valid git repository. + path = PurePath(os.getcwd()) + paths = list(path.parents) + paths.insert(0, path) + for dir in paths: + try: + repo = Repo(dir) + break + except: + pass + + if dir == path.parents[-1]: + print(f'not a git repository (or any parent up to mount point "{dir}")') + sys.exit(2) + + # Get from/to commits + fc = get_commit(repo, from_commit) + tc = get_commit(repo, to_commit) + + # Loop over new files + paths = [str(dir) for dir in args.paths.split(",")] + print(f'Scanning directories {",".join(f"{s}/" for s in paths)}...') + for file in fc.diff(tc, paths=paths).iter_change_type("A"): + filename = file.a_path + if not args.skip: + print(f"checking licenses for {filename}... ", end="", flush=True) + try: + f = dir.joinpath(dir, filename).as_posix() + lic = get_licenses_for_file(f) + if len(lic["license_clues"]) > 1: + print("multiple licenses detected") + elif ( + not args.skip + or lic["detected_license_expression_spdx"] not in COMMON_LICENSES + ): + skip_print_file(args.skip, filename) + print(f"{lic['detected_license_expression_spdx']}") + except OSError: + # Likely hit a file that was added to the repo and subsequently removed. + skip_print_file(args.skip, filename) + print("no longer in repo?") + except KeyboardInterrupt: + print("interrupted") + break + except Exception as e: + # If scanning fails, there is little we can do but print an error. + skip_print_file(args.skip, filename) + print(e) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/gdb/contrib/setup.cfg b/gdb/contrib/setup.cfg new file mode 100644 index 0000000..5541a01 --- /dev/null +++ b/gdb/contrib/setup.cfg @@ -0,0 +1,19 @@ +[codespell] + +# Skip ChangeLogs and generated files. +skip = */ChangeLog*,*/configure,gdbsupport/Makefile.in,*.dat,*.eps,gdb/features/*.c,gdb/ada-casefold.h,gdb/copying.c,gdb/gdbarch-gen.h,gdb/gdbarch-gen.c,gdb/target-delegates-gen.c + +ignore-words = gdb/contrib/codespell-ignore-words.txt +dictionary = gdb/contrib/codespell-dictionary.txt,- + +# Ignore all URLs. +uri-ignore-words-list = * + +# This codespell issue ( +# https://github.com/codespell-project/codespell/issues/3381 ) reports the +# need to have support for ignoring blocks of code. +# A suggestion there is to use ignore-multiline-regex, which does work, but +# has a bug: using -w drops all newlines in the updated files ( +# https://github.com/codespell-project/codespell/issues/3642 ). +# Consequently, disabled. To be enabled when the bug is fixed. +#ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end diff --git a/gdb/contrib/spellcheck.sh b/gdb/contrib/spellcheck.sh deleted file mode 100755 index 420891f..0000000 --- a/gdb/contrib/spellcheck.sh +++ /dev/null @@ -1,536 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Free Software Foundation, Inc. -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -# Script to auto-correct common spelling mistakes. -# -# Example usage: -# $ ./gdb/contrib/spellcheck.sh gdb* - -scriptdir=$(cd "$(dirname "$0")" || exit; pwd -P) -this_script=$scriptdir/$(basename "$0") - -url=https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines -cache_dir=$scriptdir/../../.git -cache_file=wikipedia-common-misspellings.txt -dictionary=$cache_dir/$cache_file -local_dictionary=$scriptdir/common-misspellings.txt -cache_file2=spell-check.pat1 - -bash_version_at_least () -{ - local major - major="$1" - local minor - minor="$2" - - if [ "$bash_major" = "" ]; then - bash_major=$(echo "$BASH_VERSION" | awk -F '.' '{print $1}') - bash_minor=$(echo "$BASH_VERSION" | awk -F '.' '{print $2}') - fi - - if [ "$bash_major" -lt "$major" ]; then - # Major version less then required, return false. - return 1 - fi - - if [ "$bash_major" -gt "$major" ]; then - # Major version more then required, return true. - return 0 - fi - - # Check minor version. - [ "$bash_minor" -ge "$minor" ] -} - -# Separators: space, slash, tab, colon, comma. -declare -a grep_separators -grep_separators=( - " " - "/" - " " - ":" - "," - "\"" -) -declare -a sed_separators -sed_separators=( - " " - "/" - "\t" - ":" - "," - "\"" -) - -# Pre: start of line, left parenthesis. -declare -a grep_pre -grep_pre=( - "^" - "\(" -) -declare -a sed_pre -sed_pre=( - "^" - "(" -) - -# Post: dot, right parenthesis, end of line. -declare -a grep_post -grep_post=( - "\." - "\)" - "$" -) -declare -a sed_post -sed_post=( - "\." - ")" - "$" -) - -join () -{ - local or - or="$1" - shift - - local res - res="" - - local first - first=true - - for item in "$@"; do - if $first; then - first=false - res="$item" - else - res="$res$or$item" - fi - done - - echo "$res" -} - -grep_or="|" -sed_or="\|" - -grep_join () -{ - local res - res=$(join $grep_or "$@") - echo "($res)" -} - -sed_join () -{ - local res - res=$(join $sed_or "$@") - echo "\($res\)" -} - -usage () -{ - echo "usage: $(basename "$0") [--check] <file|dir>+" - echo " $(basename "$0") --print-dictionary" -} - -make_absolute () -{ - local arg - arg="$1" - - case "$arg" in - /*) - ;; - *) - arg=$(pwd -P)/"$arg" - ;; - esac - - echo "$arg" -} - -parse_args () -{ - local files - files=$(mktemp) - trap 'rm -f "$files"' EXIT - - if [ $# -eq 1 ] && [ "$1" = "--print-dictionary" ]; then - print_dictionary=true - return - fi - - while true; do - case " $1 " in - " --check ") - check=true - shift - ;; - *) - break - ;; - esac - done - - if [ $# -eq -0 ]; then - usage - exit 1 - fi - - local arg - for arg in "$@"; do - if [ -f "$arg" ]; then - arg=$(make_absolute "$arg") - readlink -e "$arg" \ - >> "$files" - elif [ -d "$arg" ]; then - arg=$(make_absolute "$arg") - local f - find "$arg" -type f -exec readlink -e {} \; \ - >> "$files" - else - echo "Not a file or directory: $arg" - exit 1 - fi - done - - mapfile -t unique_files \ - < <(sort -u "$files" \ - | grep -v ChangeLog) - - rm -f "$files" - trap "" EXIT -} - -get_dictionary () -{ - if [ -f "$dictionary" ]; then - return - fi - - local webpage - webpage=$(mktemp) - trap 'rm -f "$webpage"' EXIT - - # Download web page containing table. - wget $url -O "$webpage" - - # Extract table from web page. - awk '/<pre>/,/<\/pre>/' "$webpage" \ - | sed 's/<pre>//;s/<\/pre>//' \ - | grep -E -v "^$" \ - > "$dictionary" - - rm -f "$webpage" - trap "" EXIT -} - -output_local_dictionary () -{ - # Filter out comments and empty lines. - grep -E -v \ - "^#|^$" \ - "$local_dictionary" -} - -output_dictionaries () -{ - ( - output_local_dictionary - cat "$dictionary" - ) | grep -E -v "[A-Z]" -} - -parse_dictionary () -{ - # Parse dictionary. - mapfile -t words \ - < <(awk -F '->' '{print $1}' <(output_dictionaries)) - mapfile -t replacements \ - < <(awk -F '->' '{print $2}' <(output_dictionaries)) - - local words_done - declare -A words_done - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - - # Skip words that are already handled. This ensures that the local - # dictionary overrides the wiki dictionary. - if [ "${words_done[$word]}" == 1 ]; then - words[i]="" - replacements[i]="" - i=$((i + 1)) - continue - fi - words_done[$word]=1 - - # Skip identity rules. - if [ "$word" = "$replacement" ]; then - words[i]="" - replacements[i]="" - fi - - i=$((i + 1)) - done -} - -print_dictionary () -{ - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - i=$((i + 1)) - - if [ "$word" == "" ]; then - continue - fi - - echo "$word -> $replacement" - done -} - -find_files_matching_words () -{ - local cache_id - cache_id=$(cat "$local_dictionary" "$dictionary" "$this_script" \ - | md5sum \ - | awk '{print $1}') - - local patfile - patfile="$cache_dir/$cache_file2".$cache_id - - local pat - if [ -f "$patfile" ]; then - pat=$(cat "$patfile") - else - rm -f "$cache_dir/$cache_file2".* - - declare -a re_words - mapfile -t re_words \ - < <(for f in "${words[@]}"; do - if [ "$f" = "" ]; then - continue - fi - echo "$f" - done \ - | sed "s/^\(.\)/[\u\1\1]/") - - pat=$(grep_join "${re_words[@]}") - - local before after - before=$(grep_join \ - "${grep_pre[@]}" \ - "${grep_separators[@]}") - after=$(grep_join \ - "${grep_separators[@]}" \ - "${grep_post[@]}") - - pat="$before$pat$after" - - echo "$pat" \ - > "$patfile" - fi - - grep -E \ - -l \ - "$pat" \ - "$@" -} - -find_files_matching_word () -{ - local pat - pat="$1" - shift - - local before after - before=$(grep_join \ - "${grep_pre[@]}" \ - "${grep_separators[@]}") - after=$(grep_join \ - "${grep_separators[@]}" \ - "${grep_post[@]}") - - if bash_version_at_least 5 1; then - patc=${pat@u} - else - # shellcheck disable=SC2001 - patc=$(echo "$pat" | sed 's/^\(.\)/\u\1/') - fi - pat="($patc|$pat)" - - pat="$before$pat$after" - - grep -E \ - -l \ - "$pat" \ - "$@" -} - -replace_word_in_file () -{ - local word - word="$1" - - local replacement - replacement="$2" - - local file - file="$3" - - local before after - before=$(sed_join \ - "${sed_pre[@]}" \ - "${sed_separators[@]}") - after=$(sed_join \ - "${sed_separators[@]}" \ - "${sed_post[@]}") - - if bash_version_at_least 5 1; then - wordc=${word@u} - replacementc=${replacement@u} - else - # shellcheck disable=SC2001 - wordc=$(echo "$word" | sed 's/^\(.\)/\u\1/') - # shellcheck disable=SC2001 - replacementc=$(echo "$replacement" | sed 's/^\(.\)/\u\1/') - fi - - local repl1 - local repl2 - repl1="s%$before$word$after%\1$replacement\2%g" - repl2="s%$before$wordc$after%\1$replacementc\2%g" - - sed -i \ - "$repl1;$repl2" \ - "$file" -} - -replace_word_in_files () -{ - local word - word="$1" - - local replacement - replacement="$2" - - shift 2 - - local id - id="$word -> $replacement" - - # Reduce set of files for sed to operate on. - local files_matching_word - declare -a files_matching_word - mapfile -t files_matching_word \ - < <(find_files_matching_word "$word" "$@") - - if [ ${#files_matching_word[@]} -eq 0 ]; then - return - fi - - if echo "$replacement"| grep -q ","; then - echo "TODO: $id" - return - fi - - declare -A md5sums - - local changed f before after - changed=false - for f in "${files_matching_word[@]}"; do - if [ "${md5sums[$f]}" = "" ]; then - md5sums[$f]=$(md5sum "$f") - fi - - before="${md5sums[$f]}" - - replace_word_in_file \ - "$word" \ - "$replacement" \ - "$f" - - after=$(md5sum "$f") - - if [ "$after" != "$before" ]; then - md5sums[$f]="$after" - changed=true - fi - done - - if $changed; then - echo "$id" - fi - - find_files_matching_word "$word" "${files_matching_word[@]}" \ - | awk "{ printf \"TODO: $id: replacement failed: %s\n\", \$0}" -} - -main () -{ - declare -a unique_files - check=false - print_dictionary=false - parse_args "$@" - - get_dictionary - - declare -a words - declare -a replacements - parse_dictionary - - if $print_dictionary; then - print_dictionary - exit 0 - fi - - # Reduce set of files for sed to operate on. - local files_matching_words - declare -a files_matching_words - mapfile -t files_matching_words \ - < <(find_files_matching_words "${unique_files[@]}") - - if [ ${#files_matching_words[@]} -eq 0 ]; then - return - fi - - if $check; then - exit 1 - fi - - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - i=$((i + 1)) - - if [ "$word" = "" ]; then - continue - fi - - replace_word_in_files \ - "$word" \ - "$replacement" \ - "${files_matching_words[@]}" - done -} - -main "$@" diff --git a/gdb/contrib/test_pubnames_and_indexes.py b/gdb/contrib/test_pubnames_and_indexes.py index a3f5c92..7a3dec6 100644 --- a/gdb/contrib/test_pubnames_and_indexes.py +++ b/gdb/contrib/test_pubnames_and_indexes.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -# Copyright (C) 2011-2024 Free Software Foundation, Inc. +# Copyright (C) 2011-2025 Free Software Foundation, Inc. # # This file is part of GDB. # diff --git a/gdb/contrib/words.sh b/gdb/contrib/words.sh index 90b1bd9..a4bb2c7 100755 --- a/gdb/contrib/words.sh +++ b/gdb/contrib/words.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2019-2024 Free Software Foundation, Inc. +# Copyright (C) 2019-2025 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or |
