/
niceSOFT
/
groff
Обзор
Документация
Войти
/
niceSOFT
/
groff
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
ChangeLog
6 987 строк
267 KB
G. Branden Robinson
groff_man_style(7): Offer advice on fonts.
09 авг 2026, 13:59
09 авг 2026, 13:59
67c5521
Код
Авторство
О чём код?
2026-08-09 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/groff_man.7.man.in (Notes) [style]: Tell document authors why it's better to use the package's font selection macros than "raw" *roff font selection escape sequences. Fixes <https://savannah.gnu.org/bugs/?68261>. 2026-08-06 G. Branden Robinson <g.branden.robinson@gmail.com> * bootstrap.conf: Add "build-aux/epsf.tex" to `gnulib_extra_files`. We need it to support Encapsulated PostScript (EPS) files as inline images when rendering some formats of groff's Texinfo manual, and the build environment won't necessarily have it. Fixes <https://savannah.gnu.org/bugs/?68566>. Thanks to Bruno Haible for the report and proposed remedy. 2026-08-03 Bruno Haible <bruno@clisp.org> [eqn]: Fix generation of 'neqn' script on Alpine Linux. * src/preproc/eqn/eqn.am (neqn): Use two separate 'sed' invocations, as BusyBox 'sed' ignores '-e' options if a '-f' option is present. Fixes <https://savannah.gnu.org/bugs/?68601>. 2026-08-06 G. Branden Robinson <g.branden.robinson@gmail.com> [grn]: Revise and parallelize usage message and command synopsis. * src/preproc/grn/grn.1.man (Synopsis): * src/preproc/grn/main.cpp (usage): Use hyphenated noun phrases to document option arguments. Contrast with "Options" sections of man pages, which contextualize metasyntactic nonce terms like "dir" and "dev" adjacently. * src/preproc/grn/grn.1.man (Synopsis): Bring up to date with recent rename of `-M` option to `-I`. Continues commit 9e60503c01, 6 May. 2026-08-02 Bruno Haible <bruno@clisp.org> [eqn,pic,tbl]: Fix Savannah #68585. Fix AddressSanitizer (ASAN) `alloc-dealloc-mismatch` errors. * src/preproc/tbl/table.cpp (block_entry::~block_entry): Free the `contents` member variable using free(3), not `delete[]`. * src/preproc/eqn/delim.cpp (delim_box::~delim_box): Free `left` and `right` member variable using free(3), not `delete[]`. * src/preproc/eqn/special.cpp (special_box::~special_box): Free `macro_name` member variable using free(3), not `delete[]`. * src/preproc/eqn/lex.cpp (argument_macro_input::~argument_macro_input): Free `s` member variable and elements of the `argv` member variable using free(3), not `delete[]`. * src/preproc/pic/object.cpp (output::~output) (output::set_args): Free `args` member variable using free(3), not `delete[]`. * src/preproc/pic/object.cpp (command_object::~command_object): Free `s` member variable using free(3), not `delete[]`. 2024-07-31 Bjarni Ingi Gislason <bjarniig@simnet.is> * ChangeLog.12[34]: Fix doubled word typos. Fixes <https://savannah.gnu.org/bugs/?68584>. 2026-08-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/main.cpp (process_format): Clarify diagnostic messages. Align terminology with our tbl(1) man page. 2026-07-25 Bruno Haible <bruno@clisp.org> [troff]: Fix Savannah #68583. * src/roff/troff/dictionary.cpp: Preprocessor-include C "<limits.h>" header file for `SSIZE_MAX` symbol. (is_good_size): Avoid signed integer overflow when computing dictionary capacity. Fixes <https://savannah.gnu.org/bugs/?68583>. [Problem introduced by me in commit 3c1fa16104, 14 April. --GBR] 2026-07-27 Deri James <deri@chuzzlewit.myzen.co.uk> [devpdf]: Revise search for URW fonts to exclude "/usr/share/ghostscript/Resource/Font". * font/devpdf/Foundry.in: * m4/groff.m4 (GROFF_URW_FONTS_CHECK): Drop it. Systems like Arch and Manjaro Linux don't use Debian's approach to Ghostscript and URW font packaging. Our "configure" script should be looking only for the full URW fonts package, not Ghostscript's "Resource/Font" directory. If Ghostscript is installed, we can configure gropdf's "intermediate" service level at least, and the directory will be found by "BuildFoundries" when it parses the output of the "gs -h" command. What "configure" should be doing is finding the full URW fonts directory, and if found, setting "$urwfontsdir." It should not be looking for Ghostscript-provided fonts at all. We know that if the only URW fonts found are the ones supplied in Ghostscript's "Resource" font directory, there are not going to be any AFMs. `groff_have_urw_fonts` should be set only if the PFB files are found elsewhere. It suffices to have `$GHOSTSCRIPT != "missing"` to know that "intermediate" service is available. Tested by adding my Ghostscript "Resource/Font" to "groff.m4", hiding real URW fonts where "configure" won't find them, and reproducing the "check-urw-foundry.sh" test script failure observed on Manjaro. If I then remove that directory from "groff.m4" it then reports "intermediate" and the test passes. "configure" could look for AFM files as well, but it is unnecessary so long as we don't search paths matching "Resource/Font". Fixes <https://savannah.gnu.org/bugs/?68567>. Thanks to Bruno Haible for the report. 2026-07-28 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am: Cut down "automake.pdf"'s excessive make(1) prerequisites. The document doesn't require pdfmom(1) and its many transitive dependencies; it doesn't use pic(1), eqn(1), or tbl(1) and does not use internal hyperlinks, so it does not require multi-pass formatting. Define new `DOC_GROFF_MOM` macro for performing a "basic mom(7) document build". (doc/automake.pdf): Drop "pdfmom" as prerequisite. Add recipe for this target rule, using new macro. Fixes <https://savannah.gnu.org/bugs/?68576>. Thanks to Nguyễn Gia Phong and Richard Purdie for the report. 2026-07-28 Richard Purdie <richard.purdie@linuxfoundation.org> * doc/doc.am (doc/groff-man-pages.pdf) (doc/groff-man-pages.utf8.txt): Add "preconv" as prerequisite. Fixes <https://savannah.gnu.org/bugs/?68575>. [Problem dates back to introduction of these targets by me in commit 52eeb52073, 2022-01-25. Thanks to Richard for the report and fix. --GBR] 2026-07-28 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (LS): Do nothing on invalid calls. 2026-07-27 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (install-doc): Fix missing "install-info" prerequisite. 2026-07-26 G. Branden Robinson <g.branden.robinson@gmail.com> [tbl]: Fix Savannah #68570. * src/preproc/tbl/main.cpp (process_format): Fix invalid memory access (SEGV) when a table description consists of multiple rows of horizontal span column classifiers (only). When processing each row descriptor, keep track--with new local Boolean `is_first_column`--of whether we're processing the first column descriptor in that row. If we encounter a horizontal span column classifier in the first column, throw an error diagnostic instead of setting `got_format` true and assigning the "type" `t` to `FORMAT_SPAN`. Set the new Boolean false after successfully interpreting one column descriptor in the row descriptor. Reset the new Boolean to true when advancing to a new row description. Fixes <https://savannah.gnu.org/bugs/?68570>. Problem appears to date back to groff's birth. Thanks to Elias Hasas for the report and an economical reproducer. 2026-07-26 G. Branden Robinson <g.branden.robinson@gmail.com> [tbl]: Regression-test Savannah #68570. * src/preproc/tbl/tests/\ do-not-segv-on-span-only-table-description.sh: Do it. * src/preproc/tbl/tbl.am (tbl_TESTS): Run test. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #68552. * src/roff/troff/input.cpp (token::description): Simplify population of buffer describing a node token and avoid potentially overwriting a heap-allocated buffer if that node's description is lengthy (as "non-interpreted character nodes", input in *roff as `\a` and `\t`, are). Discard stale code that performed a dead store of a dummy node description; `describe_node()` zeroes out its destination buffer, so that became pointless a while back. Then, simply measure the buffer as populated by `describe_node()` with strlen(3) and append to it carefully with strncat(3), respecting `bufsz`. Fixes <https://savannah.gnu.org/bugs/?68552>. Thanks to Bruno Haible for the investigation and advice. Problem apparently introduced by me in commit dd7bf97938, 23 November, but in the instance for which we have a reproducer, this heap overwrite not reached because it was masked by a prior null pointer dereference (in `lookup_request()`, which was set up to fail by `do_request()`; see Savannah #68558). 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68552. * src/roff/groff/tests/error-grief-token-descriptions.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #68558. * src/roff/troff/input.cpp (do_request): If given a first argument that is an invalid request or macro name, we cannot sensibly proceed: unwind the "compatibility stack" manipulation we just did and return early, maintaining the integrity of aforesaid stack. Fixes <https://savannah.gnu.org/bugs/?68558>. Problem dates back to commit 6795d15c7b, 2008-09-25. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68558. * src/roff/groff/tests/do-request-does-not-fail-assertion.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (ps_printer::do_import): Revise to avoid overreading heap by one byte with certain ill-formed arguments to the `import` device extension command. Recast error diagnostic to report the remaining contents of the invalid argument and characterize it simply as "invalid" due to non-numeric contents. Also clarify the language of these diagnostics. The grammar of "trout"/"grout" is such that the device control command reads, to the end of the line, one big "argument" containing space-delimited parameters. Fixes <https://savannah.gnu.org/bugs/?68560>. Thanks to Bruno Haible for the concurrent discovery. Problem introduced by me in commit 9f050b7ed7, 7 May. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (ps_printer::do_import): Fix assertion failure caused by missing argument to parameterized `error()` diagnostic call. Fixes <https://savannah.gnu.org/bugs/?68559>. Problem introduced by me in commit 9f050b7ed7, 7 May. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> [grops]: Regression-test Savannah #68559. * src/devices/grops/tests/\ device-extension-command-import-works.sh: Do it. * src/devices/grops/grops.am (grops_TESTS): Run test. (TESTS, EXTRA_DIST): Wire `grops_TESTS` into Automake test harness and ship this and any future grops test scripts. 2026-07-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (print_hyphenation_exceptions_request): When the "phw-request-works.sh" test script ran under sufficiently aggressive memory debugging, it failed. Fix 1-byte heap overread when a hyphenation word supplied by a hyphenation exceptions word list (contrast those defined by the `hw` request) has no hyphenation points at all. Fixes <https://savannah.gnu.org/bugs/?68555>. Problem introduced by me in commit 0b40885e71, 2023-11-03. 2026-07-21 G. Branden Robinson <g.branden.robinson@gmail.com> * configure.ac: Override GNU Autoconf [2.70,2.72]'s cramdowns of ISO C11 and C++11 language version defaults. See: <https://cgit.git.savannah.gnu.org/cgit/autoconf.git/commit/\ ?id=056518b94ecd487bcbefdb69046b3f52c4168222>. 2026-07-19 G. Branden Robinson <g.branden.robinson@gmail.com> * Makefile.am: Stop using (and relying on) a soelim(1) program in the host environment; we build what we need. Define macro `SOELIMBIN`. (.man): Drop prerequisite, which can't portably be used with a suffix rule. Use the new macro's interpolation, instead of a literal, in the recipe. * doc/doc.am ($(GROFF_MAN_PAGES_ALL)): Add dependency-only target declaring "soelim" as prerequisite. Fixes <https://savannah.gnu.org/bugs/?68561>, build failure in environment lacking an existing *roff installation. Thanks to Alexis Hildebrandt for the report. 2026-07-19 G. Branden Robinson <g.branden.robinson@gmail.com> Use GNU Automake's "maintainer mode" to manage maintainer-mode targets. * configure.ac: Interpolate `AM_MAINTAINER_MODE` macro with "disable" argument. * bootstrap: Note necessity of running the "configure" script with `--enable-maintainer-mode` (when building from groff's Git repository). * doc/doc.am: Add targets produced only in maintainer mode to the `MAINTAINERCLEANFILES` macro. ($(DOC_GNU_EPS)): ($(DOC_GNU_PNG)): ($(DOC_LINE_LAYOUT_EPS)): ($(DOC_LINE_LAYOUT_PNG)): Bracket existing target rules in `MAINTAINER_MODE` Automake conditional. Add `else` branch defining them with empty recipes for non-maintainer mode. Fixes <https://savannah.gnu.org/bugs/?68562>, excessive build dependencies imposed on distribution archives. Thanks to Alexis Hildebrandt for the report. 2026-07-17 G. Branden Robinson <g.branden.robinson@gmail.com> * ChangeLog.124: New file. * Makefile.am (EXTRA_DIST): Ship it. 2026-07-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/grog/grog.pl: Update `request` list to include groff 1.25's new `rhw` request. 2026-07-18 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac (fn@init): Trivially refactor. Drop now nilpotent autoincrement notation from interpolation of `fn*text-num` register. 2026-05-18 Deri James <deri@chuzzlewit.myzen.co.uk> * tmac/s.tmac: Fix Savannah #68537, making automatically numbered footnotes (`\*[*]` or `\**`) usable in tbl(1) table entries. (pg@end-text): Remove autoincrement from `fn*text-num` register. (@FS): Increment `fn*text-num` only if given no arguments. Fixes <https://savannah.gnu.org/bugs/?68527>. (Problem appears to date back to groff's birth. --GBR) 2026-07-18 G. Branden Robinson <g.branden.robinson@gmail.com> [ms]: Regression-test Savannah #68537. * tmac/tests/s_automatic-footnote-number-works-in-table.sh: Do it. * tmac/tmac.am (tmac_TESTS): Run test. 2026-07-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/grog/grog.pl: Add undocumented debugging mode. New global scalar `debugging` of Boolean sense tracks debugging state. Initialize false. (debug): New subroutine emits diagnostic message only if debugging is enabled. (process_arguments): Check for `--debug` command-line option. (interpret_line, infer_man_or_ms_package, [main]): Report debugging information of interest. Feature prompted by surprising diagnosis of Lesk's "Notes on the IBM C Compiler." memo as a _me_(7) document instead of the more correct _ms_(7). The misdiagnosis appears to be due to the presence of lowercase macro calls, `ip` and `tp`, which happen to be defined by _me_(7). (There are other problems with the document, including a fascinating crime against the CSTR #49 specification of _tbl_ input that no contemporary implementation supports, and perhaps only Lesk himself knew about, being that program's author.) See <https://www.tuhs.org/pipermail/tuhs/2026-July/034010.html> ff. 2026-07-16 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (doc/webpage.html): Restore `$(DOC_GNU_EPS)` as prerequisite and explain why. Fixes <https://savannah.gnu.org/bugs/?68525>. Problem introduced by me in commit 4a4ef28a4e, 2 July. Thanks to Dave Kemper for the report. 2026-07-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/html/pre-html.cpp (main): Make crappy suggestion in diagnostic reporting pre-grohtml(1) failure a little less crappy. Prompted by Savannah #68525. 2026-07-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (load_hyphenation_patterns_from_file_request) (append_hyphenation_patterns_from_file_request): Before returning, call `tok.next()` instead of `skip_line()`. Annotate why: doing so prevents the formatter from ignoring the input line after the request. Fixes <https://savannah.gnu.org/bugs/?68524>. Problem introduced by me in commit 85c8daa27d, 1 April. Thanks to Dave Kemper for the report. 2026-07-17 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Add unit tests for `hpf` and `hpfa` requests. * src/roff/groff/tests/hpf-request-works.sh: * src/roff/groff/tests/hpfa-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run tests. 2026-07-17 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (doc/groff-pdf-features.pdf): Add "contrib/sboxes/sboxes.tmac" as prerequisite. 2026-07-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Refactor; shift responsibility for positioning the input stream pointer prior to reading the next input line to request handlers more consistently. (open_file): Stop calling `tok.next()` before returning. (open_request, opena_request): Call `tok.next()` before returning. (read_rest_of_line_as_argument): Annotate future direction. (pipe_output, system_request) (unsafe_transparent_throughput_file_request): Update annotations. 2026-07-14 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac: Relocate initialization of new `cov*got-title` register from early occurrence within a `\n[PDFFEAT]` guard to an unconditional one within the `cov` "module". Fixes spurious `reg` warning when rendering without `PDFFEAT` in commit b6a369d79e, 10 July. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * gnulib: Update to stable/2026-07. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am ($(GROFF_HTML)): Use the official GNU stylesheet for our Texinfo manual. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/Groff-PDF-Features.ms: Rename this... * doc/groff-pdf-features.ms: ...to this. * doc/doc.am: Update `DOCFILES_INST` and `PROCESSEDDOCFILES_PDF` macro definitions. (doc/Groff-PDF-Features.pdf): Rename this target... (doc/groff-pdf-features.pdf): ...to this. Update prerequisite per foregoing rename. Update recipe likewise. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> [tmac,troff]: Warn about a deprecated font name only if it isn't translated to a non-deprecated name. * tmac/ps.tmac: Drop font translations of deprecated names; leave it up to the user to do this. * src/roff/troff/env.cpp (environment::select_font): Look up the font's translated name using "node.cpp"'s `get_font_translation()`. Move check for deprecated font name to follow font translation. * src/roff/troff/node.cpp (get_font_translation): Drop `static` linkage; as noted above, the function needs external visibility now. Annotate planned future reversal of this change. * src/roff/troff/node.h: Declare `get_font_translation()`. Annotate planned future reversal of this change. Fixes <https://savannah.gnu.org/bugs/?68345>. Thanks to Deri James for prompting this (IMO) superior direction for font name deprecation to take. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::writeHeadMetaStyle): Don't truncate images to the newly limited maximum rendering width. The browser might downscale it, making it less legible, but should also keep it right-clickable or long-pressable or similar so that our less fuzzy pre-rasterized tbl(1) tables (courtesy of pre-grohtml(1)) can be accessed and read at full size. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::writeHeadMetaStyle): Ameliorate horrible UX. grohtml(1) has ignored the media width configured by the full-service macro package or document forever, but employing a CSS 'max-width' of--effectively--infinity makes for painful reading. Configure the `body` element with a `max-width` of `80ch`, which is a little wide but congrues well with the default width used by groff man(7) and mdoc(7). Moreover, these packages render nearly all of their content indented, which brings the effective line length down to the 65n-75n range advised by typography experts. Employ automatic margins on the left and right to center the text on wide browser windows, and a `padding` of 1em for ergonomics on "swipey" devices like tablets. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (EX, EE): When formatting for HTML, begin and end, respectively, a `span` element employing the new `code-display` class set up in the document header by grohtml(1). Fixes <https://savannah.gnu.org/bugs/?68309>. Thanks to Colin Watson and Stephen Gildea for the reports and to them and Ingo Schwarze for discussion on the groff list. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::writeHeadMetaStyle): Define new class selector `.code-display`. Use monospaced font family and inherit display properties from parent block. Define new descendant selector thereof for `p` elements, eliminating vertical margins. Initially this is for use by man(7)'s `EX`/`EE` macros, but might have applications in other packages. Begins fixing <https://savannah.gnu.org/bugs/?68309>. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (token::next): Perform range check on parameter to `\N` escape sequence; throw error diagnostic if it fails. Fixes <https://savannah.gnu.org/bugs/?68521>. Problem introduced by me in commit b039f8c3d1, 2025-03-29, an attempt to improve compatibility with DWB troff. (But as noted in that commit, DWB troff appears not to _exercise_ its freedom to employ negative character indices.) 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (charinfo::set_number) (charinfo::get_number): Make `NDEBUG` execution traces safer: clamp negative character indices to zero, avoiding undefined formatter behavior. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (charinfo::set_number): Trivially refactor. Rename function parameter from `n` to `idx`, to be more expressive and suggest the truth--that it relates to indexed characters. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/text.cpp: Fix code style nits. Rationalize header file inclusions. Arrange C standard library header file names in alphabetical order. (special_char_box::output): Spell null pointer constant the idiomatic C++98 way (`0`) instead of as `NULL`. 2026-07-08 Nguyễn Gia Phong <cnx@loang.net> * src/preproc/eqn/text.cpp: Drop dead static data {`entity_table`} , its now unused data type declaration {`struct map`}. (special_to_entity): Drop now unused function. Fixes <https://savannah.gnu.org/bugs/?68499>. 2026-07-08 Nguyễn Gia Phong <cnx@loang.net> * src/preproc/eqn/text.cpp: Stop mapping special characters to named entities when outputting MathML. Preprocessor-include "unicode.h". (special_char_box::output): Use libgroff's `valid_unicode_code_sequence()` to obtain its Unicode sequence representation if it is in groff Unicode special character escape sequence syntax. If not, use `glyph_name_to_unicode()` to look up its Unicode representation. Continues fixing <https://savannah.gnu.org/bugs/?68499>. 2026-07-08 Nguyễn Gia Phong <cnx@loang.net> * src/preproc/eqn/box.cpp (half_space_box::output) (full_space_box::output) (thick_space_box::output) (thin_space_box::output): * src/preproc/eqn/other.cpp (overline_char_box::output) (overline_box::output) (underline_char_box::output) (underline_box::output): Emit numeric character references, not named entities, when generating MathML, because GNU eqn supports MathML only when producing XHTML. Begins fixing <https://savannah.gnu.org/bugs/?68499>. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (get_html_entity): Emit named character entities only for HTML 4, not XHTML, with the exception of the handful recognized by XML, already handled earlier in this function. 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (get_html_entity): Emit `'` for character code 0x27 regardless of `doctype`. Its omission appears to have been a casualty of the IE4 era (Y2K). 2026-07-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::writeHeadMetaStyle): Revise XHTML output header to modernize it and work with contemporary Firefox and Epiphany. 2026-05-02 Luis Rivera <jlrn@up.edu.mx> * tmac/es.tmac: Revise Spanish localization for correctness. Fixes <https://savannah.gnu.org/bugs/?68216>. 2026-07-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac (TC): Fix code style nit. De-pessimize by handling multiple numerical expression evaluations together with the `&` logical "and" operator to connect them. `.if ... .if` is a legitimate pattern, but only necessary when different kinds of expressions need to be evaluated, or need to be evaluated serially; in *roff the three kinds are numeric expressions, output comparison expressions, and the somewhat vaguely named "conditional expressions" (`.if d mac`, `.if t`, and so forth). This way we avoid a trip through the parser's main loop. 2026-07-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac: Trivially refactor. Rename `gottitle*pdf` register to `cov*got-title` to situate it within a documented groff ms name space. 2026-07-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac (RP): When formatting with PDF features, assign the cover page a page number starting with "T-" to avoid collision with page 1 of the main matter. This is a quality-of-life improvement when using PDF viewer applications, and the sequence "T-1", "T-2", ..., appears to be the convention in the PDF world for numbering pages prior to front matter numbered with lowercase Roman numerals. 2026-07-09 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/s.tmac (cov*tl-au-print, ID, par@reset, par@TL, par@AU) (par@AI): Cancel temporary indentation before issuing centering request, preventing issue of warning in category "style" from formatter. See <https://savannah.gnu.org/bugs/?68285>. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (token::description): * src/roff/troff/node.cpp (make_glyph_node): Adapt new special character identifier quoting technique from `transparent_translate()`. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (transparent_translate): Improve diagnostic message when griping about nonexistent special character with a neutral apostrophe in its identifier. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (remove_character_request): Stop throwing warning if character already doesn't exist. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Add accessor for `token` class's `nm` member variable. * src/roff/troff/token.h (class token): Declare `get_name()`. (token::get_name): Define `get_name()`. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (make_glyph_node): Improve diagnostic message when griping about nonexistent special character with a neutral apostrophe in its identifier. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (token::description): Revise message construction. When special character syntax `\[foo]` is not backed by an existing object, we don't know if a special character or character class is meant. Since the constructed description is followed by "is not defined", it's redundant to characterize the token as "nonexistent". Rename `static` `const` local object `nonexistent` to `special_character_or_class`, and repopulate its contents accordingly. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (remove_character): Rename this... (remove_character_request): ...to this. (init_input_requests): Update wire-uppery of request names to handler functions. 2026-07-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (remove_character): Refactor to use new `read_any_character()` function. Drop now unreachable diagnostic; `read_any_character()` complains if the token on the input stream is not a character. 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (set_character_flags_request): Change condition of `while` loop from `tok.is_any_character()` to new side-effectful `read_any_character()` function. Fixes <https://savannah.gnu.org/bugs/?68510>. Problem introduced by me in commit 3f0c81a0a7, 24 May. Thanks to Dave Kemper for the report. 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Add unit test for `cflags` request. * src/roff/groff/tests/cflags-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (set_character_flags_request): Slightly refactor to handle an error case (first argument is not an integer) differently so as to return early, dropping indentation level of the most complex part of the function. 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp (is_valid_term): Recast error diagnostic message for clarity in cases like "echo .cflags X | groff". 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #68511, recovering from a botched refactor. * src/roff/troff/input.cpp (read_any_character): New function reads any groff character--ordinary, special, or indexed--from the input stream, skipping any spaces. While a fairly simple task, it can be done incorrectly nevertheless. Multiple request handlers can use this. Start... (set_hyphenation_codes): ...here. Rearrange "do/while" loop to "while" loop. Advance the input stream after reading the destination character in a pair. Failing to do so was the defect. Simplify an error diagnostic message. Simplify a conditional code path. Fixes <https://savannah.gnu.org/bugs/?68511>. Problem introduced by me in commit 994fcce775, 24 May. Thanks to Dave Kemper for the report. 2026-07-11 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Add unit test for `hcode` request. * src/roff/groff/tests/hcode-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-07-07 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (zoom_font_request): Refactor. Call `read_font_identifier()` instead of `read_identifier()` to gather first argument. Emit error diagnostic on an attempt to apply a zoom factor to a mounting position. This was already prohibited in 1.24.0, but now, thanks to other refactoring, we can diagnose it more easily. 2026-07-05 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (suppress_node::suppress_node): Initialize `position` with a valid value; while typed `char`, it is actually an enumerated type in disguise, and zero (or `\0`) is not a valid value. See `src/roff/troff/input.cpp:do_suppress()`. 2026-07-05 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Add unit test for `fzoom` request. * src/roff/groff/tests/fzoom-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-07-05 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (RE): Rename "local" macro `an-RE-problem` to `an*msg`. 2026-07-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp (possible_command::build_argv): Use ISO C++98 exceptions to handle heap storage allocation failure. Preprocessor-include C++ "<new>" header file. Continues the long process of fixing Savannah #68192. 2026-07-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp (possible_command::build_argv): Fix code style nits. Explicitly compare variable of pointer type to ISO C++98 null pointer literal, instead of punning it down to a Boolean. Convert type of local variables `len`, `argc` and {loop locals} `i` from `int` to `size_t`, since they're used as or compared to array indices. Lift computation of `argc + 1` into a new `const` `size_t` variable `argv_len`, for the same reason and to prepare for a forthcoming change. 2026-07-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/grn/main.cpp (operand): Exit with status 2, not 8, when command-line option argument is missing. (interpret): Exit with POSIX `EXIT_FAILURE` status, not 8, when reading an unrecognized command keyword. * NEWS: Document these changes in behavior. 2026-07-04 G. Branden Robinson <g.branden.robinson@gmail.com> [grn]: Boolify. * src/preproc/grn/gprint.h: Delete `TRUE` and `FALSE` preprocessor macros. * src/preproc/grn/hdb.cpp: Demote external symbols `SUNFILE` and `compatibility_flag` from `int` to `bool`. (DBRead): Demote local variables `done` and `last` from `int` to `bool`. Assign to `bool` objects using Boolean literals. * src/preproc/grn/main.cpp: Demote global symbols `defpoint`, `pointscale`, `setdefault`, and `sflag` from `int` to `bool`. Initialize `bool` objects using Boolean literals. Declare `has_polygon()` as returning `bool`, not `int`. (main): Assign to `bool` objects using Boolean literals. (has_polygon): Demote return type from `int` to `bool`. Continues the long process of fixing Savannah #66672. 2026-07-05 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (texinfo-all): New convenience target builds all rendered forms of our Texinfo manual. Thanks to the new line layout diagram, much of groff is now a prerequisite for generating it. 2026-07-04 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am: Move definition of `DOC_LL` macro earlier... (mostlyclean_doc): ...and use it here to scrub temporary files produced by the later maintainer-mode `$(DOC_LINE_LAYOUT_EPS)` target. 2026-07-03 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (install-info-local): Clean out any existing groff Info files in `$(DESTDIR)$(infodir)`, not `$(DESTDIR)/doc`. Fixes <https://savannah.gnu.org/bugs/?68498>. Problem introduced by me in commit d79c3f3a4a, 2021-11-11. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> [doc,man]: Add line layout diagram. * doc/line-layout.roff: New file uses pic(1) to replace ASCII art diagram of *roff output line geometry. * doc/doc.am: Define new macros `DOC_LINE_LAYOUT_EPS` and `DOC_LINE_LAYOUT_PNG` to track names of rendered forms of the foregoing pic(1) image. Clean these files from the build directory. Add them as prerequisites to the `$(GROFF_DVI)` and `$(GROFF_PDF)` targets, respectively. Add the new file and its rendered forms to the `EXTRA_DIST` macro to ship them in our distribution archive. ($(GROFF_HTML)): Add `DOC_LINE_LAYOUT_PNG` as prerequisite. Copy that file into the directory used by the split-by-node version of our Texinfo manual in HTML form, as required for makeinfo(1) to generate a non-broken "<img src>" element. (maintainer-clean-local): Clean the rendered image files from the build directory. (install-html-local): Install the rendered image file where the monolithic version of our Texinfo manual in HTML form can find it. (dist-gnueps): Drop apparently unneeded target, and `dist-hook`'s use of it as a prerequisite. ($(DOC_LINE_LAYOUT_EPS)): New target rule renders the new file as Encapsulated PostScript file using groff(1), gs(1) {Ghostscript}, and sed(1), as documented in grops(1). ($(DOC_LINE_LAYOUT_PNG)): New target rule renders the new file as a Portable Network Graphics file using convert(1) {ImageMagick}. * doc/groff.texi.in (Line Layout): Inline image. Retain the ASCII art version, but output it only `ifinfo`--rendering to a format that doesn't support inline images. * man/groff.7.man (Line layout): Inline aforementioned pic(1) document with a `so` request, only if in troff mode. Otherwise, use the existing ASCII art version. So that installed systems don't have to manage the installation location of the file... * Makefile.am: ...update `.man` suffix rule to depend on soelim(1), and unconditionally run all man pages (which are generated files already anyway) through soelim(1), literally inlining the pic(1) region. Pass soelim(1) `-I` options so that the "line-layout.roff" file should be found whether the build takes place inside or outside of the build tree. Fixes <https://savannah.gnu.org/bugs/?68407>. Thanks to Stefan Möding for the new diagram! 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/eqn.am: * src/preproc/pic/pic.am: * src/preproc/refer/refer.am: Improve clean behavior, deleting YACC/Bison-generated files _only_ from an out-of-source build tree. Because they are shipped in the distribution archive, only the "maintainer-clean" target should remove them from the source tree. Add new macros `GROFF_{EQN,PIC,REFER}_YACC_FILES` to track these artifacts. Redefine `MAINTAINERCLEANFILES` in terms of these new macros. Add `mostlyclean-{eqn,pic,refer}` targets to perform the cleaning. Add these new targets as dependencies of `mostlyclean-local`. 2026-07-03 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am: Improve "maintainer-clean" target. (maintainer-clean-local): Rewrite recipe to use existing macro definitions instead of literals. Recursively delete "$(GROFF_HTML).node", since that, too, is an artifact shipped in the distribution archive. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am: Improve "clean" target. (mostlyclean_doc): Clean the "doc/gnu.{eps,png}" files and generated forms of our Texinfo manual from an out-of-source-tree build directory. Reorganize logic. (maintainer-clean-local): Stop cleaning files auxiliary to the generated forms of our Texinfo manual. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/groff.texi.in (Output Language Compatibility): On supported output formats, inline the "gnu with printing press" image as an end mark for this manual's main matter. Doing so demands much build system foolery, in part because different output formats demand different image formats. * doc/doc.am: Define new macro `DOC_GNU_PNG` storing path to relevant image in PNG format. Override definition of `TEXINPUTS` macro so that etex can locate the EPS file when generating DVI. Ship `DOC_GNU_PNG` in our distribution archive. (install_doc_gnu_png): New target rule installs `DOC_GNU_PNG` file so that installed example files can access it. (install_doc_examples): Depend on `DOC_GNU_PNG`. (uninstall_doc_examples): Remove "gnu.png" file from example directory. ($(GROFF_DVI)): Add dependency on `DOC_GNU_EPS`, since we're now embedding it in our Texinfo manual. ($(GROFF_PDF)): Add dependency on `DOC_GNU_PNG`, since we're now embedding it in our Texinfo manual. ($(GROFF_HTML)): Override Automake's definition of target with one that copies the "gnu.png" file where the HTML versions of our Texinfo manual can find it. (maintainer-clean-local): Remove "gnu.png" from the build directory. (install-html-local): Install the "gnu.png" file. ($(DOC_GNU_PNG)): New target rule generates "gnu.png" from "gnu.xpm". 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am: Use `GROFF_HTML` macro more aggressively. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/doc.am (doc/Groff-PDF-Features.pdf): Add `$(DOC_GNU_PNG)` and `font/devpdf/stamp` as prerequisites for this target. Drop duplicate `tbl` prerequisite. (doc/webpage.html): `$(DOC_GNU_PNG)` should be our prerequisite, not `$(DOC_GNU_EPS)`. ($(GROFF_DVI)): Declare `$(DOC_GNU_EPS)` as prerequisite. ($(GROFF_PDF)): Declare `$(DOC_GNU_PNG)` as prerequisite. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/txi-en.tex: Delete. * doc/doc.am (EXTRA_DIST): Stop shipping that file in our distribution archive. The macros defined in "doc/texinfo.tex" already handle English localization, making this vendored English template file redundant. However, retaining it creates problems when overriding the `TEXINPUTS` macro, which a forthcoming change requires. Specifically, the override seems to have altered the search path, causing this 13-year-old file to be discovered prior to TeX Live's own much more up-to-date version, and ultimately causing the Texinfo `@entry` command to fail. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Remove `string::remove_spaces()`. It no longer has any call sites. * src/include/stringclass.h (class string): Delete declaration. * src/libs/libgroff/string.cpp (string::remove_spaces): Delete. 2026-06-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/main.cpp (strieq, process_options) (free_input_entry_format_list, process_format, process_data): Define functions as `static`; they do not require external linkage. 2026-06-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (put_string): Make `NDEBUG`-safe. 2026-06-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::clear): Refactor to reduce unnecessary memory writes. 2026-06-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::move): Refactor to reduce heap manipulation and memory copying. 2026-06-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (salloc, sfree_and_alloc) (string::operator+=(const char *)) (string::operator+=(const string &)): Fix off-by-one errors, maintaining the new invariant that a groff `string` is always null-terminated. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (environment::add_node): Fix code style nits. Delete blank line within function definition (JJC style). Arrange equality comparisons to avoid inadvertent assignment to lvalue (GBR style). 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/main.cpp: Refactor to use our own string-trimming function instead of the `remove_spaces()` member function of groff's bespoke `string` class. (trim_spaces): New function updates string parameter to remove leading and trailing spaces. (process_data): Call new function instead of member function. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> [tbl]: Test "nospaces" region option behavior. * src/preproc/tbl/tests/nospaces-region-option-works.sh: Add test. * src/preproc/tbl/tbl.am (tbl_TESTS): Run test. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/main.cpp (process_data): Emit warning when ignoring excess table entries instead of doing so silently. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/main.cpp (strieq) (process_options) (free_input_entry_format_list) (process_format) (process_data): * src/preproc/tbl/table.cpp (init_span_reg) (compute_span_width): Define functions as `static`; they do not require external linkage. Fixes `-Wmissing-declarations` GCC warnings. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Move declarations of `dump_node_list_in_reverse()`... * src/roff/troff/int.cpp: ...and `dump_node_list()` from here... * src/roff/troff/node.h: ...to here. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Move declaration of `get_diversion_state()` from here... * src/roff/troff/input.h: ...to here. 2026-07-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (configure_page_offset_request) * src/roff/troff/env.cpp (compare_ranges) (hyphenation_character_request) (do_break_request) (input_trap) (input_trap_continued) (line_tabs_request) (sptoa): * src/roff/troff/input.cpp (exit_troff) (handle_initial_request) (do_string_case_transform) (get_file_line) (do_macro_source) (usage): * src/roff/troff/node.cpp (node_list_character_type) (node_list_vertical_extent): Define functions as `static`; they do not require external linkage. Fixes `-Wmissing-declarations` GCC warnings. 2026-07-02 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor to kill off `ESCAPE_NEWLINE`. There appears to be no advantage to encoding escaped newlines in macro definitions. Its purpose cannot be to restore or pretty-print a macro definition exactly as it was input, as no mechanism for doing so exists, and comments are already discarded upon reading. * src/roff/troff/input.h: Delete definition of `ESCAPE_NEWLINE` constant. * src/roff/troff/input.cpp (read_character_in_copy_mode) (token::next) (string_iterator::fill) (do_define_macro): Stop handling constant. Fixes <https://savannah.gnu.org/bugs/?66987>. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/object.cpp (graphic_object::add_text): Add new local constant `max_text_pieces` to impose limit on size of heap-allocated `text_piece` array. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/object.cpp (graphic_object::add_text): Convert type of local variables `len` and `i` from `int` to `size_t`, since they're used as or compared to array indices. Refactor loop to lift `i` out of it, since it requires a type declaration, and you can't do that after a comma in a C++98 for loop initialization expression. Use `i` instead of `len` as the index into the `text` array since the value of `len` is now a loop invariant--`i` is the index. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/lex.cpp (interpolate_macro_with_args): When warning of ignored excess macro arguments, explicitly state that they're ignored. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/lex.cpp (interpolate_macro_with_args): Boolify. Demote local variable `ignore` from `int` to `bool` and rename it to `is_ignoring_arguments`. Assign to it using Boolean, not integer, literals. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/lex.cpp: Slightly refactor. Replace preprocessor macro `MAX_ARG` with new `static const size_t` `pic_macro_maximum_arg_count`. (class argument_macro_input) (process_body) (argument_macro_input::get) (argument_macro_input::peek) (interpolate_macro_with_args) (class copy_thru_input) (copy_thru_input::get) (copy_thru_input::peek) (copy_thru_input::get_line): Lexically replace `MAX_ARG` with `pic_macro_maximum_arg_count`. (class argument_macro_input, class copy_thru_input): Convert type of `argc` member variable from `int` to `size_t`, since it's used as an array index. Update constructor declaration accordingly. (process_body): Convert type of local variables `j`, `i`, `n`, and `k` from `int` to `size_t`, since they're used or compared to array indices. (argument_macro_input::argument_macro_input) (argument_macro_input::~argument_macro_input) (argument_macro_input::get) (argument_macro_input::peek) (copy_thru_input::get) (copy_thru_input::peek) (get_thru_arg): Convert type of local variable `i` from `int` to `size_t`, since it's used as an array index. (get_thru_arg): Avoid modular arithmetic wraparound by adjusting loop test condition and consequent indices used to index into token buffer containing argument that isn't the name of a macro. (interpolate_macro_with_args): Convert type of local variable `argc` from `int` to `size_t`, since it's compared to an array index. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/lex.cpp (interpolate_macro_with_args): When warning of ignored excess macro arguments, report how many are supported. 2026-06-23 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/lex.cpp: Slightly refactor. Replace magic constant `9` with new `static const size_t` `eqn_macro_maximum_arg_count`. Retype variables used as array indices appropriately. Retype `const` global `NCONTEXT` from `int` to `size_t` and make it `static`. Retype global `context_index` from `int` to `size_t` and make it `static`. (class argument_macro_input): Convert type of `argc` member variable from `int` to `size_t`, since it's used as an array index. Update constructor declaration accordingly. (argument_macro_input::argument_macro_input): Update constructor definition accordingly. (interpolate_macro_with_args): Use new `size_t` object instead of magic constant. Convert type of `argc` member variable from `int` to `size_t`, since it's used as an array index. (init_table): Retype local variable `i` from `unsigned int` to `size_t`. (argument_macro_input::argument_macro_input) (show_context): Retype local variables `i` and `j` to `size_t`. (argument_macro_input::~argument_macro_input) (argument_macro_input::get) (argument_macro_input::peek) (flush_context): Retype local variable `i` to `size_t`. 2026-06-23 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/lex.cpp (interpolate_macro_with_args): Fix code style nits. Parenthesize formally complex expressions. Arrange equality comparisons to avoid inadvertent assignment to lvalue. 2026-06-23 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/lex.cpp (interpolate_macro_with_args): Declare new `bool`, `is_ignoring_arguments`. Use it within the macro argument reading loop to ignore any arguments past the ninth, so that they do not populate the `argv` array beyond its bounds. Neither AT&T nor GNU eqn has a syntax for interpolating tenth or subsequent arguments, nor any `shift` command that would made such arguments addressable. Issue one warning per macro expansion with excess arguments. Fixes <https://savannah.gnu.org/bugs/?68468>. Problem appears to date back to groff's birth. Reported apparently by an LLM robot in <https://lists.gnu.org/archive/html/bug-groff/2026-06/\ msg00053.html>. 2026-06-23 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn]: Regression-test Savannah #68468. * src/preproc/eqn/tests/\ do-not-segv-on-excess-macro-arguments.sh: Do it. * src/preproc/eqn/eqn.am (eqn_TESTS): Run test. 2026-06-23 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn]: Add `lex_warning()` function. * src/preproc/eqn/eqn.h: Declare it. * src/preproc/eqn/lex.cpp (lex_warning): Define it. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Fix `-Wstringop-truncation` warnings. (map_special_character_for_device_output): Don't overwrite the null terminator in the `character` buffer. (init_charset_table): Use memcpy(3) instead of strncpy(3) because we're deliberately doing a partial write of a non-string `char` (byte) array into a buffer. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (map_special_character_for_device_output): Slightly refactor. Drop confusing local variable `unibufsz` (which differs from a libgroff symbol only in lettercase) in favor of `UNIBUFSZ + 1` expression and (subsequently) `sizeof character`, which can both be computed at compile time. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/dictionary.cpp: Preprocessor-include "errarg.h" and "error.h" to make visible `fatal()` function. (dictionary::lookup): Add overflow check of dictionary capacity. GCC's static analyzer was suspicious that it could overflow. `capacity` is a `ssize_t`, but C++'s allocator (behind `new`) uses `size_t`. If `capacity` wraps (overflows), emit fatal error. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff,troff]: Reallocate fatal error exit responsibilities, and communicate them to the compiler. * src/include/error.h: Annotate non-returning `fatal_with_file_and_line()`, `fatal()`, and `fatal_error_exit()` functions with `__noreturn__` attribute to ease static analysis. * src/libs/libgroff/error.cpp (do_error_with_file_and_line): Stop checking `type` and calling `fatal_error_exit()` if it's `FATAL`. (fatal, fatal_with_file_and_line): Call `fatal_error_exit()` from here instead. * src/roff/troff/div.h: Annotate non-returning `write_any_trailer_and_exit()` function declaration with `__noreturn__` attribute to ease static analysis. * src/roff/troff/input.cpp (do_error): Stop checking `type` and calling `write_any_trailer_and_exit()` if it's `FATAL`. (fatal): Call `write_any_trailer_and_exit()` from here instead. 2026-06-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/json_encode.cpp (json_encode_char): Explicitly initialize stack-allocated structure. Fixes `-Wmaybe-uninitialized` warning. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/gpl.texi: Add copy of the GNU GPL in Texinfo format. * doc/doc.am: Add it to `EXTRA_DIST` so that it ships in our distribution archive. * doc/groff.texi.in: Document fact of dual-licensing on copyright page, inspired by GNU nano's approach. `@include` the new "gpl.texi" file in the "Copying This Manual" appendix. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Trivially refactor. (sfree_alloc): Rename this... (sfree_and_alloc): ...to this. (string::operator=(const char*), string::operator=(char) [0]) (string::move): Update call sites. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (sfree_alloc): Avoid unnecessary memset(3) if the new string's `len` (length) is equal to the existing string's capacity (`oldsz`), because it is already {invariantly} null-terminated. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (srealloc): Avoid unnecessary memset(3) if the new string's `length` is equal to the old string's `sz` (capacity), because it is already (invariantly) null-terminated. Simplify condition for performing memcpy(3) of old contents to new: do it only if the old length is not zero. Unconditionally memset(3) the memory from the end of the new string to the end of string's capacity. 2026-06-19 G. Branden Robinson <g.branden.robinson@gmail.com> [refer]: Add smoke test. * src/preproc/refer/tests/artifacts/little-schemer.bib: Add. * src/preproc/refer/tests/smoke-test.sh: Add. * src/preproc/refer/refer.am (refer_TESTS): Run test. (EXTRA_DIST): Ship new artifact. 2026-06-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::string): Drop redundant memset(3) calls; `salloc()` already ensures that the memory to which it returns a pointer is cleared. 2026-06-19 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Disable unused `string` functions. * src/include/stringclass.h: "`#if 0`"-out `string` constructor taking a `char` parameter and collation operator `friend` function declarations. * src/libs/libgroff/string.cpp (string::string) (operator<, operator<=, operator>, operator>=): "`#if 0`"-out corresponding function definitions. 2026-06-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Refactor. (string::operator=): assert(3) that the `p` parameter (a C string) is not a null pointer. For `NDEBUG` builds, assign it an empty string literal in this situation. Measure `p` with strlen(3) and call `sfree_alloc()` instead of `sfree()` on member variable `ptr` to size a buffer for it. assert(3) that `sfree_alloc()` returns a non-null pointer. (sfree): Delete now-unused function. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::clear): Maintain invariant that `ptr` is never a null pointer even in `NDEBUG` builds. 2026-06-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Manipulate assert(3)ions verifying the invariant of `ptr`'s non-nullity to sit closer to assignments thereto. This way the assertions can/should reliably fail prior to null pointer dereference. Also check this invariant in more places. (string::string, string::operator+=, string::remove_spaces): Relocate assertions. (string::append, string::set_length, put_string): Add assertions. (string::clear): Uncomment assertion. Its comment was inaccurate. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Document invariant. (salloc, sfree_alloc): Always allocate room for a null terminator since the groff `string` class's `contents()` member function returns a C string. That interface does not combine well with a class design that uses neither memset(3) nor ISO C++03 value initialization to fastidiously clean the allocated buffers (which, as of recently, we do). This way we wear suspenders _and_ a belt. As a side effect, we more often {re}allocate in chunks of 64 bytes a time, reducing the total number of allocations and improving build time by about 5%. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/tmac.am: Add "tmac/devtag.tmac" to `TMAC_PACKAGE_MS` macro definition, since the package internally uses that auxiliary macro file, and any update to it should render an ms(7) document out-of-date. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn,pic,refer]: Improve maintainer-clean target. * src/preproc/eqn/eqn.am (maintainer-clean-eqn): * src/preproc/pic/pic.am (maintainer-clean-pic): * src/preproc/refer/refer.am (maintainer-clean-refer): New phony target rule scrubs directories housing files generated by GNU Bison. Add each as dependency of `maintainer-clean-local`. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn]: Rearrange Automake material. * src/preproc/eqn/eqn.am: Organize build target generation before "clean"-stage stuff. 2026-06-20 G. Branden Robinson <g.branden.robinson@gmail.com> [pic,refer]: Rearrange Automake material. * src/preproc/pic/pic.am: * src/preproc/pic/refer.am: Organize consistently with "eqn.am". 2026-06-14 G. Branden Robinson <g.branden.robinson@gmail.com> * Makefile.am: Fix coverage generation. (gen-coverage): Pass `--prefix` and `--ignore-errors` to genhtml(1) to kludge around the problem of Bison/YACC-generated source files being present in the _build_ tree rather than the source tree when doing out-of-tree (`VPATH`) builds. Fixes <https://savannah.gnu.org/bugs/?68304>. 2026-06-14 G. Branden Robinson <g.branden.robinson@gmail.com> * configure.ac: Make build robust against combination of maintainer mode, use of GNU Bison, Make's ordering of targets, and out-of-tree builds. Use `AC_CONFIG_COMMANDS` macro to ensure that the eqn, pic, and refer directories in the build tree that will be populated by Bison (or YACC) exist in the event no C++ files from these subdirectories get compiled first. Fixes <https://savannah.gnu.org/bugs/?67808>. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (RE): Trivially refactor. Rename register `an-RE-requested-level` to `an*RE-requested-level`. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Trivially refactor. Rename macro. (an-deprecation-warn): Rename this... (an*deprecation-warn): ...to this. (AT, UC, DT, PD, SB, OP): Update interpolation sites. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [grotty]: Slightly refactor. * src/devices/grotty/tty.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (tty_font::load_tty_font) (tty_printer::make_rgb_string) (tty_printer::has_color) (tty_printer::add_char) (tty_printer::begin_page): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [grops]: Slightly refactor. * src/devices/grops/ps.cpp: * src/devices/grops/psrm.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. * src/devices/grops/ps.cpp: (ps_font::load_ps_font, ps_printer::set_subencoding) (ps_printer::get_subfont, ps_printer::define_encoding) (ps_printer::encode_fonts, ps_printer::set_style): * src/devices/grops/psrm.cpp: (resource_manager::lookup_resource) (resource_manager::lookup_font) (resource_manager::document_setup): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. * src/devices/grops/psrm.cpp (resource_manager::document_setup): Trivially refactor to lift a repeated expression into a local variable, `count`. Continues the long process of fixing Savannah #68192. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [grolj4]: Slightly refactor. * src/devices/grolj4/lj4.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (lj4_font::load_lj4_font): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [grolbp]: Slightly refactor. * src/devices/grolbp/lbp.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (lbp_font::load_lbp_font): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/html/pushback.cpp: Migrate to ISO C++98 Boolean literals. Drop preprocessor manipulations of `TRUE` and `FALSE` macros. (pushBackBuffer::pushBackBuffer, pushBackBuffer::getPB) (pushBackBuffer::isString): Use ISO C++98 Boolean literals. * src/preproc/html/pushback.h (class pushBackBuffer): Demote declared return type of `isString()` member function from `int` to `bool`. * src/preproc/html/pushback.cpp (pushBackBuffer::getPB, pushBackBuffer::isString) (isDigit, isWhite, isHexDigit): Demote return type from `int` to `bool`. Continues the long process of fixing Savannah #66672. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/quotearg.c: Slightly refactor. Favor C standard library symbol `EXIT_FAILURE` over integer literal "1". Continues the long process of fixing Savannah #66672. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/quotearg.c: Undefine file-local preprocessor macros after we're done with them. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/quotearg.c: Rationalize header file inclusions. Arrange C standard library header file names in alphabetical order. Annotate why they're included. Drop inclusions that seem unnecessary ("<limits.h>" and "nonposix.h"). 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Boolify "quotearg.c". * src/libs/libgroff/quotearg.c: Preprocessor-include "<stdbool.h>" header file to ensure visibility of `true` and `false` symbols. Drop preprocessor manipulations of `TRUE` and `FALSE` macros. (needs_quoting): Migrate Boolean literals from preprocessor macros to language objects. Demote return type from `int` to `bool`. Continues the long process of fixing Savannah #66672. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Trivially refactor. Rename `string` class's private member function `grow1()` to `embiggen()`. The former name is now potentially misleading because the internal function `srealloc()` now doubles a `string` object's capacity (`sz`) when exceeded. * src/include/stringclass.h (class string): Rename declaration. (string::operator+=): Update call site. * src/libs/libgroff/string.cpp (string::grow1): Rename this... (string:embiggen): ...to this. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Continue refactoring to implement new invariant: any existing `string` object is backed by "clean" storage. (string::move): Rewrite. Use `sfree_alloc()`, which also backs `operator=()`, on destination string to perform replacement allocation. Use memcpy(3) to copy contents from source string to destination. Use member function `clear()` on source string. Add assert(3)ion enforcing new invariant. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::operator=) (string::grow1, string::clear): Add assert(3)ion enforcing new invariant. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (sfree_alloc, srealloc): Be more fasitidious with heap memory; when "shortening" the length of an existing `string`, zero out the memory from the new length to the capacity (which is not reduced). 2026-06-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Continue refactoring to implement new invariant: any existing `string` object is backed by "clean" storage. (sfree_alloc, srealloc): Simplify special-case handling of replacement string having zero length. Deal with it as `salloc()` now does, using `initial_string_buffer_size` as the `amount` in that case. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Increase initial buffer size backing `string` objects from 8 to 64, and stop allocating double the requested length on initial or replacement allocations. An initial buffer size of 64 appears to balance build time against minimization of reallocations. (salloc, sfree_alloc): Allocate only `len` `char` elements, not `len * 2`, unless `len` equals zero. (sfree_alloc): Drop now-tautologous assert(3)ion. 2026-06-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Continue refactoring to implement new invariant: any existing `string` object is backed by "clean" storage. (sfree): After deleting old storage, allocate "clean" replacement new storage of `initial_string_buffer_size`, just as `salloc()` does when given a zero second argument. Also delete unused second function parameter. (string::~string): Stop calling `sfree()` (since it reallocates the backing storage) in favor of direct deletion, thus maintaining the invariant--it occurs only when the entire object is being destroyed. (string::operator=, string::move): Update to call `sfree()`with its new, simpler signature. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Use a more appropriate return type for `string::find()`: `ssize_t` instead of `size_t`, since the function can return `-1`. * src/include/stringclass.h: * src/libs/libgroff/string.cpp: Preprocessor-include C "<sys/types.h>" header file to ensure visibility of POSIX.1-2001 type `ssize_t`. * src/include/stringclass.h: Update function declaration. * src/libs/libgroff/string.cpp (string::find): Update function definition. * src/preproc/tbl/table.cpp: Preprocessor-include C "<sys/types.h>" header file. (table::add_entry): Retype `len` local variable from `size_t` to `ssize_t` to pacify `-Wsign-compare` GCC warning. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/stringclass.h: * src/libs/libgroff/string.cpp: Preprocessor-include C "<stddef.h>" header file to ensure visibility of ISO C99 type `size_t`. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/soelim/soelim.cpp (usage, set_location, do_so): Define function as `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/refer/command.cpp (get_word): * src/preproc/refer/ref.cpp (sortify_words) (sortify_word) (sortify_other) (sortify_title) (sortify_name) (sortify_date) (sortify_label) (reverse_names) (join_fields) (capitalize_field) (is_terminated): * src/preproc/refer/refer.cpp (rcompare): Define function as `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-06-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/refer/command.cpp (input_item::input_item): Populate member variable `buffer` with copy initialization. We shouldn't use `string::move()` because the string contents might not be heap-allocated in the first place. Problem found the hard way with refactoring of groff's `string` class. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> [refer]: Migrate to strdup(3). * src/preproc/refer/command.cpp: * src/preproc/refer/token.cpp: Preprocessor-include C "<string.h>" header file. * src/preproc/refer/command.cpp (input_item::input_item): * src/preproc/refer/token.cpp (init_ascii, init_letter) (init_two_char_letter, init_strings): Migrate from libgroff's `strsave()` to POSIX's strdup(3). Continues the long process of fixing Savannah #66672. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/refer/command.cpp (input_item::~input_item): The storage backing the `filename` pointer is allocated (via `strsave()`) by malloc(3), not `new`, so free it with free(3), not `delete[]`. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/tests/\ do-not-segv-when-backslash-R-in-text-block.sh: Revise test to stop using the presence of a "core" file as a failure oracle. Instead, pattern-match tbl(1)'s output for the `TE` macro call that gets issed only after the formerly buggy table processing completes successfully. Fixes <https://savannah.gnu.org/bugs/?68204>. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/tbl/tests/\ do-not-segv-on-invalid-vertical-span-entry.sh: Revise test to stop using the presence of a "core" file as a failure oracle. Instead, pattern-match tbl(1)'s output for the `TE` macro call that gets issed only after the formerly buggy table processing completes successfully. Continues fixing <https://savannah.gnu.org/bugs/?68204>. 2026-06-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/tests/\ do-not-crash-when-reading-macro-arguments.sh: Revise test to stop using the presence of a "core" file as a failure oracle. Instead, pattern-match pic(1)'s output for the `PE` *roff macro call that gets issed only after the formerly buggy macro processing completes successfully. Begins fixing <https://savannah.gnu.org/bugs/?68204>. 2026-06-07 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/count-macro-arguments-correctly.sh: Add test verifying that macro arguments are counted correctly even in the presence of right-brace escape sequences. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-06-07 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (decode_macro_call_arguments): Handle right-brace escape sequences in macro arguments consistently with past groffs, and Solaris 10, DWB 3.3, and Heirloom Doctools troffs (but not _quite_ like Plan 9 troff, whose macro argument handling differs from all the others). Fixes <https://savannah.gnu.org/bugs/?68430>. Problem introduced by me in commit ebf943aa66, 2026-04-20. Also completes fixing <https://savannah.gnu.org/bugs/?42675>. 2026-06-07 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68430. * src/roff/groff/tests/\ handle-right-brace-escape-as-macro-argument.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (assign_control_character_request) (assign_no_break_control_character_request): Fix (further) blunders in assert(3) usage. Spurious assertion failures occurred when user attempted to set a control character to one that is already in use as the other control character, or the escape character. Fixes <https://savannah.gnu.org/bugs/?68420>. Problem introduced by me in commit 20feac542d, 2025-11-23. Thanks to Martin Simmons for the report. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68420. * src/roff/groff/tests/c2-request-does-not-fail-assertion.sh: * src/roff/groff/tests/cc-request-does-not-fail-assertion.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run tests. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Migrate to ISO C++98 Boolean literals. * src/devices/grohtml/html-table.cpp: * src/devices/grohtml/html-text.cpp: * src/devices/grohtml/output.cpp: * src/devices/grohtml/post-html.cpp: Kill preprocessor-based definitions of `TRUE` and `FALSE` macros. Replace macros `TRUE` and `FALSE` with language literals `true` and `false`, respectively. Continues the long process of fixing Savannah #66672. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Be more fastidious with heap-allocated memory. * src/devices/grohtml/post-html.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. Declare and initialize variables outside of `try`/`catch` blocks. Initialize such variables with null pointers where appropriate. (files::add_new_file) (text_glob::text_glob_html) (text_glob::text_glob_special) (text_glob::text_glob_line) (text_glob::text_glob_auto_image) (text_glob::text_glob_tag) (list:add) (list:insert) (page::page) (page::add) (page::add_tag) (page::add_line) (html_font::load_html_font) (assert_state::add) (generate_img_src) (html_printer::write_header) (html_printer::lookahead_for_tables) (html_printer::flush_page) (html_printer::draw) (html_printer::html_printer) (html_printer::begin_page) (get_str) (main): Do it. Continues the long process of fixing Savannah #68192. 2026-06-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp: Fix memory leak. Preprocessor-include C++ "<stack>" header file. (class char_buffer): Add new private member variable `strings` of type `std::stack<char *>`, to keep track of the strdup(3)ped pointers that our `add_string()` member functions return. (char_buffer::char_buffer): Initialize `strings`. (char_buffer::~char_buffer): Pop `strings` stack, free(3)ing strdup(3)ped pointers until empty. (char_buffer::add_string): strdup(3) the `char *` argument and push it onto the `strings` stack before returning. 2026-06-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp: Refactor. New `static` `const` variable of type `size_t`, `char_buffer_block_size`, does what's written on the tin. (struct char_block): Drop; it has been used exclusively by the file-local `char_buffer` class. (class char_buffer): Replace `head` and `tail` private member variables--pointers to `char_block`s, with an STL vector of `char` called `vec`. (char_buffer::char_buffer): Drop initializers of `head` and `tail`. Populate body with call of `std::vector<char>` constructor. (char_buffer::~char_buffer): Empty destructor body; being an STL container, we expect `vec` to dispose of its own backing storage when it goes out of scope (when its parent object, a `char_buffer`, is destroyed). (char_buffer::add_string): Rewrite to idiomatically populate `vec`, resizing it by `char_buffer_block_size` as needed. Stop returning a pointer into the data backing the STL vector, as its `resize()` operation might alter its memory addresses, as realloc(3) can. Return a strdup(3)-ed copy of the string instead. Continues the long process of fixing Savannah #66672. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp: Improve type-correctness. `html` is an object of type `simple_output` which contains a `word_list`, which contains a list of `word`s, each of which stores its contents in a `char *`, _not_ objects of groff's `string` class. (html_printer::write_html_anchor) (html_printer::write_xhtml_anchor) (html_printer::write_header): Populate `html` object with `contents()` of `header.header_buffer` object. (html_printer::do_heading): Catenate `header.header_buffer` with `g->text_string`, not a groff `string` constructed therefrom. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (page::add_encode): Add null terminator to `html_string` groff `string`. Its contents are shortly to become a C string. Null-terminating it prevents trailing garbage from populating a `char_buffer`. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Fix code style nit. * src/devices/grohtml/post-html.cpp (char_block::~char_block) (text_glob::~text_glob) (text_glob::remember_table) (element_list::~element_list) (list::sub_move_right) (replace_negate_str) (replace_str) (html_printer::lookahead_for_tables) (html_printer::draw): Simplify. Don't wrap `delete` operations with null pointer checks. It is not necessary to make conditional the deletion (or array deletion) of a null pointer. "If the _delete-expression_ calls the implementation deallocation function (3.7.3.2), and if the operand of the delete expression is not the null pointer constant, the deallocation function will deallocate the storage referenced by the pointer thus rendering the pointer invalid" {ISO/IEC 14882-1998, §5.3.5, paragraph 4}. Or as Stroustrup puts it, "Applying _delete_ to zero has no effect." (_The C++ Programming Language, Special Edition_, p. 128). 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp: Handle `NDEBUG` builds better. (text_glob::get_arg) (list::is_less) (page::add_and_encode) (header_desc::write_headings) (assert_state:add) (assert_state:compare) (assert_state:close) (replace_negate_str) (assert_state:set) (assert_state:build) (assert_state:check_value) (html_printer::is_bold) (html_printer::make_bold) (html_printer::emit_raw) (html_printer::handle_tag_within_title) (html_printer::do_center) (exists) (html_printer::do_auto_image) (html_printer::do_heading) (html_printer::do_linelength) (html_printer::do_pageoffset) (html_printer::do_indentation) (html_printer::do_tempindent) (html_printer::do_verticalspacing) (html_printer::do_pointsize) (html_printer::do_fill) (html_printer::do_head) (html_printer::do_space) (html_printer::do_tab_ts) (html_printer::do_tab) (html_printer::do_col) (html_printer::troff_tag) (html_printer::do_math) (html_printer::calc_po_in) (html_printer::determine_space) (html_printer::end_font) (html_printer::start_font) (html_printer::do_font) (html_printer::start_subscript) (html_printer::start_superscript) (html_printer::end_subscript) (html_printer::end_superscript) (html_printer::do_end_para) (html_printer::emit_html) (html_printer::set_line_thickness) (html_printer::draw) (html_printer::sbuf_continuation) (html_printer::overstrike) (html_printer::set_char) (html_printer::set_numbered_char) (html_printer::set_char_and_width) (html_printer::emit_link) (get_str) (make_val) (html_printer::special) (html_printer::devtag) (main): After (almost) every assert(3)ion, add appropriate {usually complementary} tests and return early from functions when invariants are not maintained. (html_printer::add_table_end) [DEBUG_TABLES]: Replace assert(3)ion with fatal error, since we know we're doing highly targeted debugging. (html_printer::do_file_components): Avoid null pointer dereference of `current_paragraph` if `NDEBUG` defined; simply guard the dereference and otherwise proceed normally since this function is responsible for writing out a lot of high-level HTML structure, and failing to mark an invalid/nonexistent paragraph as "done" seems relatively harmless. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::do_job_name): Add paranoia. Add assert(3)ion that our (C string) argument is not a null pointer. For `NDEBUG` builds, return early if it is. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (generate_img_src): Add paranoia. Ensure we don't dereference a null `filename` pointer. (Strictly, the `static` function `exists()` handles that case for us, but my idea of design-by-contract permits sanity checks on both ends of a call. This choice also gives us flexibility to replace or change `exists()`, possibly with a standard library function call.) 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp: Fix code style nit. (main): Return using exit(3), not `return`. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Be more fastidious with heap-allocated memory. * src/devices/grohtml/output.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (word::word): Slightly refactor. Introduce new local variable `amount` to track the count of `char`s we wish to allocate, so that we can use it both in a `new` allocation and in the fatal diagnostic thrown if that allocation fails. (word::word): (word_list::add_word): Catch `std::bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Fix code style nit. * src/devices/grohtml/output.cpp (word::word) (word_list::word_list) (word_list::add_list): Do it. Use idiomatic C++98 null pointer literal instead of C's `NULL`. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grohtml]: Be more fastidious with heap-allocated memory. * src/devices/grohtml/html-table.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor- include C++ "<new>" header file and project-local "errarg.h" and "error.h" header files. (tabs::init): Slightly refactor. Introduce new local variable `newtab` (a pointer to `tab_position`, lifting 2 identical attempted memory allocations out of both branches of an if/else. (tabs::init): (html_table::html_table): (html_table::insert_column): (html_indent::html_indent): Catch `std::bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::determine_header_level): Fix code style nit. Isolate loop variable within `for` loop scope. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (string::remove_spaces): Fix code style nit. Parenthesize formally complex expression. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Back all strings with clean storage. And ensure that all `string`s are backed by storage such that the capacity `sz` is always nonzero, and the `contents()` member function never returns a null pointer. Add assert(3)ions to enforce these invariants. New `static` `const` variable `initial_string_buffer_size` does what it says, reserving a minimum backing allocation even for empty strings. (salloc): Discard zero-length code, except insofar as a zero-length allocation request gets `initial_string_buffer_size` `char` array elements. (string::string): Discard now tautologously true `if` tests. Drop early return from constructor taking only a `char *` argument, so that we can assert(3) a postcondition invariant. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> Refactor groff's bespoke `string` class to use `size_t` instead of `int` to track array sizes and indices. * src/include/stringclass.h: Update declarations and `inline` function definitions. * src/libs/libgroff/string.cpp: Update definitions and local variables. Drop now tautologically true assert(3)ions. (string::remove_spaces): This function searches for spaces backward from the end of the string contents. Alter loop invariant from a non-negative array index (now impossible) to one that is less than the array length. Discard now-unreachable logic branch. * src/preproc/eqn/lex.cpp (add_quoted_context): * src/preproc/preconv/preconv.cpp (get_tag_lines): * src/preproc/refer/label.ypp (format_serial) (alternative_expr::evaluate) (substitute_expr::evaluate) (int_set::set) (int_set::get): * src/preproc/refer/ref.cpp (reference::canonicalize_authors): * src/preproc/tbl/table.cpp (table::add_entry): Retype variables populated by or compared to applicable `string` member functions from `int` to `size_t`. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Drop unnecessary prototypes of functions that lack external linkage (they're `static`) and do not require forward declarations. 2026-06-06 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Boolify groff's `string` class. * src/include/stringclass.h: * src/libs/libgroff/string.cpp: Demote return values of `operator==()`, `operator!=()`, `empty()`, `operator<=()`, `operator<()`, `operator>=()`, and `operator>()` from `int` to `bool`. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. Preprocessor-include "error.h" and its prerequisite "errarg.h" to make visible the `fatal()` symbol. (salloc, sfree_alloc, srealloc, string::remove_spaces): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp (salloc, sfree_alloc, srealloc) (string::string, string::clear, string::remove_spaces): Zero out memory allocations from the heap, and when user requests erasure of `string` contents. 2026-06-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/string.cpp: Fix code style nits. Parenthesize formally complex expressions. Arrange equality comparisons to avoid inadvertent lvalue assignment (exception: in overloads of comparison operators, retain "risky" arrangement to keep structural parallels and contrasts clear). 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-table.cpp (tabs::tabs) (tabs::delete_list) (tabs::compatible) (tabs::init) (tabs::find_tab) (tabs::get_tab_pos) (tabs::get_tab_align) (tabs::dump_tabs) (html_table::html_table) (html_table::remove_cols) (html_table::set_linelength) (html_table::get_effective_linelength) (html_table::emit_table_header) (html_table::get_right) (html_table::emit_colspan) (html_table::emit_td) (html_table::emit_col) (html_table::finish_row) (html_table::emit_new_row) (html_table::add_column) (html_table::get_column) (html_table::insert_column) (html_table::modify_column) (html_table::find_column) (html_table::no_columns) (html_table::is_gap) (html_table::no_gaps) (html_table::dump_table): Fix code style nit. Use idiomatic C++98 null pointer literal instead of C's `NULL`. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [pic]: Rationalize header file inclusions. Move some into "object.h" or "pic.h" where not doing so introduces cycles. * src/preproc/pic/output.h: Preprocessor-include "text.h". * src/preproc/pic/lex.cpp: * src/preproc/pic/main.cpp: * src/preproc/pic/object.cpp: * src/preproc/pic/output.cpp: * src/preproc/pic/pic.h: * src/preproc/pic/pic.ypp: * src/preproc/pic/tex.cpp: * src/preproc/pic/troff.cpp: Arrange inclusions of "libgroff" header files before pic-specific ones. Within such inclusions, sort header file names in alphabetical order, where the header file dependency graph permits. Annotate exceptions. * src/preproc/pic/main.cpp: Preprocessor-include "<limits.h>" to ensure visiblity of `CHAR_MAX` symbol. * src/preproc/pic/pic.h: Declare `extern` symbols from "lex.cpp" here, not in "pic.ypp". 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [pic]: Fix code style nit. * src/preproc/pic/lex.cpp (process_body) (interpolate_macro_with_args, lookup_keyword) (get_token_after_dot, get_token, get_delimited, do_define) (do_undef, get_thru_arg): * src/preproc/pic/main.cpp (do_picture, usage): * src/preproc/pic/object.cpp (draw_arrow): * src/preproc/pic/troff.cpp (strsame): Define functions as `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/pic/lex.cpp: Fix code style nit. Add `extern` declarations of `yylex()` and `yyerror()` since--as I understand it--there is still no standard header file that prototypes them. Fixes `-Wmissing-declarations` GCC warning. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grn]: Give command a proper header file for external symbols. * src/preproc/grn/hdb.cpp: * src/preproc/grn/hpoint.cpp: * src/preproc/grn/main.cpp: Move `extern` declarations of symbols from here... * src/preproc/grn/grn.h: ...to this new file. * src/preproc/grn/hdb.cpp: * src/preproc/grn/hgraph.cpp: * src/preproc/grn/hpoint.cpp: * src/preproc/grn/main.cpp: Preprocessor-include new file. * src/preproc/grn/grn.am: Add new file to `grn_SOURCES` macro definition. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/grn/main.cpp: Rationalize header file inclusions. Arrange inclusions of "libgroff" header files before grn- specific one. Within such inclusions, sort header file names in alphabetical order (where the header file dependency graph permits). 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [grn]: Rearrange code. * src/preproc/grn/main.cpp (operand): Relocate definition to precede function housing its only call site (`main()`). Define functions as `static`; external linkage is not required. (main): Drop now unnecessary function-local declaration of `operand()`. * src/preproc/grn/hdb.cpp (DBCreateElt): * src/preproc/grn/hgraph.cpp (line, change): * src/preproc/grn/main.cpp (usage, add_file, initpic): Define functions as `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-05-31 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn]: Fix code style nit. * src/preproc/eqn/main.cpp (do_file, usage): * src/preproc/eqn/lex.cpp (flush_context, show_context) (add_context, add_quoted_context, get_delimited_text) (interpolate_macro_with_args, get_token, do_include) (ignore_definition, do_definition, do_undef, do_gsize, do_gfont) (do_gifont, do_grfont, do_gbfont, do_space, do_ifdef, do_delim) (do_chartype, do_set, do_reset): * src/preproc/eqn/text.cpp (special_to_entity): Define functions as `static`; external linkage is not required. * src/preproc/eqn/lex.cpp: Add `extern` declarations of `yylex()` and `yyerror()` since--as I understand it--there is still no standard header file that prototypes them. Fixes `-Wmissing-declarations` GCC warning. 2026-05-30 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libbib/common.cpp: Preprocessor-include "index.h" to make visible prototype of `hash()`. Fixes `-Wmissing-declarations` GCC warning. 2026-05-30 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Trivially refactor. Rename string `an-pageref` to `an*pageref`. (PT, an*abbreviate-page-identifier, an-footer) ([initialization]): Update definition and interpolation sites. 2026-05-30 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (LE): Throw warning when macro called with no lists open. 2026-05-30 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Trivially refactor. (an-warn): Rename this... (an*warn): ...to this. (an*abbreviate-inner-footer, SH, SS, TP, MT, ME, UR, UE, LS) (TE): Update call sites. 2026-05-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (suppress_node::tprint): Migrate from libgroff's `strsave()` to POSIX's strdup(3). Continues the long process of fixing Savannah #66672. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (top_level_diversion::remove_trap) (top_level_diversion::remove_trap_at) (top_level_diversion::change_trap) (top_level_diversion::print_traps): Fix code style nit. Explicitly compare variables of pointer type to null pointer literal instead of letting them pun down to Booleans. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (top_level_diversion::add_trap) (top_level_diversion::remove_trap) (macro_diversion::set_diversion_trap): Add assert(3)ions and {if `#define NDEBUG`} early returns if the `symbol` argument `is_null()`. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (when_request, diversion_trap_request): Enforce space requirement between first and second arguments to `wh` and `dt` requests. * doc/groff.texi.in (Other Differences): * man/groff_diff.7.man (Other differences): * NEWS: Document this behavioral difference from AT&T troffs. Fixes <https://savannah.gnu.org/bugs/?68357>. Problem appears to date back to groff's birth. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68357. * src/roff/groff/tests/\ error-no-space-between-dt-or-wh-arguments.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `wh` and `ch` requests. * src/roff/groff/tests/wh-and-ch-requests-work.sh: Do it. * src/roff/groff/groff.am: Run test. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (has_arg): Revise semantics. Function now returns `false` if the current token is anything but a space. 2026-05-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (set_page_number_request): Refactor-- trivially now but not so in the near future. Stop calling `has_arg()` redundantly. A forthcoming change will alter the semantics of `has_arg()` to return true only if an _argument_ lies ahead in the input stream, and a space must occur before any _argument_. `has_arg()`, when not "peeking", advances the input stream cursor, and this function has already called it (to throw a diagnostic if the argument is omitted), so the cursor is already at the argument's first character. The argument is at {or under} the formatter's feet, and does not "lie ahead". Manual inspection suggests that other request handlers are already careful not to call `has_arg()` except (1) upon receiving control from the request-handler dispatcher (via a function pointer); or (2) after calling a function that itself advances the input stream cursor, like `read_identifier()`. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> [tfmtodit]: Be more fastidious with heap-allocated memory. * src/utils/tfmtodit/tfmtodit.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (tfm::load): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> [tfmtodit]: Be more fastidious with heap-allocated memory. * src/utils/tfmtodit/tfmtodit.cpp (struct char_list): Declare destructor. (char_list::~char_list): Define destructor free(3)ing memory allocated by strdup(3) in constructor. (main): Handle failure of strdup(3) as a fatal error. free(3) the storage it allocates when done with it. Continues the long process of fixing Savannah #68192. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (char_list::char_list, main): Migrate from libgroff's `strsave()` to POSIX's strdup(3). Continues the long process of fixing Savannah #66672. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (main): Use ISO C standard constant `M_PI` instead of our own `PI`. Continues the long process of fixing Savannah #66672. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (read2, read4, read_map): Fix code style nit. Define functions as `static`; external linkage is not required. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (usage): Relocate function definition to obviate need for forward declaration. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (tfm::load, gf::load) (read_map): Characterize files being read as "invalid", not "bad". 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp: Boolify. (class tfm): Demote member function declaration return types for `load()`, `contains()`, `get_param()`, and `get_lig()` from `int` to `bool`. (class kern_iterator): Demote member function declaration return type for `next()` from `int` to `bool`. (class gf): Demote member function declaration return types for `skip()` and `load()` from `int` to `bool`. (kern_iterator::next) (tfm::get_lig) (tfm::contains) (tfm::get_param) (tfm::load) (gf::load) (gf::skip): Update definitions. Return Boolean, not integer, literals. (read_map): Demote return type from `int` to `bool`. (tfm::load): Declare local variable closer to point of use. (main): Demote local variable `special_flag` from `int` to `bool` and rename it to `is_font_special`. Assign to it using Boolean, not integer, literals. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (tfm::load, gf::load) (read_map, main): Revise diagnostic messages to more closely match terminology from our documentation and model language from elsewhere in the source tree. Continues the long process of fixing Savannah #66519. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (tfm:load): Fix code style nits. Parenthesize formally complex expressions. Explicitly compare variables of pointer type to null pointer literal instead of letting them pun down to Booleans. Arrange equality comparisons to avoid inadvertent lvalue assignment. (main): Consistently use exit(3) to return from `main()` instead of `return`. Simplify an `error()`+`return` combination to a `fatal()` call. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/tfmtodit/tfmtodit.cpp (kern_iterator::next) (tfm::get_lig) (tfm::get_italic) (tfm::load) (gf::load) (main): Fix code style nits. Parenthesize formally complex expressions. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> [grodvi]: Slightly refactor. * src/devices/grodvi/dvi.cpp: Use ISO C++98 exceptions to handle heap storage allocation failures. Preprocessor-include C++ "<new>" header file. (dvi_font::load_dvi_font): Catch `std:bad_alloc` exception and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-05-25 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Extend argument printer for diagnostic messages to handle object of type `unsigned long int`, so that we can report `sizeof` values messages, perhaps among other purposes. * src/include/errarg.h (class errarg): Extend `type` enumeration type to support `UNSIGNED_LONG_INTEGER` constant. Add member `l` of type `unsigned long int` to anonymous union. Declare constructor taking argument of `unsigned long int` type. * src/libs/libgroff/errarg.cpp (errarg:errargs): Define new constructor taking argument of `unsigned long int` type. (errarg::print): Handle case of `UNSIGNED_LONG_INTEGER`, delegating printing to fprintf(3). 2026-05-25 Deri James <deri@chuzzlewit.myzen.co.uk> [gropdf]: Handle URI encoded characters (%nn) correctly. * src/devices/gropdf/gropdf.pl (LoadPDF): A `%` is used to introduce a comment in a PDF, but it may also be used to hex encode a character in a URI of an external link. Page 620 of the PDF 1.7 standard has a NOTE about this. Fix it. 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> When building outside of the source tree and with the configure script's dependency tracking disabled, work harder to ensure that the parent directories of build artifacts exist before attempting to populate them. * font/devpdf/devpdf.am (font/devpdf/SS): * src/libs/libgroff/libgroff.am (src/libs/libgroff/version.cpp): Do it. Fixes <https://savannah.gnu.org/bugs/?68372>. Thanks to Ross Burton for the report. 2026-05-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Refactor. Convert `while` loop to `do-while` loop. (1) We already know at first loop entry that the condition is true and (2) structuring it this way makes more sense given a planned revision to `has_arg()` to tighten up its semantics. 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (set_hyphenation_codes): Refactor in preparation for firming-up of the semantics and logic of request arguments to constitute space-separated lexical tokens, as our documentation defines "argument". Call `token::skip_spaces()` when "pumping" the input stream and govern the input-reading loop using `token::is_newline()` and `token::is_eof()` instead of `has_arg()`. 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (set_character_flags_request): Refactor; use `token::is_any_character()` instead of `has_arg()` as loop control, in preparation for firming-up of the semantics and logic of request arguments to constitute space-separated lexical tokens, as our documentation defines "argument". 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (set_hyphenation_codes): Throw error if either the "source" or "destination" operand of a hyphenation code assignment pair it not a character token. 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (define_font_specific_character_request): Check for presence of arguments after the first before handing off processing of the remainder of the input line to `define_character()`. Resolves assertion failure thrown by the latter function. Fixes <https://savannah.gnu.org/bugs/?68395>. Problem introduced by me in commit f87b107497, 20 November. 2026-05-24 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68395. * src/roff/groff/tests/\ do-not-crash-on-missing-fschar-argument.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-05-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (escape_off_request) (save_escape_char_request) (restore_escape_char_request) (assign_control_character_request) (assign_no_break_control_character_request) (backtrace_request) (exit_request) (return_macro_request) (do_request) (flush_pending_lines) [WIDOW_CONTROL] (print_macro_request) (read_from_terminal_request) (ignore) (remove_macro) (rename_macro) (alias_macro) (substring_request) (length_request) (unformat_macro) (line_file) (source_request) (source_quietly_request) (pipe_source_request) (ps_bbox_request) (tag) (taga) (warnscale_request) (spreadwarn_request) (translate) (translate_no_transparent) (translate_input) (hyphenation_patterns_file_code) (abort_request) (pipe_output) (system_request) (vjustify) [COLUMN] (macro_source_request) (macro_source_quietly_request) (configure_desired_warnings_request): Fix code style nits; declare as `static`; external linkage is not required. 2026-05-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (stringdown_request) (stringup_request): Fix code style nits; declare as `static`; external linkage is not required. 2026-05-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor and fix code style nits. (next_file) (shift) (compatible): Rename these... (next_file_request) (shift_request) (configure_att_compatibility_mode_request): ...to these, and declare them as `static`; external linkage is not required. (init_input_requests): Update wire-uppery of request names to handler functions. 2026-05-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Fix code style nits. (do_register_assignment, do_string_assignment): Explicitly compare variable of pointer type to null pointer literal instead of letting it pun down to a Boolean. 2026-05-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Refactor to rationalize and parallelize vertical position trap request handlers. (when_request): Check for presence of arguments; if none, throw warning in category "missing". (diversion_trap_request): Clear any existing diversion trap if given a valid vertical position argument but no second argument, analogously to `ch` request. 2026-05-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.h (class diversion): Declare new pure virtual member function `print_diversion_trap()` returning `void`. (class macro_diversion) (class top_level_diversion): Declare member function `print_diversion_trap()`. * src/roff/troff/div.cpp (macro_diversion::print_diversion_trap): New member function reports properties of a diversion trap if one exists. (top_level_diversion::print_diversion_trap): New member function throws assert(3)ion if called. Internal logic should ensure that such a call is impossible. (print_traps_request): Call the `print_traps()` member function of `topdiv` (as before) only if it is the current diversion. Otherwise, the current diversion is a macro diversion, so call its `print_diversion_trap()` member function. * doc/groff.texi.in (Diversion Traps, Debugging): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): Document it. * src/roff/groff/tests/pwh-request-works.sh: Unit-test request. * src/roff/groff/groff.am (groff_TESTS): Run test. Fixes <https://savannah.gnu.org/bugs/?68358>. 2026-05-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (char_list::get): De-optimize read access at the end of a `char_list`, because the pointer arithmetic used can access heap storage beyond the end of the underlying `char_block` used to house the list. Fixes <https://savannah.gnu.org/bugs/?68340>. Resolves ASAN runtime error when formatting the groff_mdoc(7) man page in troff mode. Thanks to Bjarni Ingi Gislason for the report. 2026-05-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor and fix code style nit. (eoi_macro) (blank_line_macro) (leading_spaces_macro): Rename these... (configure_end_of_input_macro_request) (configure_blank_line_macro_request) (configure_leading_spaces_macro_request): ...to these. (configure_end_of_input_macro_request) (configure_blank_line_macro_request) (configure_leading_spaces_macro_request): Declare as `static`; external linkage is not required. (init_input_requests): Update wire-uppery of request names to handler functions. 2026-05-16 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Implement `LS`/`LE` indentation management. * tmac/an.tmac (TP, IP, HP): If macro called in a list context {the "list depth" is greater than zero}, and an indentation for the current list depth is defined, set the prevailing indent to that indentation. {We still, and subsequently, override the prevailing indent with the indentation argument to these macros, if present.} * tmac/groff_man.7.man.in (List enclosure macros): Document it. * tmac/tests/an_LS-and-LE-work.sh: Unit-test effect of list indentation on `HP` macro behavior. Update test expecations of `TP` behavior now that indentation is inherited. Apply a tab-to-space-translating tr(1) command on the test output, fearing inconsistencies among Unices in how nl(1) prefixes the lines it filters. Continues fixing <https://savannah.gnu.org/bugs/?68335>. 2026-05-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp: Preprocessor-include "<string.h>" header file, since strcmp(3) and strncmp(3) are used (the former only in `#if 0`-ed code). 2026-05-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-text.cpp: Preprocessor-include "<string.h>" header file, since strcmp(3) and strlen(3) are used. * src/devices/grohtml/post-html.cpp: Preprocessor-include "<strings.h>" header file, since strcasecmp(3) is used. 2026-05-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/preconv/preconv.cpp: Preprocessor-include "<strings.h>" header file, since strcasecmp(3) is used. 2026-05-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/post-html.cpp (html_printer::write_navigation): Fix code style nit. Parenthesize formally complex expressions. 2026-05-13 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/tests/an_MR-works.sh: Validate behavior of macro when given only one argument. Prompted by GitHub user avih in <https://github.com/gwsw/less/pull/770>. 2026-05-13 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/tests/an_MR-works.sh: Future-proof test against more spaceful "grout" (still within CSTR #54/#97 specifications). See <https://savannah.gnu.org/bugs/?63544>. 2026-05-15 G. Branden Robinson <g.branden.robinson@gmail.com> [hpftodit]: Give command a proper header file for external symbol. * src/utils/hpftodit/hpftodit.cpp: Move `extern` declaration of symbol defined in "hpuni.cpp" from here... * src/utils/hpftodit/hpftodit.h: ...to this new file. * src/utils/hpftodit/hpftodit.cpp: * src/utils/hpftodit/hpuni.cpp: Preprocessor-include new file. * src/utils/hpftodit/hpftodit.am: Add new file to `hpftodit_SOURCES` macro definition. 2026-05-15 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Give command a proper header file for external symbols. * src/roff/groff/pipeline.c: Move `extern` declarations of symbols defined in "groff.cpp" from here... * src/roff/groff/groff.h: ...to this new file. * src/roff/groff/groff.cpp: * src/roff/groff/pipeline.c: Preprocessor-include new file. * src/roff/groff/groff.cpp: Wrap this inclusion in `extern "C"` brace scope to indicate need for C-compatible calling conventions. * src/roff/groff/groff.am: Add new file to `groff_SOURCES` macro definition. Fixes `-Wmissing-declarations` GCC warning. 2026-05-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/html/pre-html.cpp (html_system, make_string) (min, max, addArg, addRegDef, print_args): * src/preproc/html/pushback.cpp (localexit): Declare `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-05-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-text.cpp: Slightly refactor. Use ISO C++98 exceptions to handle heap storage allocation failures. Align code style a bit more with groff conventions. Preprocessor-include C++ "<new>" header file. (html_text::push_para, html_text::do_para): Catch `std:bad_alloc` exceptions and `fatal()` out with an attempt to describe what we were doing. Continues the long process of fixing Savannah #68192. 2026-05-11 Deri James <deri@chuzzlewit.myzen.co.uk> [ms]: Add PDF support. * tmac/s.tmac: Depend on register `PDFFEAT` set to 1 to add features. Allowed only with `-T pdf`. Alias new `XR`/`XM` macros to LP (to prevent use before ms initialises). (XR/@XR): New macro implements internal hyperlinks. (XM/@XM): New macro 'marks' destination for internal links. (RP): Name 'title' page, used to relocate TOC in .TC. (cov*ab-init): Turn on XR/XM. (cov*print): Extract title and author for PDF metadata. (@SH, @NH): New second parameter names bookmark; XR can link to it. Set up box diversion to collect bookmark text. (@NH-end): Diversion trap springs when NH/SH has collected one line of _output_. (XA): Start hotlink text (in TOC). (XE): Stop marking hotlink. (TC): If .RP seen, move TOC to page after 'title'. * doc/ms.ms.in: Call RP so that the TOC moves after the cover. Name sections throughout to support hyperlinking. Change man page references to hyperlinks. (Abstract): Mention PDF features. (Introduction): Enable PDF features with PDFFEAT register. (Document Structure): Change references to hotlinks. (Tables of Contents): Explain how to move TOC. (Headings) <NH, SH>: Document new 'name' parameter. (Using PDF features with ms): New. * tmac/groff_ms.7.man: * doc/groff.texi.in: Sync with the foregoing. * doc/Groff-PDF-Features.ms: New file documents PDF features in pdf.tmac and macro packages mom, "man", mdoc, ms (and their absence in "me" and mm). * doc/doc.am: Build the new file above and switch ms.ms and pic.ms to generate PDFs with `PDFFEAT` enabled. * NEWS: Announce changes to groff ms(7) and acknowledge antecedent work in spdf.tmac (now externally maintained). Fixes <https://savannah.gnu.org/bugs/?68299>. 2026-05-11 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Rename test scripts to accommodate case-insensitive file systems. * src/roff/groff/tests/backslash-l-works.sh: * src/roff/groff/tests/backslash-L-works.sh: Rename these... * src/roff/groff/tests/backslash-lowercase-l-works.sh: * src/roff/groff/tests/backslash-uppercase-L-works.sh: ...to these. * src/roff/groff/groff.am (groff_TESTS): Reflect rename. Fixes <https://savannah.gnu.org/bugs/?68327>. Thanks to John Gardner for the report. Problem introduced by me in commit 58adafa251, 22 April. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/grog/grog.pl: Account for new `LS` and `LE` man(7) macros. (interpret_line): Drop `LE` from list of mm(7)-exclusive macros. Drop `LS` from list of mom(7)-exclusive macros. See <https://savannah.gnu.org/bugs/?68335>. 2026-05-11 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Unit-test new `LS` and `LE` macros. * tmac/tests/an_LS-and-LE-work.sh: Do it. * tmac/tmac.am (tmac_TESTS): Run test. 2026-05-11 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Begin implementing new `LS` and `LE` macros. * tmac/an.tmac (LS): New interface macro starts (opens) a list enclosure. (LE): New interface macro ends (closes) a list enclosure. (an*list-close-all): New macro recursively closes all lists. (an*end-document, SH, SS): Call `an*list-close-all`. (an*break-paragraph): Recognize argument to permit override of inter-paragraph spacing application by checking list context for "compactness". (TP, IP, HP): Call `an*break-paragraph` with an argument, thereby checking for override of inter-paragraph spacing application. * tmac/groff_man.7.man.in (Description): Document it. Add new macro names to initial table thereof. (List enclosure macros): New subsection specifies them. Begins fixing <https://savannah.gnu.org/bugs/?68335>. 2026-05-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/xditview.am: Fix race when running "install" Make target with concurrency. This Automake file was duplicatively claiming responsibility for installing the "tty.tmac" and "tty-char.tmac" files, which are properly within _grotty_'s demense exclusively. Drop definitions of `GXDITVIEWTMACFILES`, `gxditviewtmacdir`, and `dist_gxditviewtmac_DATA` macros. (uninstall_xditview): Drop now superfluous rmdir(1) from recipe. Fixes <https://savannah.gnu.org/bugs/?68336>. Thanks to Santiago Vila for the report and analysis. Problem introduced by me in commit 8910c63b6d, 2024-12-10. 2026-05-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-text.cpp: Fix code style nits. (html_text::html_text) (html_text::dump_stack_element) (html_text::dump_stack) (html_text::end_tag) (html_text::start_tag) (html_text::flush_text) (html_text::is_present) (html_text::uses_indent) (html_text::do_push) (html_text::push_para) (html_text::do_pre) (html_text::shutdown) (html_text::check_emit_text) (html_text::do_para) (html_text::remove_indent) (html_text::remove_def) (html_text::remove_break) (html_text::remove_para_align) (html_text::get_alignment): Use idiomatic C++98 null pointer literal. (html_text::dump_stack_element) (html_text::end_tag) (html_text::start_tag) (html_text::do_push) (html_text::shutdown) (html_text::do_para) (html_text::remove_def) (html_text::remove_break): Arrange equality comparisons to avoid inadvertent lvalue assignment. (html_text::do_para) (html_text::remove_para_align) (html_text::get_alignment): Parenthesize formally complex expressions. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (IP): Slightly refactor. Call `an*break-paragraph` only if there are no arguments. If arguments are present, we call `TP`, which already calls the aforementioned internal macro. This way we avoid calling it duplicatively. Right now there's no visible effect, but there is with forthcoming work to implement new `LS` and `LE` macros. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/grog/grog.pl (interpret_line): Recognize `pwh` instead of `ptr` as a GNU troff request name. That request- renaming was a late-breaking change in the 1.24.0 release cycle. Continues fixing <https://savannah.gnu.org/bugs/?67880>. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/doc.tmac: Recognize that using the "X100-12" or "X75-12" output devices necessarily implies an `S` register default of "12", not "10", just as groff man(7) has done for a long time. Fixes <https://savannah.gnu.org/bugs/?68334>. Problem dates back to introduction of mdoc(7) support for `S` register in commit b57a732836, 2001-03-24. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Define empty "bookmark handlers" for X100, X100-12, X75, and X75-12 devices. Continues commit d1a148f95c, 2024-01-27. Fixes <https://savannah.gnu.org/bugs/?68333>. 2026-05-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/device.c: Fix damage from code style nit picking, which rendered gxditview(1) ineffective. Repair sense of comparisons to null pointers, and apply parentheses where overlooked. Problem introduced by me in commit 111bf3255b, 8 May. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (an*reset-section-parameters) (an-write-paragraph-tag, P, TP, IP, HP, RS, RE): Trivially refactor; rename registers. - an-inset-level -> an*inset-level - an-margin -> an*margin - an-saved-margin!1 (etc.) -> an*saved-margin!1 (etc.) - an-prevailing-indent -> an*prevailing-indent - an-saved-prevailing-indent!1 (etc.) -> an*saved-prevailing-indent!1 (etc.) 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Make `-I` stop implying `-g` and `-s`. To date, specifying `-I` has been the only way (employing only groff(1) as a pipeline manager, a usage we strongly encourage) to get an `-M` option of the same meaning to grn. However, we didn't know, simply from the user's specification of `-I`, that they meant to pass a `-M` option to grn. Maybe they meant it for soelim(1). Maybe they don't need preprocessors at all. With grn(1) now accepting `-I`, groff(1) no longer needs to synthesize `-g` and `-s` to cover these bases. If the user demands the grn(1) and soelim(1) preprocessors, they can specify their options, consistently with other groff preprocessors supported by a command-line option. * src/roff/groff/groff.cpp (main) <I>: Do it. * doc/doc.am (doc/webpage.html, doc/webpage.ps): Drop now-unnecessary dependencies on "grn" and "soelim". * src/roff/groff/groff.1.man (groff-specific options) <I>: * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?68277>. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> [grn]: Rename `-M` option to `-I`, better paralleling troff(1). Inclusions are not the same things as macro file resources. * src/preproc/grn/main.cpp (main): Accept `-I`, `-L` (the original Berkeley name for this option), and `-M` (groff grn prior to this point) synonymously. * src/roff/groff/groff.cpp (main): Pass `-I` options as-is to grn. Stop doing so for `-M` arguments. * doc/doc.am (doc/grnexmpl.ps): Pass groff option `-I` instead of `-M`. * src/preproc/grn/grn.1.man (Synopsis, Options): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?68276>. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (temporary_indent_request): If given no arguments, cancel any temporary indentation existing in the environment. Fixes <https://savannah.gnu.org/bugs/?68286>. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Trivially refactor. (temporary_indent): Rename this... (temporary_indent_request): ...to this. Also declare it `static`, as `friend` access via the `environment` class is now gone and so external linkage is unnecessary. (init_env_requests): Update wire-uppery of request name to handler function. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor. Separate engine logic from parsing logic with respect to an environment's temporary indentation. * src/roff/troff/env.h: Stop globally declaring `temporary_indent()`. (class environment): Declare new member functions returning `void`: `set_temporary_indent()` and `cancel_temporary_indent()`. Drop declaration of `friend` functions `temporary_indent()` and `cancel_temporary_indentation()`. * src/roff/troff/env.cpp (environment::set_temporary_indent) (environment::cancel_temporary_indent): New functions mutate object of `environment` class. (set_temporary_indentation): New function updates the environment through a mutator member function, much like the existing `cancel_temporary_indentation()` does. (temporary_indent): Use accessor member functions of `environment` class to get at object properties. Call `set_temporary_indentation()` instead of open-coding its operations. Continues the long process of fixing Savannah #68240. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (temporary_indent): Slightly refactor. Rename `temp` local variable to `amount` for clarity. Shift output line breaking logic earlier, separating it from argument handling. Drop redundant `skip_line()` call when handling no-argument case. Move declaration of `is_valid` local variable within its narrower applicable brace scope. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Rename `.zoom` register to `.fzoom`. * src/roff/troff/env.cpp (init_env_requests): Do it. * tmac/troffrc: Configure alias for backward compatibility. * doc/groff.texi.in (Selecting Fonts): * man/groff.7.man (Read-only registers): * man/groff_diff.7.man (New register): * NEWS: Document change. Fixes <https://savannah.gnu.org/bugs/?68288>. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> Relocate "pdfmom" in source tree. * src/devices/gropdf/pdfmom.1.man: * src/devices/gropdf/pdfmom.pl: Rename these... * src/utils/pdfmom/pdfmom.1.man: * src/utils/pdfmom/pdfmom.pl: ...to these. * src/devices/gropdf/gropdf.am: Move pdfmom-related stuff from here... * src/utils/pdfmom/pdfmom.am: ...into this new file. * Makefile.am: Include new Automake script. * doc/doc.am (GROFF_MAN_PAGES1): Update man page location. * MANIFEST: Update. Fixes <https://savannah.gnu.org/bugs/?68294>. 2026-05-05 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Fix code style nits. (decode_macro_call_arguments) (decode_escape_sequence_arguments) (read_from_terminal_request) (do_define_string) (define_character) (do_define_macro) (length_request) (read_size) (skip_branch) (is_conditional_expression_true) (while_request) (psbb_locator::get_line) (tag) (taga): Arrange equality comparisons to avoid inadvertent lvalue assignment. Parenthesize formally complex expressions. For clarity, compare characters (even if of `int` type for <stdio.h> reasons) to null character literal instead of numeric zero. (is_conditional_expression_true): More consistently cast character literals to `int` in comparisons. 2026-05-05 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (read_character_in_escape_sequence_parameter): Fix code style nits. Arrange equality comparisons to avoid inadvertent lvalue assignment. Return explicitly constructed `char` object from an `int` value rather than relying upon "the default conversions" of C and C++. 2026-05-05 G. Branden Robinson <g.branden.robinson@gmail.com> Correct some highly outdated Microsoft DOS/Windows port stuff. * arch/mingw/groffer.cmd: * arch/mingw/roff2dvi.cmd: * arch/mingw/roff2html.cmd: * arch/mingw/roff2pdf.cmd: * arch/mingw/roff2ps.cmd: * arch/mingw/roff2text.cmd: * arch/mingw/roff2x.cmd: Delete these remnants of "groffer", dropped from the groff distribution in commit 1d2df80a93, 2020-04-22. * arch/mingw/mingw.am: Update `WINSCRIPTS` macro to no longer specify them. * arch/djgpp/README: Update. 2026-05-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/device.c: Fix code style nits. Spell null pointer literal the idiomatic (pre-ISO C23) C way (`NULL`) instead of as `0`. Explicitly compare variables of pointer type to null pointer literal instead of letting them pun down to Booleans. Arrange equality comparisons to avoid inadvertent lvalue assignment. Explicitly compare variables of type `int` {that store error codes, for example} to integer literals instead of letting them pun down to Booleans. Parenthesize formally complex expressions. 2026-05-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/Dvi.c: * src/devices/xditview/device.c: * src/devices/xditview/font.c: Arrange inclusions of C standard library header files before X11 ones. Within such inclusions, sort header file names in alphabetical order. 2026-05-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp (tty_font::handle_x_command): Fix code style nit. Parenthesize formally complex expressions. 2026-05-07 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (ps_font::handle_unknown_font_command) (ps_font::handle_unknown_desc_command) (ps_printer::ps_printer) (ps_printer::set_char) (ps_printer::draw) (ps_printer::end_page) (ps_printer::~ps_printer) (ps_printer::special) (ps_printer::do_exec) (ps_printer::do_file) (ps_printer::do_def) (ps_printer::do_mdef) (ps_printer::do_import) (ps_printer::do_invis) (main): Revise diagnostic messages to more closely match terminology from our documentation and model language from elsewhere in the source tree. Continues the long process of fixing Savannah #66519. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp: Fix code style nits. (ps_output::special) (ps_font::handle_unknown_font_command) (ps_font::handle_unknown_desc_command) (ps_printer::ps_printer) (ps_printer::set_char) (ps_printer::define_encoding) (ps_printer::encode_fonts) (ps_printer::set_style) (ps_printer::flush_sbuf) (ps_printer::do_import): Arrange equality comparisons to avoid inadvertent lvalue assignment. (style::operator==) (ps_printer::ps_printer) (ps_printer::set_char) (ps_printer::define_encoding) (ps_printer::flush_sbuf) (ps_printer::do_import) (main): Parenthesize formally complex expression. (ps_printer::ps_printer): Compare variable of floating-point type to a floating point, not integer, literal. (ps_printer::set_subencoding): Compare variable of unsigned integer type to unsigned, not signed, integer literal. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (ps_printer::flush_sbuf): Demote local variable `space_flag` from `int` to `bool`. Assign to it using Boolean, not integer, literals. Explicitly construct integer from its Boolean value when summing it with other values to compute an array index. 2026-05-06 G. Branden Robinson <g.branden.robinson@gmail.com> [grops]: Boolify and rename class member variable. * src/devices/grops/ps.h (class ps_output): `need_space` of type `int` becomes `is_space_needed` of type `bool`. * src/devices/grops/ps.cpp (ps_output::ps_output): Update constructor's initializer list. (ps_output::end_line): (ps_output::special): (ps_output::simple_comment): (ps_output::end_comment): (ps_output::put_delimiter): (ps_output::put_string): (ps_output::put_number): (ps_output::put_fix_number): (ps_output::put_float): (ps_output::put_symbol): (ps_output::put_color): (ps_output::put_literal_symbol): Update. (ps_output::put_fix_number): (ps_output::put_float): (ps_output::put_symbol): (ps_output::put_color): (ps_output::put_literal_symbol): Explicitly construct integer from its Boolean value when summing it with `col` and `end` to decide whether to put a newline on the output. 2026-05-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/asciify-request-works.sh: Unit-test asciification of (left and regular) italic correction nodes. 2026-05-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/asciify-request-works.sh: Test sample input not just with "ps" output device, but "utf8" as well. Exclude two checks when producing output for the latter; one because it's an as yet unimplemented feature, and the other because the feature (ligature rendering) is unsupported in nroff mode. This is to catch more cases like Savannah #68303. 2026-05-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (left_italic_corrected_node::asciify): Revise assert(3)ion: it's okay for this type of container node's contents (`nodes`) to be empty if we're in nroff mode. Fixes <https://savannah.gnu.org/bugs/?68303>. Problem introduced by me in commit d445aee94e, 10 September, as part of the redesign of the "asciify" feature. Thanks to Deri James for catching the problem and root-cause analyzing it. 2026-05-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (process_input_stack): Add null pointer check when processing a horizontal motion token. An experimental code change in my working copy while exploring Savannah #68303 caused a SEGV in a place I didn't expect. While that change is likely erroneous, it appears to have revealed an implicit invariant. Make the invariant explicit, as above, and add an assert(3)ion for it as well. 2026-04-29 Dave Kemper <saint.snit@gmail.com> * doc/groff.texi.in (Ligatures and Kerning): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): Clarify behavior of `tkf` request. 2026-04-30 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Alter extension macro testing technique. What we had wasn't working when copy-and-pasted into man pages formatted with non-groff-compatible formatters. Now people copying these macros can preserve or discard the lines with brace escape sequences surrounding the macro definitions, or not, and things should work either way. * tmac/an.tmac: Initialize `mG` register to zero. * tmac/an-ext.tmac: Invert sense of test when defining macros that also have definitions in "tmac/an.tmac". * tmac/tests/an-ext_MR-works.sh: Assign `mG` value of "1" when testing the "an-ext.tmac" file's implementations of macros. 2026-04-30 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an-ext.tmac: Relocate material. Group all the macro definitions that don't conditionalize their definitions on the value of the `mG` register before those that do. 2026-04-30 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #68285. * src/roff/troff/env.cpp (temporary_indent): When invalidly given no argument, the `ti` request was failing to correctly skip the remainder of the input line. Drop two instances of logic that "manually" pump the input stream by calling `tok.next()`. The second is better replaced by `skip_line()`, and the first made redundant thereby. Nearly all request handlers call that function to consume the remainder of the input line once they've handled all the arguments they expect. Fixes <https://savannah.gnu.org/bugs/?68285>. Problem introduced by me in commit ca8c376830, 2024-11-09. I altered request handlers for four requests in that commit: `pn`, `ti`, `rchar`, and `hpfcode`, but fortunately made this mistake only with `ti`. 2026-04-29 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68285. * src/roff/groff/tests/ti-request-works.sh: Do it. 2026-04-29 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `ti` request. * src/roff/groff/tests/ti-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-29 G. Branden Robinson <g.branden.robinson@gmail.com> * doc/groff.texi.in: Explicitly call out read-only registers as such. Most already were; gather up the stragglers: `.O`, `.cdp`, `.cht`, `.csk`, `.int`, `.n`, `.nm`, `.nn`, and `.w`. (Built-in Registers): Accordingly annotate the table of such registers whose values are fixed at formatter start-up. Fixes <https://savannah.gnu.org/bugs/?68287>. Thanks to Dave Kemper for the report. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/reg.cpp: Slightly refactor. Tweak and make consistent the buffer-scribbling technique when formatting register properties. Never hit the stack for this purpose; use only one file-global static buffer, `scratchpad`. Shrink size of said buffer to only what we require: `INT_DIGITS` decimal places plus a leading sign. As long as `INT_DIGITS` remains {generously} sized for 64-bit `int`s--though I'm not familiar with a port of groff to an I(LP)64 host--doing so buys back over 100 bytes of BSS. If this technique seems old-fashioned, it is--but GNU troff is single-threaded and none of the functions using this buffer participate in cycles in the program's call graph (i.e., they're not reëntrant). (number_value_to_ascii, number_format_to_ascii, dump_register): Zero out the buffer with memset(3) before using it, for paranoia's sake. (number_format_to_ascii): Use the `scratchpad` buffer instead of a different, local `static` buffer of 24 bytes. (dump_register): Use `scratchpad` instead of automatic (stack) storage as buffer for autoincrement value. Declare `n` variable closer to use. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_increment_and_escape_sequence_parameter): Rename this... (read_crement_and_escape_sequence_parameter): ...to this. (read_character_in_copy_mode, token::next): Update call sites. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp: Trivially refactor. Rename `incr_number_result` enumeration type to `crement`. Annotate it. Rename `get_incr_number()` to `read_crement()`. We're pretty close to exclusive use of function names starting with "read_" for input stream reading operations; that is, interpretation of *roff language. This change moves us closer to that goal. (get_incr_number): Rename this... (read_crement): ...to this. Update its return type. (read_vunits, read_hunits, read_measurement_crement) (read_integer_crement): Update call sites. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp: Slightly refactor. Convince any compiler that we're not falling off the end of functions returning `bool` (or `incr_number_result`). End function bodies with an unconditional `return`. (read_vunits, read_hunits, read_measurement, read_integer) (get_incr_number): Do it. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Stop reporting number format for read-only registers. * src/roff/troff/reg.h (class reg, class general_reg): Define new public member function `has_format()` returning `bool`. * src/roff/troff/reg.cpp (reg::has_format): Return `false`. (general_reg::has_format): Return `true`. (dump_register): Report register format only if register's `has_format()` member function returns `true`. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (init_input_requests): Make `.O` read-only register actually read-only. Fixes <https://savannah.gnu.org/bugs/?68284>. Problem appears to date back to feature addition in commit bc34881980, 2007-09-19. 2026-04-27 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Decouple diagnosis of file I/O error from font description file content validation. * src/include/font.h (class font): Update declaration of `load_font()` public member function to insert new `bool`-valued argument defaulting `true`. It governs whether an error diagnostic is issued if a font description file can't be opened. Update declaration of `load()` protected member function with a new `bool`-valued first argument defaulting true, for the same purpose, since the former function wraps the latter. * src/libs/libgroff/font.cpp (font::load_font): Update definition and pass new argument to `load()` member function of object. (font::load): Condition `error()` diagnostic on `want_diagnostic` argument, not `validate_only`. * src/roff/troff/node.cpp (assign_font_and_file_name_to_mounting_position): Update `font::load_font()` call to pass `!in_nroff_mode` as the value of the former's `want_diagnostic` parameter. Annotate why. (is_font_available): Pass `font::load_font()` a `false` `want_diagnostic` parameter. Continues commit 48ab338d25, 12 April. 2026-04-24 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (SH, SS, TP): Throw warning if macro called while an input trap is pending (for example, `.TP` followed immediately by `.SH`). This input is not specified or countenanced by any man(7) documentation I know of, mandoc(1)'s "-T lint" warns about it, and it produces unpredictable output across implementations. (Given the foregoing example, Plan 9 troff truncates the input at that point.) Seen in the wild in fvwm-config(1). Fixes <https://savannah.gnu.org/bugs/?68279>. 2026-04-24 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (environment::newline): Reset contents of read-only `.itm` register (introduced in groff 1.24.0) to null after an input trap has sprung, so that it doesn't interpolate a stale and/or misleading value. Fixes <https://savannah.gnu.org/bugs/?68278>. Continues commit 1fce75f6ba, 2024-02-11. 2026-04-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/\ roman-format-register-interpolation-works.sh: Fix missing parentheses in AT&T troff register interpolation syntax. Make final two test cases capable of actually failing. Continues commit 5050eecc10, 16 April. 2026-04-22 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `\l` and `\L` escape sequences. * src/roff/groff/tests/backslash-L-works.sh: * src/roff/groff/tests/backslash-l-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run tests. 2026-04-22 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. Rename `get_flags()` global function to `recompute_character_flags()`. In addition to being more clear and explicit, it avoids a collision with the `charinfo` class's own `get_flags()` member function. * src/roff/troff/input.cpp (get_flags): Rename this... (recompute_character_flags): ...to this. * src/roff/troff/charinfo.h: Update `extern` declaration "importing" the symbol. (charinfo::overlaps_horizontally) (charinfo::overlaps_vertically) (charinfo::allows_break_before) (charinfo::allows_break_after) (charinfo::ends_sentence) (charinfo::is_transparent_to_end_of_sentence) (charinfo::ignores_surrounding_hyphenation_codes) (charinfo::prohibits_break_before) (charinfo::prohibits_break_after) (charinfo::is_interword_space): Update call sites. Drop now- unneeded namespace access operator. 2026-04-22 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Drop dead/unreachable nested character class code. * src/roff/troff/charinfo.h (class charinfo): Drop `nested_classes` member variable. Drop declaration of overloaded `add_to_class()` member function taking a pointer to a `charinfo` as argument. (charinfo::add_to_class): Drop definition per above. (charinfo::is_class): Simplify test to eliminate dereference of now-vanished `nested_classes` member variable. * src/roff/troff/input.cpp (define_class_request): Generalize condition under which warning diagnostics about inability to nest character classes are thrown. Drop branches unreached in a "make check" build that required the aforementioned member function signature. (charinfo::contains, charinfo::dump): Drop iterators of nested classes `#if 0`-ed out in groff 1.24.0 release. 2022-04-17 Dave Kemper <saint.snit@gmail.com> * tmac/pspic.tmac: Allow `PSPIC` macro to place image at bottom of page. From groff 1.01 through 1.04, the macro did not invoke a break, explicitly or implicitly; thus the vertical space `.ne`eded for the image was incremented by 1v to account for a possible partially collected output line. The "ChangeLog.old" file shows that James Clark added a break to this macro in a 1992-03-16 change (the Git history for this era is too coarse to show this change alone; it first shows up in commit a48ab7b6d, containing all changes between groff 1.04 and groff 1.05). However, the 1v increment was (presumably) inadvertently left in place, preventing an image that would exactly fit at the bottom of a page from being placed there. This adjustment has been unnecessary for over 34 years, so get rid of it. [Fixes <https://savannah.gnu.org/bugs/?50770>. Per the foregoing, problem dates back to groff 1.05, March 1992. Thanks to Dave and Deri for sorting this out! --GBR] 2026-04-24 Deri James <deri@chuzzlewit.myzen.co.uk> [pdfmom]: Don't try to pass commands to pdfroff (-Tps) if pdfroff is not installed. * src/devices/gropdf/pdfmom.pl: Check if pdfroff is available before using it. (Withdrawn from groff since 1.24.0) See: <https://savannah.gnu.org/bugs/?68207> 2026-04-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (do_error): Report warning category governing diagnostic message even if the type of diagnostic is not `WARNING` but `OUTPUT_WARNING`. Continues commit 0220f00c3c, 18 March. 2026-04-21 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Throw warning in category "style" when nilpotent brace escape sequences are encountered. * src/roff/troff/input.cpp (read_character_in_copy_mode): Do it. * doc/groff.texi.in (Conditional Blocks, Warnings): * man/groff.7.man (Conditional blocks): * src/roff/troff/troff.1.man (Warnings): Document new warning circumstance. 2026-04-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Handle brace escape sequences better. In AT&T troff, a brace escape sequence not occurring in a macro definition is discarded when read in copy mode--make GNU troff do likewise. When occurring in a macro definition, it _is_ being read in copy mode and must be preserved, so retain existing behavior. Unfortunately, the datum "we are defining a macro right now" was not consistently being passed from `do_define_macro()` to `read_character_in_copy_mode()`, so fix that problem too. Fixing one of these without the other produces chaos in the formatter and wrecks a build; couple them. (read_character_in_copy_mode): Return an `ESCAPE_{LEFT,RIGHT}_BRACE` encoded token (character code) only if `is_defining_macro`. (do_define_macro): Supply `true` parameter (`is_defining_macro`) in _all_ `read_character_in_copy_mode()` calls. Fixes <https://savannah.gnu.org/bugs/?68260>. Problems appear mostly to date back to groff's birth: see groff 1.01,[1] "input/troff.cc", lines 741-746, 3113, and 3119. Commit 5ba4c4ddb3, 2001-11-16, added another instance of `read_character_in_copy_mode()` (then called `get_copy()`) being given only one argument. [1] https://minnie.tuhs.org/cgi-bin/utree.pl?file=Net2/usr/src/\ usr.bin/groff/troff/input.cc 2026-04-20 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68257. * src/roff/groff/tests/\ backslash-right-brace-ends-string-definition.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/token.h (token::is_terminator): Add `TOKEN_RIGHT_BRACE` to the set of tokens that can terminate a sequence of nonterminal symbols (such as the character sequence producing a *roff identifier, transparent throughput argument, contents of a string definition, or other string-like request argument like file names or system commands). Fixes <https://savannah.gnu.org/bugs/?68257>. 2026-04-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename `read_character_in_copy_mode()` function parameter from `is_defining` to `is_defining_macro` in declaration. (read_character_in_copy_mode): Update definition. (read_character_in_escape_sequence_parameter): (do_define_macro): Update call sites. 2026-04-19 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Test conditional block nesting. * src/roff/groff/tests/nested-conditional-blocks-work.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/token.h (class token): Add new member function, `is_terminator()` returning `bool`. The idea is to make handlers of formatter instructions more consistent by having one source of truth regarding whether, in the parlance of parser theory, the token on the input stream terminates a "nonterminal symbol" (like an identifier or numeric expression, which can comprise multiple "terminal" or "primitive" tokens). Right now, GNU troff pretty consistently regards newline and EOF tokens as terminating a sequence of such nonterminals. It's less consistent regarding whether a right brace escape sequence does, causing inconsistency in its handling of numeric expressions as arguments to `nr` requests on the one hand and "contents" arguments to `ds` requests on the other, and divergence between AT&T and GNU troff parsers. See Savannah #68257. (token::is_terminator): Define it `inline`. Equivalent to `tok.is_newline() && tok.is_eof()`. * src/roff/troff/div.cpp (begin_page_request) (space_request) (need_vertical_space_request) (save_vertical_space_request) (output_saved_vertical_space_request) (flush_request): * src/roff/troff/env.cpp (fill) (no_fill) (center) (right_justify) (indent) (temporary_indent) (number_lines) (do_break_request): * src/roff/troff/input.cpp (diagnose_missing_identifier) (diagnose_invalid_identifier) (flush_pending_lines) (decode_macro_call_arguments) (remove_character) (do_define_macro) (read_title_parts) (ps_bbox_request) (do_translate) (hyphenation_patterns_file_code) (define_class_request) (read_drawing_command): * src/roff/troff/node.cpp (remove_font_specific_character_request): Migrate input handlers to call `token::is_terminator()` rather than evaluating the Boolean expression `tok.is_newline() && tok.is_eof()`. 2026-04-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/dt-request-works.sh: Add test case: a diversion trap is removed when `dt` is called with one argument. If that argument is a valid numeric expression (albeit a meaningless one), the diversion's trap is removed. 2026-04-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Slightly refactor. Demote return types of `font_info` class's member functions returning Boolean values from `int` to `bool`. (class font_info): Update declarations. (font_info::contains) (font_info::is_special) (font_info::is_style) (font_info::is_named): Update definitions. 2026-04-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (read_font_identifier): Rewrite to fix Savannah #68252, an assertion failure I introduced in commit 8b83fd3dd8, 2 November, in "src/roff/troff/input.cpp:token:: is_usable_as_delimiter()". Time to fire up the development mailing list critical homunculus and have a Socratic dialogue with it. "Rewrite"? Why hit such a small thing with such a big hammer? Why not just fix my stupid mistake of adding an assertion? {A} I couldn't--wouldn't--just rip the assertion out, because it's in a hot-path function where much sanity checking takes place. {B} This code implicates one of the more challenging parts of the *roff language grammar: (almost) anywhere you can read a font identifier like `B`, `TR`, or `MARIGOLD`, it's also valid to read a font mounting position, a nonnegative integer. In *roff, nonnegative integers also happen to be valid identifier names! You can have a register named `55` or a string/macro/ diversion named `10` {hello, eqn(1)}. {C} This function is called only by uncommonly used request handlers, those for `uf`, `fschar`, `rfschar`, `special`, `fspecial`, `fzoom`, `bd`, and `tkf`. {It's a rare groff user who can say with confidence what _all_ of those requests do.} "But wait," you say, "isn't this same kind of font lookup done wildly commonly, like with every `ft` request or `\f` escape sequence? {D} Yes! So there's good news and bad news, and they're the same news. There is logic to do exactly that--to disambiguate and resolve font mounting positions and identifiers behind that request and that escape sequence. It lives, duplicatively, in another function in a different source file. {E} "But wait," you say, "why was this function, a supporting function for request handlers that read only space-delimited arguments, calling `token:: is_usable_as_delimiter()`?" Good question! I don't know, but it's been doing that, modulus the renamings for which I fear I am becoming notorious, at least since groff 1.22.3 in November 2014. Within the request handlers, we aren't in a delimited input context at all--as the GNU troff code base uses that term--so it's a mystery. `token::is_usable_as_delimiter()` used to be known as `token::delimiter()`. I invite the reader to compare and contrast the following. https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/src/roff/\ troff/node.cpp?h=1.22.3#n6163 https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/troff/\ node.c?h=1.02#n4373 Why not just have `read_font_identifier()` call `select_font()` over in "env.cpp"? Side effects. As with many GNU troff functions, parsing logic is entangled with engine logic; see Savannah #68240. So instead I copy the lexical analysis bits of `select_font()` into this function, replacing most of it. How can I be confident this will work well? Apart from a build proceeding successfully and passing our test suite (now with 340 automated tests), the `select_font()` logic is a hotter code path and, I suspect, always has been. After my last push earlier this week, I gathered coverage statistics from a full build with tests. `read_font_identifier() was hit 12,023 times. That seems like a lot, right? `select_font()` was hit 254,489 times. If you can't successfully change fonts in GNU troff, a lot more people (and test scripts of ours) will notice than can observe malfunctions of the `uf`, `fschar`, `rfschar`, `special`, `fspecial`, `fzoom`, `bd`, or `tkf` requests. (And, in fact, `bd` _doesn't_ work well. See Savannah #68256.) So if I have to violate DRY, I'm cribbing the battle-hardened code. Moreover there's no reason at all, on any principle, to be checking for delimiters in this input context, as noted above. Fixes <https://savannah.gnu.org/bugs/?68252>. The root cause of the problem depends on what you consider the defect to be. As noted above, the assertion came in with commit 8b83fd3dd8, 2 November, affecting groff 1.24.0. But the code path _containing_ the assertion should never have been taken in the first place by the request handlers for `bd` et al.--yet it has been, ever since groff's birth. Until now. 2026-04-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (read_font_identifier): Trivially refactor. Rename local variable `n` to `mp` to prepare for subsequent, larger change. 2026-04-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (select_font): Fix code style nit. Parenthesize formally complex expression. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. * src/roff/troff/env.h: Declare `init_size_list()` here, corresponding to its definition in "env.cpp", and along with other "size_list"-related stuff... * src/roff/troff/node.h: ...instead of here. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.h: Drop declaration of unused symbol `read_desc()`. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Trivially refactor. (read_special_fonts): Rename this... (read_special_font_identifiers): ...to this. (set_font_specific_special_fonts_request) (set_special_fonts_request): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_request): Rename this... (read_from_terminal_request): ...to this. It's a *roff input-reading function, but an oddball and highly specific one. Make its name less apparently general. (init_input_requests): Update wire-uppery of request name to handler function. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_escape_sequence_parameter) (read_long_escape_sequence_parameters): Rename function parameter `mode`, a heavily overloaded term in *roff parlance--like "transparent" and "special"--to `arity`. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_char_in_copy_mode): Rename this... (read_character_in_copy_mode): ...to this. (read_character_in_escape_sequence_parameter) (has_arg) (process_input_stack) (decode_macro_call_arguments) (decode_escape_sequence_arguments) (read_request) (do_define_string) (define_character) (do_define_macro) (length_request) (do_non_interpreted) (device_request) (output_request) (tag) (taga) (terminal_write) (do_write_request) (abort_request) (read_rest_of_line_as_argument): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (abort_request): Fix code style nit. Drop extraneous parentheses; no formally complex operation is occurring here, in the sense I use the term. Only one dyadic operator is involved in the `for` loop's updating expression. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_char_in_escape_sequence_parameter): Rename this... (read_character_in_escape_sequence_parameter): ...to this. (read_two_character_escape_sequence_parameter) (read_long_escape_sequence_parameters) (read_escape_sequence_parameter): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_two_char_escape_parameter): Rename this... (read_two_character_escape_sequence_parameter): ...to this. (read_escape_sequence_parameter) (read_increment_and_escape_sequence_parameter) (token::next): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_long_escape_parameters): Rename this... (read_long_escape_sequence_parameters): ...to this. (read_escape_sequence_parameter) (read_increment_and_escape_sequence_parameter) (token::next) (device_request): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_increment_and_escape_parameter): Rename this... (read_increment_and_escape_sequence_parameter): ...to this. (read_char_in_copy_mode) (token::next): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (read_escape_parameter): Rename this... (read_escape_sequence_parameter): ...to this. (read_increment_and_escape_parameter) (read_char_in_copy_mode) (token::next): Update call sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename constant in `escape_sequence_parameter_cardinality` enumeration from `ALLOW_EMPTY` to `ARGUMENTS_OPTIONAL`. (read_long_escape_parameters) (token::next): Update dereference sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename constant in `escape_sequence_parameter_cardinality` enumeration from `WITH_ARGS` to `ARGUMENTS_MANDATORY`. (read_long_escape_parameters) (read_char_in_copy_mode) (token::next) (device_request): Update dereference sites. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename constant in `escape_sequence_parameter_cardinality` enumeration from `NO_ARGS` to `ARGUMENTS_FORBIDDEN`. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename `read_mode` enumeration type to `escape_parameter_cardinality`. (read_escape_parameter, read_long_escape_parameters): Update function declarations and definitions accordingly. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Trivially refactor. Rename `hyphen_trie` class's member function `read_patterns_file()` to `interpret_patterns_file()`. We're pretty close to exclusive use of function names starting with "read_" for input stream reading operations; that is, interpretation of *roff language. This change moves us closer to that goal. (class hyphen_trie): Update declaration. (hyphen_trie::read_patterns_file): Rename this... (hyphen_trie::interpret_patterns_file): ...to this. (read_hyphenation_patterns_from_file): Update call site. 2026-04-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (read_hyphenation_patterns_from_file): Trivially refactor. Rename function parameter `append` to `appending`. (Elsewhere, we have member functions named `append()` that have side effects.) 2026-04-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/dot-nm-register-works.sh: Repair test script so it can fail again. Problem introduced by me in commit 7aaeddf570, 14 April. 2026-04-16 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (remove_hyphenation_exception_words_request): Implement argument processing; each is normalized (hyphens are removed), looked up in the current hyphenation language's exception word dictionary, and deleted thence if present. * src/roff/groff/tests/rhw-request-works.sh: Test functionality. * doc/groff.texi.in (Manipulating Hyphenation): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?64478>. 2026-04-16 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `pnr` request. * src/roff/groff/tests/pnr-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-16 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test support for alphabetic register formats. * src/roff/groff/tests/\ alpha-format-register-interpolation-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-16 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test support for Roman numeral register formats. * src/roff/groff/tests/\ roman-format-register-interpolation-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. See <https://savannah.gnu.org/bugs/?61999>. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `sv` and `os` requests. * src/roff/groff/tests/sv-and-os-requests-work.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (set_page_number_request): Drop logic testing existence of `ps4html` register. That can easily be handled by macro programming. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Trivially refactor. Rename request handler functions. (need_space) (page_number) (save_vertical_space) (output_saved_vertical_space): Rename these... (need_vertical_space_request) (set_page_number_request) (save_vertical_space_request) (output_saved_vertical_space_request): ...to these. (init_div_requests): Update wire-uppery of request names to handler functions. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (diversion_trap_request): Ignore request if arguments are present but nonsensical: either the first is not a valid vertical position expression, or it's present and valid but no second argument is present. * src/roff/groff/tests/\ error-dt-request-given-bogus-arguments.sh: Test it. * src/roff/groff/groff.am (groff_TESTS): Run test. * doc/groff.texi.in (Other Differences): * man/groff_diff.7.man (Other differences): * NEWS: Document this behavioral difference from AT&T troffs. Fixes <https://savannah.gnu.org/bugs/?68246>. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp (diversion_trap_request): Throw warning in category "missing" if a `dist` argument is specified but the then-mandatory `name` argument is omitted. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Trivially refactor. Rename request handler functions. (diversion_trap) (change_trap) (print_traps) (mark) (vertical_position_traps): Rename these... (diversion_trap_request) (change_trap_request) (print_traps_request) (mark_request) (configure_vertical_position_traps_request): ...to these. (init_div_requests): Update wire-uppery of request names to handler functions. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Improve `dt` request unit test. * src/roff/groff/tests/dt-request-works.sh: Verify that diversion trap removal works. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (suppress_node::tprint): Refactor code populating "grohtml-info" output to access the contents of built-in registers `.F` and `.c` directly via their `get_string()` member functions instead of this file's `get_string()` function, which syntactically sugars that procedure. (get_string): Delete now-unused function. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (suppress_node::tprint): Refactor code populating "grohtml-info" output to access the contents of `psbb` request-populated registers `op{min,max}{x,y}` directly via their member functions instead of this file's `get_register()` function, which syntactically sugars that procedure. (get_register): Delete now-unused function. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (suppress_node::tprint): Refactor code populating "grohtml-info" output to access the formatter's page offset and line length parameters directly, instead of going through the register dictionary. Because we're adding them, check that addition for overflow; preprocessor-include "<stdckdint.h>" accordingly. We can consequently drop some now-unused code. * src/roff/troff/div.cpp (class page_offset_reg): Drop `get_value()` declaration. (page_offset_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Drop unused code introduced in initial commit 351da0dcd, 1991-06-02. Since the `.p` register is interpolated only by its `get_string()` member function and never `get_value()` (and nothing internal to the formatter attempts to get at it by that means, since accessing the datum via `curdiv->get_page_length()` is more straightforward), we can drop the latter. (class page_length_reg): Drop `get_value()` declaration. (page_length_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Drop unused code introduced in initial commit 351da0dcd, 1991-06-02. Since the `.d` register is interpolated only by its `get_string()` member function and never `get_value()` (and nothing internal to the formatter attempts to get at it by that means, since accessing the datum via `curdiv->get_vertical_position()` is more straightforward), we can drop the latter. (class vertical_position_reg): Drop `get_value()` declaration. (vertical_position_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Drop unused code introduced in initial commit 351da0dcd, 1991-06-02. Since the `.h` register is interpolated only by its `get_string()` member function and never `get_value()` (and nothing internal to the formatter attempts to get at it by that means, since accessing the datum via `curdiv->get_high_water_mark()` is more straightforward), we can drop the latter. (class high_water_mark_reg): Drop `get_value()` declaration. (high_water_mark_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Drop unused code introduced in initial commit 351da0dcd, 1991-06-02. Since the `.t` register is interpolated only by its `get_string()` member function and never `get_value()` (and nothing internal to the formatter attempts to get at it by that means, since accessing the datum via `curdiv->distance_to_next_trap()` is more straightforward), we can drop the latter. (class distance_to_next_trap_reg): Drop `get_value()` declaration. (distance_to_next_trap_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.cpp: Drop unused code introduced in commit 0216fc85d3, 2001-05-05. Since the (now-named) `.ns` register is interpolated only by its `get_string()` member function and never `get_value()` (and nothing internal to the formatter attempts to get at it by that means, since accessing the datum via `curdiv->is_in_no_space_mode` is more straightforward), we can drop the latter. (class no_space_mode_reg): Drop `get_value()` declaration. (no_space_mode_reg::get_value): Delete. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `.ns` register. * src/roff/groff/tests/dot-ns-register-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `ns` request. * src/roff/groff/tests/ns-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `dt` request. * src/roff/groff/tests/dt-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `aln` and `als` requests. * src/roff/groff/tests/aln-request-works.sh: * src/roff/groff/tests/als-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run tests. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Slightly refactor. Migrate GNU troff's `dictionary` class to use `ssize_t` for measurement of its capacity and occupancy (and for integer variables iterating through it on that basis). * src/roff/troff/dictionary.h (class dictionary_iterator): Migrate member variable `i` from `int` to `ssize_t`. (class dictionary): Migrate member variables `capacity` and `occupancy` from `int` to `ssize_t`. (class dictionary, class object_dictionary): Migrate constructors to take an argument of `ssize_t` instead of `int` type. * src/roff/troff/dictionary.cpp: Preprocessor-include "<sys/types.h"> header defining `ssize_t` type. * bootstrap.conf: Add "ssize_t" to `gnulib_modules`, since neither ISO C99 nor ISO C++98 guarantee its existence. (It's a POSIX 2001 feature). 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. * src/roff/troff/dictionary.h (class dictionary): Rename member variables. - size -> capacity - used -> occupancy * src/roff/troff/dictionary.cpp (dictionary::dictionary) (dictionary::lookup) (dictionary::remove) (dictionary_iterator::dictionary_iterator): Update dereference sites. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/dictionary.cpp: Fix code style nits. (is_good_size) (dictionary::lookup) (dictionary::remove): Parenthesize formally complex expressions. (dictionary::lookup): (object_dictionary::lookup): Favor C++ `static_cast<>` operator over omnipotent C-style cast. (object_dictionary::define) (object_dictionary::rename) (object_dictionary::remove): Explicitly compare value of pointer type to null pointer literal instead of letting it pun down to a Boolean. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/dictionary.h (class dictionary): Drop declaration of unimplemented member function `rehash()`. 2026-04-14 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. Drop some code introduced in commit dc3c168c3b, 2004-10-10. Per lcov(1) it's never used, and deleting it does not adversely affect "make check". It appears that the `is_diversion()` member function that callers actually use comes from the `macro` and `string_iterator` classes in "input.cpp". * src/roff/troff/div.h (class diversion): Drop `is_diversion()` declaration. (macro_diversion::is_diversion) (top_level_diversion::is_diversion): Drop definitions. 2026-04-15 G. Branden Robinson <g.branden.robinson@gmail.com> Ditch our local (outdated) copy of gnulib's "localcharset" module. Our build brings it in already via the dependencies of gnulib's "hypot" module. * src/include/localcharset.h: * src/libs/libgroff/localcharset.c: Delete. * src/libs/libgroff/libgroff.am (libgroff_a_SOURCES): Drop "src/libs/libgroff/localcharset.c" from macro definition. (EXTRA_DIST): Stop shipping "src/libs/libgroff/config.charset", "src/libs/libgroff/ref-add.sin", and "src/libs/libgroff/ref-del.sin" in our distribution archive. (CLEANFILES): Drop "config.charset", "ref-add.sin", and "ref-del.sin" from macro definition. (LIBGROFF_PACKAGE): Delete macro definition. (all, charset.alias, ref-add.sed, ref-del.sed) (install-data-local, install_charset_data) (uninstall-local, uninstall_charset_data): Delete targets. * m4/localcharset.m4: Delete. * acinclude.m4: Drop inclusion of "m4/localcharset.m4". Continues the long process of fixing Savannah #66518. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Trivially refactor. (class hyphen_trie): Demote type of `read_patterns_file()` member function's second argument from `int` to `bool`. (hyphen_trie::read_patterns_file): Likewise, and rename it from `append` to `appending`. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (has_arg): Trivially refactor. Rename argument from `want_peek` to `peeking`. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (make_composite_node) (make_glyph_node): Fix code style nit. Parenthesize formally complex expressions. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Trivially refactor. Rename class to better align with terminology used in our documentation. (class next_available_font_position_reg): Rename this... (class next_available_font_mounting_position_reg): ...to this. (init_node_requests): Update wire-uppery of built-in register interpolator to its name in the formatter. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Trivially refactor. Rename objects to better align with terminology used in our documentation. (class troff_output_file): Rename: - font_position -> font_mounting_position - nfont_positions -> mounting_position_count (troff_output_file::set_font) (troff_output_file::really_begin_page) (troff_output_file::really_copy_file) (troff_output_file::~troff_output_file) (troff_output_file::troff_output_file): Update dereference sites. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. Rename functions to better align with terminology used in our documentation. * src/roff/troff/node.h: Rename declaration of `symbol_fontno()` to `mounting_position_of_font()`, since it has nothing to do with a "symbol font" (like "S") as discussed by user-facing documentation. * src/roff/troff/node.cpp (symbol_fontno): Rename this... (mounting_position_of_font): ...to this. * src/roff/troff/env.cpp (environment::set_font): * src/roff/troff/node.cpp (is_abstract_style) (font_family::resolve, read_font_identifier): Update call sites. * src/roff/troff/node.h: Rename declaration of `next_available_font_position()` to `next_available_font_mounting_position()`. Yes, it's a mouthful, but the term "position" can apply to many things: the drawing position, an index into an array, and so forth. * src/roff/troff/node.cpp (next_available_font_position): Rename this... (next_available_font_mounting_position): ...to this. * src/roff/troff/env.cpp (environment::set_font): * src/roff/troff/node.cpp (assign_font_and_file_name_to_mounting_position) (font_family::resolve, read_font_identifier, zoom_font_request) (next_available_font_position_reg): Update call sites. * src/roff/troff/node.h: Rename declaration of `mount_font()` to `mount_font_at_position()`. Also resequence arguments to follow the order implied by the English words in the function name. (Compare fprintf(3) and contrast fputs(3).) * src/roff/troff/node.cpp (mount_font): Rename this... (mount_font_at_position): ...to this. Resequence arguments. * src/roff/troff/env.cpp (environment::set_font): * src/roff/troff/input.cpp (main): * src/roff/troff/node.cpp (mount_font_at_position_request) (read_font_identifier, zoom_font_request): Update call sites. * src/roff/troff/node.cpp (associate_style_with_font_position_request): Rename this... (assign_style_to_font_mounting_position_request): ...to this. (init_node_requests): Update wire-uppery of request handler to its name in the formatter. * src/roff/troff/node.h: Rename declaration of `get_underline_fontno()` to `get_selected_underline_font_mounting_position()`. * src/roff/troff/env.cpp (get_underline_fontno): Rename this... (get_selected_underline_font_mounting_position): ...to this. * src/roff/troff/env.cpp (environment::set_font) (configure_underlining): Update call sites. * src/roff/troff/node.h (class font_family): Rename declaration of `invalidate_fontno()` to `invalidate_selected_font_mounting_position()`. * src/roff/troff/node.cpp (font_family::invalidate_fontno): Rename this... (font_family::invalidate_selected_font_mounting_position): ...to this. * src/roff/troff/node.cpp (assign_font_and_file_name_to_mounting_position) (mount_style): Update call sites. * src/roff/troff/env.h (class environment): Rename declaration of `set_font()` to `select_font()`. * src/roff/troff/env.cpp (environment::set_font): Rename these overloaded names... (environment::select_font): ...to these. (select_font): Update call site. * src/roff/troff/node.cpp (class troff_output_file): Rename declaration of `set_font()` to `select_font()`. (troff_output_file::set_font): Rename this... (troff_output_file::select_font): ...to this. (troff_output_file::start_device_extension) (troff_output_file::put_char_width): Update call sites. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Refactor. Take one of `assign_font_and_file_name_to_mounting_position()`'s jobs away from it, awarding it instead to the only call site that wanted that job done. (assign_font_and_file_name_to_mounting_position): Drop `check_only` parameter and dependent logic, and... (is_font_available): ...open-code that logic here instead. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Trivially refactor. Rename library's `concat()` function to `catenate()`. * src/include/symbol.h: Rename in declaration. * src/libs/libgroff/symbol.cpp (concat): Rename this... (catenate): ...to this. * src/roff/troff/node.cpp (get_font_name, is_abstract_style) (font_family::resolve): Update call sites. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/font.cpp (font::load_font, font::load): Trivially refactor. Rename `load_header_only` parameter to `validate_only` for clarity. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Trivially refactor. Relocate `is_abstract_style()` function definition to precede its call site. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (is_abstract_style): Fix code style nits. Return Boolean instead of integer literal from this function returning `bool`. Parenthesize formally complex expression. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. Rename `is_font_name()` to `is_font_available()`. * src/roff/troff/node.h: Update declaration. * src/roff/troff/node.cpp (is_font_name): Rename this... (is_font_available): ...to this. * src/roff/troff/input.cpp (is_conditional_expression_true): Update call site. 2026-04-13 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Trivially refactor. Rename `mount_font_no_translate()` to `assign_font_and_file_name_to_mounting_position()` and update ordering of parameters to match the function name. (mount_font_at_position_request): Rename this... (assign_font_and_file_name_to_mounting_position): ...to this, and resequence arguments. (mount_font, is_font_name, font_family::resolve): Update call sites. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/preconv/preconv.cpp (conversion_latin1) (conversion_utf8, conversion_cp1047, conversion_iconv, get_BOM) (get_tag_lines, is_comment_line, get_variable_value_pair) (check_coding_tag, detect_file_encoding, do_file, usage): Declare `static`; external linkage is not required. Fixes `-Wmissing-declarations` GCC warning. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/font.cpp (font::load): If we're not "loading header only" (that is, not attempting to actually mount a font, but merely checking its availability or validity, as with `.if F ZCMI`), issue an error diagnostic disclosing the problem and the system's reason the file couldn't be opened. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libdriver/printer.cpp (printer::load_font): Validate function arguments. Add assert(3)ion that the string representing the font description to load isn't a null pointer. If a function argument is invalid, return early in `NDEBUG` builds. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libdriver/printer.cpp: Favor idiomatic C++ over C preprocessor hacks. (check_for_output_error) [!(_MSC_VER || _WIN32 && !__CYGWIN && !__UWIN)]: Define as a `static inline` function, not a preprocessor macro. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: The `phw` request now accepts arguments. Each is interpreted as a possible hyphenation word, ignoring hyphens in the input, looked up in the hyphenation exception list for the hyphenation language, and if found, is reported with its hyphenation points. Only hyphenation exceptions defined by the `hw` request are reported. * src/roff/troff/env.cpp (print_hyphenation_exceptions_request): Implement it. * src/roff/groff/tests/phw-request-works.sh: Test it. * src/roff/groff/groff.am (groff_TESTS): Run test. * doc/groff.texi.in (Debugging): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?68205>. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (print_hyphenation_exceptions): Slightly refactor. Declare `const` symbol `bufsz` computed at compile time as `static`. Explicitly clear the stack memory allocated to `wordbuf` array. 2026-04-12 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (read_hyphenation_exception_word): Generalize. Support being called with null pointers to store the breakpoint count and positions, so that a request that isn't interested in them need not reserve storage for them. Consequently, default these function parameters to null pointers. Dereference and populate the breakpoint count and positions only if these data are wanted. Add assert(3)ion that either both pointers are null or neither one is. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Refactor. Lift logic for parsing a hyphenation exception word request argument into its own function, so that other requests can use it. (read_hyphenation_exception_word): Implement it. (add_hyphenation_exception_words_request): Call new function from here. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Slightly refactor. Declare `const` symbols computed at compile time as `static`. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Trivially refactor. Rename local variables. - buflen -> wordbuflen - buf -> wordbuf - posbuflen -> bpbuflen - pos -> bpbuf - npos -> bp_count - newposbuflen -> newbpbuflen 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Slightly refactor. Revise loop style. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp: Fix code style nit. Favor C++ `static_cast<>` operator over omnipotent C-style cast. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp: Fix code style nit. Arrange equality comparisons to avoid inadvertent lvalue assignment. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp: Fix code style nit. Correctly type-annotate zero literals in comparisons. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp: Fix code style nit. Parenthesize formally complex expressions. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/symbol.cpp (symbol::json_length): Strengthen assertion, since we don't permit use of C0 controls in identifiers; see Savannah #67734. Use preprocessor to disable unused code. (symbol::json_extract): Add assertion, since we don't permit empty identifiers. Use preprocessor to disable unused code. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Test unusual characters in identifiers, improving coverage of src/libs/libgroff/symbol.cpp:symbol::json_extract(). * src/roff/groff/tests/coverage-symbol-cpp.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-11 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Validate error path in src/libs/libgroff/symbol.cpp:symbol::symbol. * src/roff/groff/tests/error-overflow-symbol-table.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Slightly refactor. Lift `wordbuflen` and `bpbuflen` into file-global scope... (add_hyphenation_exception_words_request): ...from here, because we're going to need them in another function. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (remove_hyphenation_exception_words_request): Free the heap storage backing a hyphenation exception word when removing one--specifically, this is its list of hyphenation break points expressed as integers representing character positions in the word, allocated by `add_hyphenation_exception_words_request()`. Fastidiously assign null pointer literal to `word` local addressing that memory afterwards. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Rationalize header file inclusions, adding "<stdlib.h>" and "<string.h>". 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/symbol.h: Preprocessor-include "<string.h>" header file to access strchr(3). (class symbol): Declare new member function `contains()` indicating whether the `char` argument appears in the symbol's name. (symbol::contains): Implement it `inline`, wrapping strchr(3). 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/symbol.h (class symbol): Comment formal argument names in function declarations as a compromise with the Stroustrup-style C++ used in most of groff. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Catch `std::bad_alloc` exceptions from `new` operator. Throw a fatal error indicating how much memory we couldn't allocate. Continues the long process of fixing Savannah #68192. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Refactor and add warning diagnostic. Elevate scope of `c` local variable and rename it to `hc` since it stores the hyphenation code of the character under examination in the hyphenation exception word argument. Gather that hyphenation code earlier, and mark the word as invalid if the hyphenation code is zero--exception: the character '-' is also valid, since it marks a hyphenation point. Consequently, we now throw a warning in category "char" on attempted hyphenation exception words like "non*sense" and "0123456789". (You can still make these eligible for hyphenation breaking by using the `hcode` request to assign hyphenation codes to non-letters.) Drop later conditional loop break if the hyphenation code is zero since that now cannot be the case--we now don't enter the containing branch in the first place. Replace a test of the index into the hyphenation exception word being greater than zero with an assert(3)ion of the same inside the branch, as it's now an invariant that a valid hyphenation exception word contains a nonzero count of characters bearing hyphenation codes. * doc/groff.texi.in (Warnings): * src/roff/troff/troff.1.man (Warnings): Document new warning circumstance. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Slightly refactor. memset(3) stack-allocated character arrays `buf` and `pos` with null terminators/zeroes as a paranoid hedge. Drop later null-termination of `buf` as redundant. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Fix code style nits. Assign unsigned literal to array element of type `unsigned char`. Explicitly compare value of pointer type to null pointer literal instead of letting it pun down to a Boolean. Parenthesize formally complex expressions. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/symbol.cpp (symbol::symbol): Fix code style nits. Use explicit `const_cast` C++ operator instead of C-style cast. Parenthesize formally complex expressions. 2026-04-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/symbol.cpp (symbol::symbol): Improve fatal diagnostic message; when construction of a larger symbol table is impossible, describe the failure better and report the exceeded table capacity. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Trivially refactor. * src/include/symbol.h (class symbol): Rename `table_used` member variable, which could suggest a Boolean value, to `table_occupancy`. * src/libs/libgroff/symbol.cpp: Update dereference sites. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/div.h (class diversion, class macro_diversion): Use Boolean, not numeric, literals as default values for function arguments of type `bool`. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Use `UCHAR_MAX` from ISO C99's standard library instead of the literal `256` to configure the `WORD_MAX` constant. Preprocessor-include "<limits.h>" header file to access `UCHAR_MAX`. Declare `WORD_MAX` as `static`; external linkage is not required. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp (read_measurement_crement) (read_integer_crement): Trivially refactor for semantic clarity. Rename the last argument from `prev_value` to `operand`. Nothing in the implementation of these functions imparts a "previous value" meaning to that argument; that matter is context-dependent. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor for semantic clarity. We had two pairs of functions where the pair members differed by signature but not by name, and which moreover did somewhat different things, interpreting different expression types. Rename them to more obviously distinguish their purposes. ("crement" is a nonce usage I devised to generalize "{in,de}crement".) * src/roff/troff/token.h: Rename functions in declarations. - read_measurement() -> read_measurement_crement() - read_integer() -> read_integer_crement() * src/roff/troff/number.cpp (read_measurement): Rename this... (read_measurement_crement): ...to this. (read_integer): Rename this... (read_integer_crement): ...to this. * src/roff/troff/div.cpp (begin_page, page_number): * src/roff/troff/env.cpp (number_lines): Migrate callers of two-argument `read_integer()` to `read_integer_crement()`. * src/roff/troff/env.cpp (point_size): * src/roff/troff/input.cpp (read_delimited_measurement) (do_register): * src/roff/troff/reg.cpp (define_register_request) (inline_define_register): Migrate callers of three-argument `read_measurement()` to `read_measurement_crement()`. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp: * src/roff/troff/token.h: Comment formal argument names in function declarations as a compromise with the Stroustrup-style C++ used in most of groff. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Work on Savannah #67735. Improve type discipline with respect to internal character representation. * src/roff/troff/env.cpp (embolden_font_request) (configure_track_kerning_request) (constantly_space_font_request): * src/roff/troff/reg.cpp (inline_define_register): Improve type discipline with respect to groff's internal character representation. Call `read_measurement()` with explicitly constructed literal argument of type `unsigned char` for conceptual clarity. "groff characters" are, for now, unsigned bytes. Continues the long process of fixing Savannah #67735. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor for semantic clarity. The parameter named `required` used by many functions was employed for two slightly different purposes. In some instances, it indicated whether emission of a diagnostic message was desired in the event a token expected on the input stream (or similar) was absent. In others, it encoded whether the presence of such a token was _optional_, in which case both its presence and absence were valid. The latter tends to be the case when a token is present but might or might not have associated `charinfo` data. Detangle these issues by renaming `required` arguments to `want_diagnostic` and `is_mandatory` as appropriate. * src/roff/troff/token.h: Rename `required` argument in declarations. * src/roff/troff/input.cpp (diagnose_missing_identifier): (token::get_charinfo): Rename `required` argument to `is_mandatory` in definitions. (read_identifier, read_long_identifier) (read_input_until_terminator): Rename `required` argument to `want_diagnostic` in definitions. * src/roff/troff/column.cpp (column_justify) [COLUMN]: * src/roff/troff/div.cpp (change_trap): * src/roff/troff/env.cpp (do_input_trap) (add_hyphenation_exception_words_request): * src/roff/troff/input.cpp (read_rgb, read_cmy, read_cmyk) (read_gray, do_overstrike, do_bracket, token::next) (token::description, read_input_until_terminator) (do_define_string, define_character, print_character_request) (remove_character, do_define_macro, read_line_rule_expression) (do_register, encode_special_character_for_device_output) (device_request, device_macro_request) (is_conditional_expression_true, ps_bbox_request, open_file) (do_write_request, stream_write_macro_request, do_translate) (set_character_flags_request, define_class_request) (vjustify) [COLUMN]: * src/roff/troff/node.cpp (translate_font_request) (mount_font_at_position_request) (associate_style_with_font_position_request) (read_font_identifier, remove_font_specific_character_request) * src/roff/troff/reg.cpp (inline_define_register): Update annotations at call sites and in declarations. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [build]: Drop no longer needed check for `struct exception`. It was used only by the now-deleted "src/libs/libgroff/matherr.c". * m4/groff.m4 (GROFF_STRUCT_EXCEPTION): Delete. * configure.ac: Drop interpolation of `GROFF_STRUCT_EXCEPTION`. * Makefile.am: Stop advertising availability of `HAVE_STRUCT_EXCEPTION` preprocessor symbol. 2026-04-09 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Drop "matherr.c" file, declaring (under the preprocessor conditions `HAVE_STRUCT_EXCEPTION` and `TLOSS`) `matherr()` and `struct exception`. The former is K&R-style, thus prohibited in ISO C23, and can (should) cause build failures with compilers configured for that version of the C language. Autoconf 2.73, released last month, tries harder to arrange builds to use C23 by default. Also we straight up don't _need_ this declaration (anymore); nothing in groff calls `matherr()`. It dates back to the Clark days, when there was no gnulib for groff to use (or maybe it didn't support C++ projects at the time). Whatever gnulib needs, it transitively closures for us, so we no longer need to and can throw this 30-year old stuff of ours away. * src/libs/libgroff/matherr.c: Delete. * src/libs/libgroff/libgroff.am (libgroff_a_SOURCES): Drop aforementioned file from macro definition. Fixes <https://savannah.gnu.org/bugs/?68220>. Thanks to Alexis Hildebrandt for the report. Also continues the long process of fixing Savannah #66518. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (space_size): Clarify warning diagnostics in category "range". 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp (is_valid_term): Compare local `scaling_unit` and return value of `token::char()` to literals of type `unsigned char` instead of signed literals for conceptual clarity. "groff characters" are, for now, unsigned bytes. Continues the long process of fixing Savannah #67735. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/number.cpp: Refactor. Migrate `valid_scaling_units` global constant from type array-of-`char` to a groff `string`. (is_valid_term): Employ `contains()` function of `valid_scaling_units`'s new type instead of the standard C library's strchr(3). Continues the long process of fixing Savannah #67735. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Add `contains()` member function to `string` class. * src/include/stringclass.h (class string): Declare it, returning `bool`. * src/libs/libgroff/string.cpp (string::contains): Define it. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Improve type discipline with respect to internal character representation. * src/roff/troff/env.cpp (point_size): Call `read_measurement()` with explicitly constructed literal argument of type `unsigned char` for conceptual clarity. "groff characters" are, for now, unsigned bytes. * src/roff/troff/number.cpp (is_valid_term): Demote local variable `si` from `int` to `unsigned char`. Continues the long process of fixing Savannah #67735. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (environment_copy, environment_switch): Declare as `static`; external linkage is not required. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (fill, no_fill): Declare as `static`; external linkage is not required. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor. Separate engine logic from parsing logic with respect to an environment's filling enablement. * src/roff/troff/env.h: Stop globally declaring `fill()` and `no_fill()`. (class environment): Declare new member function returning `void`: `configure_filling()`. Drop declaration of `friend` functions `fill()` and `no_fill()`. * src/roff/troff/env.cpp (environment::configure_filling): New function mutates object of `environment` class. (fill, no_fill): Replace manipulation of `environment` object's private member variable with calls of the foregoing new public member function. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. * src/roff/troff/env.h (get_fill): Rename this declaration... (get_filling): ...to this. * src/roff/troff/env.cpp (environment::get_fill): Rename this definition... (environment::get_filling): ...to this. (init_env_requests): Update wire-uppery to `.u` register. * src/roff/troff/input.cpp (diverted_space_node::need_reread): Update call site. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `fi` and `nf` requests. * src/roff/groff/tests/fi-and-nf-requests-work.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (family_change, point_size) (space_size): Declare as `static`; external linkage is not required. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor. Separate engine logic from input parsing logic with respect to an environment's "line tabs" enablement (a GNU troff feature altering how tab stop positions are computed). * src/roff/troff/env.h: Stop globally declaring `line_tabs_request()`. (class environment): Declare new member function returning `void`: `configure_line_tabs()`. Drop declaration of `friend` function `line_tabs_request()`. * src/roff/troff/env.cpp (environment::configure_line_tabs): New function mutates object of `environment` class. (line_tabs_request): Replace manipulation of `environment` object's private member variable with call of the foregoing new public member function. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `linetabs` request. * src/roff/groff/tests/linetabs-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor. Separate engine logic from input parsing logic with respect to configuration of an environment's inter-word and supplemental inter-sentence space sizes. * src/roff/troff/env.h: Stop globally declaring `space_size()`. (class environment): Declare new member functions returning `void`: `set_interword_space_size()`, `reset_supplemental_intersentence_space_size()`, and `set_supplemental_intersentence_space_size()`. (The latter two differ because callers of the former require no knowledge of the default supplemental inter-sentence space size.) Drop declaration of `friend` function `space_size()`. * src/roff/troff/env.cpp (environment::set_interword_space_size) (environment::reset_supplemental_intersentence_space_size) (environment::set_supplemental_intersentence_space_size): New functions mutate object of `environment` class. (space_size): Replace manipulation of `environment` object's private member variables with calls of public member functions. 2026-04-08 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test `ss` request. * src/roff/groff/tests/ss-request-works.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-04 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/box.cpp (get_param): Add strictly unnecessary `return 0` statement with lint-style `NOTREACHED` comment to quieten `-Wreturn-type` warning on GCC 9.5.0, which apparently doesn't do deep enough control-flow analysis to figure that out for itself. 2026-04-03 Johannes Altmanninger <aclopte@gmail.com> * src/preproc/eqn/neqn.sh: * src/roff/nroff/nroff.sh: Use a subshell instead of the user's login shell `$SHELL` to evaluate features of the running shell. The login shell need not be a POSIX shell script interpreter; it could be tcsh(1), rc(1), fish(1), or something exotic. * src/roff/nroff/nroff.sh: Update fatal error diagnostic accordingly. Fixes <https://savannah.gnu.org/bugs/?68212>. [Problem introduced by me in commit c34c6cc58c, 2024-12-07. --GBR] 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp (xstrdup): * src/roff/groff/pipeline.c (sbasename): Tweak wording of error diagnostics; favor "cannot" over "unable to". Continues the long process of fixing Savannah #66519. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> [man,mdoc]: Apply new `rhw` request, clearing the user-defined hyphenation exception word list with every new man(7) or mdoc(7) document. * tmac/an.tmac (TH): * tmac/doc-old.tmac (Dd): * tmac/mdoc/doc-common (Dd): Do it. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Implement `rhw` request removing user-defined hyphenation exception words. (remove_hyphenation_exception_words_request): New function empties the current language's hyphenation exception word dictionary of all entries populated by the input. (We retain those supplied by files like "tmac/hyphenex.{cs,en,pl}".) (init_hyphenation_pattern_requests): Wire up `rhw` request name to `remove_hyphenation_exception_words_request()`. * src/roff/groff/tests/rhw-request-works.sh: Test it. * src/roff/groff/groff.am (groff_TESTS): Run test. * doc/groff.texi.in (Manipulating Hyphenation): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): * NEWS: Document it. Begins fixing <https://savannah.gnu.org/bugs/?64478>. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/dictionary.cpp (dictionary_iterator::get): Accept a null pointer as second argument, to handle situation where the caller is interested only in the dictionary's keys. Dereference the pointer only if that argument is not null. {There was no check for this previously; were I not making this change, I'd be assert(3)ing that instead.} 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Promote `process_input_stack()`'s local `can_accept_control_character` from local to global. (interpolate_macro_or_invoke_request): Drop early `return` when no invokable request or callable macro exists on control line. Add assert(3)ion at end of function to enforce invariant: once we've processed a control line, either `can_accept_control_character` must be true or the token pending on the input stream must represent the end of input. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (do_define_macro): Rename `reading_beginning_of_input_line` local to `can_terminate_definition_with_dot`. (transparent_throughput_file_request): Rename `reading_beginning_of_input_line` local to `is_at_beginning_of_input_line`. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (do_define_macro) (transparent_throughput_file_request): Fix code style nits. Arrange equality comparisons to avoid inadvertent lvalue assignment. Parenthesize formally complex expressions. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (process_input_stack): Slightly refactor. Align element type of templated STL `stack` container with the local variable representing its top: demote from `int` to `bool`. Rename the stack object from `trap_bol_stack` to `trap_accepting_control_character_stack` and the (effective) top from `reading_beginning_of_input_line` to `can_accept_control_character`. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Slightly refactor. Resequence prototypes and forward declarations. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Slightly refactor. Retype `default_buffer_size` from `int` to more idiomatic `size_t`. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (interpolate_macro): Rename this... (interpolate_macro_or_invoke_request): ...to this. (do_request, process_input_stack, do_define_macro): Update call sites. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (decode_escape_sequence_arguments): Revise wording of warning diagnostic. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename global `while_depth` to `while_loop_depth`. (while_request, while_break_flag, while_continue_request): Update dereference sites. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (make_glyph_node): Revise wording of warning diagnostic. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (embolden_font_request): Throw warning in category "style" if request arguments are ambiguous. This could arguably be made an error since the ambiguity is inherent; it shows up in Kernighan troff (see CSTR #54, 1992 revision). But let's try a style warning first. See Savannah #62776. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (translate_font) (mount_font_at_position) (associate_style_with_font_position) (select_underline_font) (define_font_specific_character) (remove_font_specific_character) (set_font_specific_special_fonts) (set_special_fonts) (zoom_font) (embolden_font) (configure_track_kerning) (constantly_space_font) (set_ligature_mode) (set_kerning_mode): Rename these... (translate_font_request) (mount_font_at_position_request) (associate_style_with_font_position_request) (select_underline_font_request) (define_font_specific_character_request) (remove_font_specific_character_request) (set_font_specific_special_fonts_request) (set_special_fonts_request) (zoom_font_request) (embolden_font_request) (configure_track_kerning_request) (constantly_space_font_request) (set_ligature_mode_request) (set_kerning_mode_request): ...to these, following a developing convention of naming request handlers to end with "_request". 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp (print_font_mounting_position_request): Declare as `static` since it does not require external linkage. 2026-04-03 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/reg.cpp (number_value_to_ascii): When diagnosing a value beyond representatable Roman numeral range, disclose the specific format demanded. Also tighten message wording. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Slightly refactor. Make request handler functions more consistent such that calling `skip_line()` is the last thing they do before returning. The goal is to build confidence that request handlers reliably leave the input stream in a well-defined state. Doing so would prevent several bugs I introduced (and resolved, with help from fellow developers) in the groff 1.23.0 development cycle, but also cases like Savannah #68064, which lurked insidiously for about 35 years. (read_hyphenation_patterns_from_file): Stop calling `tok.next()` before returning. (load_hyphenation_patterns_from_file_request) (append_hyphenation_patterns_from_file_request): Call `skip_line()` before returning. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (print_environment_request): Declare as `static` since it does not require external linkage. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Trivially refactor. (print_hyphenation_exceptions) (load_hyphenation_patterns_from_file) (append_hyphenation_patterns_from_file): Rename these... (print_hyphenation_exceptions_request) (load_hyphenation_patterns_from_file_request) (append_hyphenation_patterns_from_file_request): ...to these, following a developing convention of naming request handlers to end with "_request". 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Rationalize header inclusions. Use preprocessor to include <assert.h> header, which declares the `assert()` macro used in this file. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/dictionary.h (object_dictionary_iterator::get): Use explicit `reinterpret_cast` C++ operator instead of C-style cast. Annotate why we use this footgun. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (print_hyphenation_exceptions): Skip the remainder of the input line if no hyphenation language is configured. Fixes <https://savannah.gnu.org/bugs/?68203>. Problem introduced by me in commit e05fb6b377, 2024-11-07 (itself a fix for a SEGV under the same circumstance). 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68203. * src/roff/groff/tests/\ phw-request-skips-line-if-no-hyph-lang.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Continue interpreting any arguments subsequent to an invalid one. Doing so is more consistent with AT&T troff (tested: DWB 3.3, Plan 9 from User Space, Heirloom Doctools, Solaris). Demote diagnostic emitted in this circumstance from an error to a warning in category "char". Fixes <https://savannah.gnu.org/bugs/?68202>. Problem appears to date back to groff's birth. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #68202. * src/roff/groff/tests/\ hw-request-skips-only-invalid-arguments.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (add_hyphenation_exception_words_request): Stop throwing assert(3)ion if an argument contains a non-character. The assertion is invalidated by plausible user input like: `.hw baz\%qux` and similar. Begins fixing <https://savannah.gnu.org/bugs/?68202>. Problem introduced by me in commit effd6835dc, 19 November. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Trivially refactor. * tmac/an.tmac (TH, AT, UC, PT, BT) (an*abbreviate-page-identifier, an*abbreviate-inner-footer): Rename `an-extra[123]` strings to `an*extra[123]`. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> [man]: Trivially refactor. * tmac/an.tmac (an-style-warn): Rename this... (an*style-warn): ...to this. (an-deprecation-warn, an-blank-line-trap, an-leading-space-trap) (TH, BI, BR, IB, IR, RB, RI, RE, OP, EX, EE, MR): Update call sites. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac (BI, BR, IB, IR, RB, RI): Trivially refactor; rename `an-result` (a temporary string into which these macros' arguments are catenated with font style changes and italic corrections as appropriate) to `an*result`. 2026-04-01 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Trivially refactor; rename `an-msg` (a temporary string into which a diagnostic message is constructed) to `an*msg`. 2026-03-31 G. Branden Robinson <g.branden.robinson@gmail.com> * tmac/an.tmac: Ensure that the register `an-remap-I-style-in-headings` is always defined. Fixes `reg` warning when user employs `HF` string to configure an unusual heading font (one that isn't bold, or isn't a member of a family). 2026-03-30 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/relocate.cpp (set_current_prefix): Move call of `msw2posixpath()` from an all-configurations path through the function to an `#ifdef _WIN32`-only one. (msw2posixpath) [_WIN32]: Since this function is now called only if `_WIN32` is defined, shift an `#endif` to also define it only under that circumstance. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/lf.h: Drop argument names from function declarations, better aligning code style with groff's Stroustrup-ish conventions. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Improve #include discipline. To illuminate inter-module dependencies, get header files out of the business of `#include`-ing other header files, in favor of putting preprocessor inclusions only in translation units (C/C++ source files). Annotate dependencies. * src/include/lf.h: Move preprocessor inclusion of "stringclass.h" from here... * src/libs/libgroff/change_lf.cpp: ...to here. * src/libs/libgroff/lf.cpp: Drop unnecessary inclusion of "lib.h". 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Improve #include discipline. * src/include/lib.h: Declare `gen_tempname()` here... * src/libs/libgroff/mksdir.cpp: ...instead of here (as an `extern`). * src/libs/libgroff/tmpname.cpp: Resequence order of header file inclusions. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/strsave.cpp: Fix code style nits. Arrange equality comparisons to avoid inadvertent lvalue assignment. Favor C++ `static_cast<>` operator over omnipotent C-style cast, except to explicitly discard unused return value of function. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/strsave.cpp: Improve header file inclusion discipline. Use preprocessor to include "lib.h" header, which declares the `strsave()` function defined here. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/prime.cpp: Improve header file inclusion discipline. Use preprocessor to include "lib.h" header, which declares the `ceil_prime()` function defined here. Also unabbreviate data type of argument and return value; I prefer `unsigned int` to `unsigned`. (It's surprising that Thompson and Ritchie overcame their inclination to extreme abbreviation here; not only are "signed" and "unsigned" intelligible English words, but it proved to be a bad idea to make their syntax closely resemble what eventually became ANSI C89's type qualifiers `const`--notably an abbreviation again--and `volatile`. It took ISO C99 to bring us `uintN_t` and `intN_t` types, at which point Ritchie, at least, appears to have given up on staying current with the language, never preparing with Kernighan a third edition of their _The C Programming Language_ before his death in 2011. I find it a fun diversion to consider how many problems of the C language can be sufficiently explained by a tacit but intense resentment of strong typing.) 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/mksdir.cpp: Improve header file inclusion discipline. Use preprocessor to include "lib.h" header, which declares the `mkdsir()` function defined here. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/relocate.cpp (msw2posixpath): Declare as `static` since it does not require external linkage. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/relocate.cpp (searchpathext): Declare as `static` since it does not require external linkage. Doing so reveals that the function is not used at all except on Windows systems, so bracket with preprocessor `#ifdef`/`#endif`, avoiding `-Wunused-function` warning from GCC on GNU/Linux. 2026-03-29 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/pipeline.c: Check for memory allocation failure, and emit a fatal error diagnostic if it occurs. Unfortunately I was unable to test this code, as it's only reachable on non-POSIX systems (via `is_system_shell()` and `system_shell_dash_c()`; see "src/include/nonposix.h"). Continues the long process of fixing Savannah #68192. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (main): Fix spaces missing from recently recast error diagnostic messages. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (parse_output_page_list, main): Revise and parallelize wording of error diagnostics relating to invalid command-line option arguments. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (in_output_page_list, main): Explicitly compare values of pointer type to null pointer literals instead of letting them pun down to Booleans. 2022-02-05 Dave Kemper <saint.snit@gmail.com> * doc/groff.texi.in (Assigning Register Formats): Clarify guidance from Unicode regarding Roman numerals. Fixes <https://savannah.gnu.org/bugs/?61998>. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Withdraw support for AT&T's exotic Roman numerals except in compatibility mode. * src/roff/troff/reg.cpp (number_value_to_ascii): When not in compatibility mode, reduce acceptable range of register values when attempting to format them with Roman numerals to that expressible only with the numerals "MDCLXVI". (In compatibility mode, retain support for AT&T troff's novelty of Roman numerals "W" for 5,000 and "Z" for 10,000.) Recast error diagnostic when register value is outside of the representable range. * doc/groff.texi.in (Assigning Register Formats): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?61999>. Thanks to Dave Kemper for the report. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/reg.cpp (number_value_to_ascii): Trivially refactor. Rename cryptic local `s` to `roman_numerals`. Also parenthesize formally complex expression. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (environment::set_font): Invert returned Boolean value when this function is activated on a font-change operation that lies on the unformatted portion of an input line interrupted/continued with `\c`. Returning "true" was inconsistent with other environment mutator functions in this file, including the signature of this member function taking an integer (rather than a `symbol`) argument. Eventually, we should make these functions unreachable in such circumstances. See Savannah #67812. 2026-03-28 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Rename `WARN_INVALID` enumeration constant to `WARN_DUMMY`. The latter is more descriptive of its purpose. * src/roff/troff/troff.h (enum warning_category): * src/roff/troff/input.cpp (debug, error, fatal): Do it. 2026-03-27 G. Branden Robinson <g.branden.robinson@gmail.com> [libdriver]: Rename `do_file()` to `interpret_troff_output_file()`. There are several `do_file()` functions in the groff code base; each does something different. * src/include/driver.h: Update declaration. * src/libs/libdriver/input.cpp (do_file): Rename this... (interpret_troff_output_file): ...to this. * src/devices/grodvi/dvi.cpp (main): * src/devices/grohtml/post-html.cpp (main): * src/devices/grolbp/lbp.cpp (main): * src/devices/grolj4/lj4.cpp (main): * src/devices/grops/ps.cpp (main): * src/devices/grotty/tty.cpp (main): Update call sites. 2026-03-27 G. Branden Robinson <g.branden.robinson@gmail.com> Rationalize header file inclusions. To illuminate inter-module dependencies, get header files out of the business of `#include`-ing other header files, in favor of putting preprocessor inclusions only in translation units (C/C++ source files). Annotate dependencies. * src/devices/grodvi/dvi.cpp: * src/devices/grodvi/dvi.cpp: * src/devices/grohtml/html-table.cpp: * src/devices/grohtml/html-text.cpp: * src/devices/grohtml/html-text.h: * src/devices/grohtml/post-html.cpp: * src/devices/grolbp/lbp.cpp: * src/devices/grolj4/lj4.cpp: * src/devices/grops/ps.cpp: * src/devices/grotty/tty.cpp: * src/include/color.h: * src/include/driver.h: * src/include/printer.h: * src/libs/libdriver/input.cpp: * src/libs/libdriver/printer.cpp: * src/libs/libgroff/color.cpp: * src/roff/troff/dictionary.cpp: * src/roff/troff/div.cpp: * src/roff/troff/env.cpp: * src/roff/troff/input.cpp: * src/roff/troff/mtsm.cpp: * src/roff/troff/mtsm.h: * src/roff/troff/node.cpp: * src/roff/troff/number.cpp: * src/roff/troff/reg.cpp: * src/roff/troff/troff.h: Do it. 2026-03-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-table.cpp: Arrange inclusions of libgroff header files before application-specific ones. 2026-03-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grohtml/html-table.cpp: Improve header file inclusion discipline. Drop unnecessary preprocessor inclusions of standard <stdio.h> and local (but nonexistent) "ctype.h" header files. 2026-03-26 G. Branden Robinson <g.branden.robinson@gmail.com> [grodvi,grolbp,grops]: Improve header file inclusion discipline. * src/devices/grodvi/dvi.cpp: * src/devices/grolbp/lbp.cpp: * src/devices/grops/ps.cpp: Drop unnecessary preprocessor inclusions of "paper.h" header file. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/driver.h: Drop preprocessor inclusion of "geometry.h" from here, and add it where it's needed... * src/devices/grodvi/dvi.cpp: * src/devices/grolj4/lj4.cpp: * src/devices/grops/ps.cpp: * src/libs/libgroff/geometry.cpp: ...here. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/error.cpp: Drop `extern` declaration of `fatal_error_exit()`, now declared properly in "error.h". * src/libs/libgroff/fatal.cpp: Use preprocessor to include "errarg.h" and "error.h" header files. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/libs/libgroff/change_lf.cpp (change_filename): Parenthesize formally complex expressions. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/htmlhint.h: Drop redundant keyword `extern` from `html_begin_suppress()` and `html_end_suppress()` function declarations. * src/libs/libgroff/htmlhint.cpp: Use preprocessor to include "htmlhint.h" header file. Fixes `-Wmissing-declarations` warning. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/lf.h: Declare `change_filename()` and `change_lineno()` properly here... * src/libs/libgroff/lf.cpp: ...instead of here, with `extern`. * src/libs/libgroff/change_lf.cpp: Use preprocessor to include "lib.h" header file, also obviating the need for an `extern` declaration of `strsave()`, declared by the same header file. Fixes `-Wmissing-declarations` warning. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> [libgroff]: Boolify and rename static class member variable. * src/include/cmap.h (class cmap_init): `initialised` of type `int` becomes `is_initialised` of type `bool`. * src/libs/libgroff/cmap.cpp: Assign to it with Boolean, not integer, literals. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/include/error.h: Declare `fatal_error_exit()` here... * src/libs/libgroff/error.cpp: ...instead of here. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (is_ascii): Fix code style nit. Produce return value with value construction syntax instead of a C-style type cast. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (is_ascii): Boolify. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/ps.cpp (radians): Fix code style nit. Parenthesize formally complex expression. 2026-03-25 G. Branden Robinson <g.branden.robinson@gmail.com> [grodvi,post-grohtml,grops]: Improve declarations. * src/devices/grodvi/dvi.cpp (scale): * src/devices/grohtml/post-html.cpp (stop, replace_negate_str) (replace_str): * src/devices/grops/ps.cpp (degrees, radians, is_ascii): * src/devices/grops/psrm.cpp (read_one_of) (skip_possible_newline): Declare functions as `static` since they do not require external linkage. 2026-03-26 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grolbp/lbp.cpp: Rearrange and annotate header file inclusions, placing module-specific header files last, after those of libraries. 2026-03-23 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix `-Wformat-truncation` warnings. * src/roff/troff/input.cpp (charinfo::dump): * src/roff/troff/node.cpp (glyph_node::asciify): When preparing to format the hexadecimal representation of a Unicode code point with `get_unicode_code()`, prepare a buffer of sufficient size to store a 32-bit unsigned integer, as that's what snprintf(3)'s `%X` conversion produces. It doesn't know that Unicode is only a 20.1-bit code. Even if it did, our use of `int` to store the value fails to communicate that constraint to snprintf(3). Even the use of `int` is a bit slippery of us, since it's a signed type with a 31-bit significand. But libgroff's `glyph_to_unicode()`, which `get_unicode_code()` calls, uses "-1" to signal an error in-band. 2026-03-23 G. Branden Robinson <g.branden.robinson@gmail.com> * configure.ac: Stop interpolating `GROFF_CXX_CHECK` macro. * m4/groff.m4 (GROFF_CXX_CHECK): Delete macro definition. It dates back at least to groff 1.10 (1995). ISO standardized C++ in 1998; it no longer seems necessary in 2026 to check that a purported C++ compiler validly compiles a `main()` that simply returns `0`, nor that it correctly arranges for constructors and destructors of statically allocated objects to run. A C++ compiler failing to meet these criteria is not fit for purpose; working alternatives are universally available. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (char_list::set, char_list::get): Fix code style nit. Parenthesize formally complex expressions. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (class char_list): Rename member variable `len` to `length`. (char_list::char_list, char_list::get_length, char_list::append) (char_list::set, char_list::get): Update dereference sites. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/request.h (class macro): Rename member variable `len` to `length`. * src/roff/troff/input.cpp (macro::macro, macro::operator=) (macro::append, macro::get_length, macro::chop) (macro::print_size, macro::json_dump) (string_iterator::string_iterator) (operator==): Update dereference sites. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Rename some member functions. * src/roff/troff/request.h (class macro): * src/roff/troff/input.cpp (class char_list, class node_list): Rename `length()` member function to `get_length()`, like other accessors in GNU troff. (char_list::length) (node_list::length) (macro::length): Rename these... (char_list::get_length) (node_list::get_length) (macro::get_length): ...to these. (macro:append, macro_header::json_dump_macro) (do_string_case_transform, substring_request): Update call sites. (do_string_case_transform): Drop unnecessary name space qualification. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Ignore request if both the "start" and "end" indices are out of range. * src/roff/troff/request.h (class macro): Drop declaration of `friend` function `substring_request()`. * doc/groff.texi.in (Strings) <substring>: * man/groff_diff.7.man (New requests) <substring>: Document it. * src/roff/groff/tests/\ substring-request-on-invalid-range-is-no-op.sh: Test new behavior. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Slightly refactor. Use `macro` object's public member function `length()` to obtain its length rather than its private member variable `len`. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Slightly refactor. Introduce new local variable `len` to store value of `len` `macro` object being operated on, in case the compiler isn't smart enough to cache a repeated member variable access itself. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Slightly refactor. When iterating through a `macro` object's contents, use its stored length to decide when to stop instead of relying on the `string_iterator` accessing each element to return an `EOF` character. This change prepares for a future where GNU troff's internal character type might not encode `EOF` (at least not in the same way that the C standard I/O library does). This change also weakens the resemblance of file and string iterators, but that seems acceptable to me, because string and macro contents have already undergone input processing. In the future, this will mean decoding of UTF-8 sequences and possible NFD decomposition to one or more `char32_t` (or similar) objects. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Recast warning diagnostics when request argument(s) out of range. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Trivially refactor. Rename local variable `real_length` to `operable_length`. Annotate its meaning. 2026-03-21 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Drop dead code. (class node_list): Undeclare `extract()` member function. (node_list::extract): Drop unused member function definition. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Unit-test no-op behavior of `substring` request when one of its index arguments is both negative and out-of-range. * src/roff/groff/tests/\ substring-request-on-invalid-range-is-no-op.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-03-22 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (substring_request): Revise wording of error diagnostic. The use of the term "extract" could be taken to mean that the original string was left intact, which is not the case; if the operation proceeds, it edits the string/ macro/diversion's contents in place. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (abort_request): Slightly refactor. Avail ourselves of the `has_arg()` function; don't mess with any of the input character processing rigmarole if there is no such input to process. Also flush the standard error stream only if we have written to it. If someone feels that this should happen anyway "just in case" some other part of GNU troff neglects to do so, then an even better place for that insurance policy is `write_any_trailer_and_exit()`--it lies on _all_ controlled paths out of the program. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/ab-request-works.sh: Conform better to modern groff test conventions. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> Skip more tests on buggy Solaris. * src/roff/groff/tests/ab-request-works.sh: * src/roff/groff/tests/handle-special-input-code-points.sh: * tmac/tests/an-ext_MR-works.sh: * tmac/tests/an_MR-works.sh: * tmac/tests/an_font-remapping-does-not-affect-titles.sh: Skip test if the shell in "/usr/xpg4/bin" mis-implements the `unset` shell built-in command. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (abort_request): Trivially refactor; for clarity, apply DeMorgan's Law so that the `if` statement guarding entry to a `for` loop matches the expression tested within it. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Improve type discipline with respect to groff's internal character representation. (macro::append): Append to macro contents using explicitly unsigned character (integer) literal rather than a signed one, for conceptual clarity. "groff characters" are, for now, unsigned bytes. (macro_header::copy, string_iterator::fill, substring_request): Compare character read from an groff string iterator to unsigned character {integer} literal. Continues the long process of fixing Savannah #67735. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (composite_glyph_name) (do_define_string): Explicitly compare values of pointer type to null pointer literals instead of letting them pun down to Booleans. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (string_iterator::fill) (decode_macro_call_arguments, decode_escape_sequence_arguments) (do_define_string, do_define_macro, do_string_case_transform) (substring_request, do_non_interpreted, abort_request): Parenthesize formally complex expressions. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (read_char_in_copy_mode) (token::next, process_input_stack, macro_header::copy) (string_iterator::fill, decode_macro_call_arguments) (decode_escape_sequence_arguments, do_define_string) (interpolate_positional_parameter, do_define_macro) (do_string_case_transform, substring_request, asciify_request) (unformat_macro, non_interpreted_node::interpret) (do_non_interpreted, skip_branch, while_request) (stream_write_macro_request, abort_request, transparent_throughput_file_request, do_error): Arrange equality comparisons to avoid inadvertent lvalue assignment. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor. Make the "guts" of the `chop_macro()` function a member function of class `macro`. * src/roff/troff/request.h (class macro): Declare member function `chop()`, taking no argument and returning `void`. Drop declaration of `friend` function `chop_macro()`. * src/roff/troff/input.cpp: Drop forward declaration of `chop_macro()`. (macro::chop): Member function takes responsibility for updating a `macro` object, reducing internally stored length of its contents if possible. (Exception: token values used internally to manage the state of compatibility mode do not contribute to the length count.) (chop_macro): Rename this... (chop_request): ...to this, following a developing convention of naming request handlers to end with "_request". Declare it `static`. Replace lengthy `else` branch with a call of the resolved macro's `chop()` member function. (init_input_requests): Update wire-uppery of "chop" request name. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (chop_macro): Arrange equality comparisons to avoid inadvertent lvalue assignment. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (chop_macro): Drop error diagnostic that was thrown only in the event of an internal logic error. An assert(3)ion now handles that, and in an `NDEBUG` build the formatter can safely proceed. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (chop_macro): Add assert(3)ion to verify an invariant in the logic. Instrumenting this test during a full groff build reveals that it's reached 670 times. 2026-03-20 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (chop_macro): Boolify and rename local variable. `have_restore` becomes `contains_mode_tokens`. Assign to it with Boolean, not integer, literals. 2026-03-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (transparent_throughput_file_request): Disallow all grout-invalid characters in "safer" transparent file throughput {implemented by the `trf` request}, including all C0 control code points except the line feed (Unix newline), and all C1 controls and Latin-1 extension code points. Revise warning diagnostic message. * doc/groff.texi.in (Host System Service Access, Warnings): * man/groff.7.man (Request short reference): * man/groff_diff.7.man (New requests): * src/roff/troff/troff.1.man (Warnings): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?66547>. 2026-03-19 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grotty/tty.cpp (tty_printer::end_page): Further simplify new dynamic memory management strategy to avoid memory leaks. Always `delete[]` (free) the `lines` array at the end of a page, regardless of the value of `nlines`. Comparing the latter's value to `default_lines_per_page` was a naïve technique. First, reducing the page length _below_ the default would also cause a reallocation, so it needs to be freed. Second, if the input document resized the page multiple times, with the final value being equal to `default_lines_per_page`, we would similarly wrongly skip the `delete[]`. Since `begin_page()` _always_ allocates storage with `new` and `add_char()` _always_ reallocates it with `new` (and deletes the old one) in the event of a resize, we can (and should) _always_ delete the storage allocated to `lines` at the end of a page. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/eqn/tests/neqn-smoke-test.sh: Configure `GROFF_BIN_PATH` so that the build tree's "neqn" is more likely to locate the "eqn" that is also in the build tree. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> [eqn]: Add test of neqn's wrapping of eqn; their versions should match. * src/preproc/eqn/tests/neqn-finds-matching-eqn.sh: Add test. * src/preproc/eqn/eqn.am (eqn_TESTS): Run test. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: When issuing warning diagnostic messages, report the warning category governing its emission. Idea ripped off from GCC, which has been doing similarly for many years. * src/roff/troff/troff.h (enum warning_category): Add a `WARN_INVALID` enumeration constant with value zero, since we're extending the signatures of diagnostic functions to pass the warning category as a parameter. Doing so has no meaning for non-warning diagnostics, but `do_error()` handles _all_ diagnostic messages, and so it must be prepared for this extra data. This being C++98, we also don't have option types. * src/roff/troff/input.cpp (lookup_warning_by_bitset): New function looks up a bitset in the `warning_map` array and returns a pointer to a constant string naming the corresponding warning category. (do_error): Expect new parameter of type `warning_category`. If the diagnostic is of type `WARNING`, look up the new parameter with `lookup_warning_by_bitset` and report it in the message to the user. (debug, warning, output_warning, error, fatal): Update to pass `do_error()` either the given warning category, or `WARN_INVALID` if the diagnostic is not a warning. * NEWS: Document this. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Trivially refactor. * src/roff/troff/troff.h (enum warning_type): Rename this... (enum warning_category): ...to this, for consistency with groff documentation and because the enumeration constants aren't distinct "types" in the C++ sense. * src/roff/troff/input.cpp (warning, output_warning): Update users of the enumeration type. Any given diagnostic warning corresponds to only one warning category. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. (struct warning_category): Rename this... (struct warning_selection): ...to this. This data structure creates a mapping between C strings and a bit vector. The strings "all" and "w" aren't warning categories, but convenient shorthands for selection of multiple warning categories. 2026-03-18 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Trivially refactor. Rename global array `warning_table` to `warning_map`, for greater precision. (lookup_warning): Update dereference site. Also rename this... (lookup_warning_by_name): ...to this. (enable_warning, disable_warning): Update call sites of foregoing. (struct warning_category): Rename member variable `set` to `bitset`, for clarity. 2026-03-17 G. Branden Robinson <g.branden.robinson@gmail.com> [grotty]: Revise dynamic memory allocation strategy. The `tty_printer` class maintains a member variable `lines`, a pointer to a pointer to type `tty_glyph`, which it uses as a reference to a dynamically allocated rectangular array of character cells. The size of this array is not fixed; `pl` requests in an input document can grow or shrink the page length. The strategy it has used to date to handle a non-default page length proved to perform badly with the new approach to continuous rendering used by groff's man(7) and mdoc(7) packages in version 1.24.0. If the initial allocation of 66 lines (rows) of character cells was insufficient because the input extended the page length, it grew the allocation (and performed a memcpy(3) of its existing contents) by only as much as required to accommodate the current vertical drawing position, plus one line. For a famously lengthy man page like bash(1), this meant many repeated trips through new/memcpy()/delete sequences, giving the language runtime allocator a lot of work to do. In an extreme case {formatting 64 catenated copies of bash(1)}, this led to long runtimes with two-thirds of program time spent in the kernel. The new strategy is to double the allocation size every time it is exceeded, but also to free any resized allocation at the end of every page. When continuously rendering, there will only ever be one page anyway, but this tactic is more considerate for the {admittedly niche} case of paginated documents that vary the page length frequently--which is the approach that groff 1.23.0 and earlier took to continuous rendering, and that other macro packages or documents might still. (tbl(1) users should beware of the sort of problem observed in Savannah #65189.) This change reduces grotty(1)'s wall-clock time for the aforementioned 64 copies of bash(1) from 92.1 seconds to 3.5, and the proportion of process time spent in the system from 63.3% to 6.38%. * src/devices/grotty/tty.cpp: Define new global constant integer, `default_lines_per_page`, obviating a local literal in the `tty_printer` constructor. (class tty_printer): Drop empty body from `begin_page()` declaration. (tty_printer::tty_printer): Replace open-coded initial allocation of storage backing `lines` member variable with call to `begin_page()`. (tty_printer::~tty_printer): Stop freeing the storage backing `lines` here. Instead... (tty_printer::end_page): ...do it here, if its size grew beyond the default initial allocation. (tty_printer::add_char): Apply the new reallocation strategy. Use memset(3) to zero out the new storage before memcpy(3)-ing the previous contents into (part of) it, eliminating a `for` loop that manually zeroed out the pointers after the copied storage. While arguably wasteful to write some memory twice, the runtime can easily optimize `memset()`, as, for instance, glibc does.[1] Measurement shows that any reduced efficiency here is lost in the noise of the vast improvement arising from the allocation size-doubling approach. (tty_printer::begin_page): Implement. Initially allocate `default_lines_per_page` rows of `tty_glyph` pointers. Use memset(3) here, too, to zero them out. [1] https://sourceware.org/git/?p=glibc.git;a=commit;\ h=46b5e98ef6f1 Fixes <https://savannah.gnu.org/bugs/?68145>. Thanks to Dmytro Bagrii for the report, to Morten Bo Johansen for help reproducing the problem, and to Deri James for a shrewd pointer in the right direction. 2026-03-17 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (struct warning_category): Add {further} `const` qualification to type and its members. This structure is immutable for the lifetime of the program. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * bootstrap.conf (gnulib_modules): Drop `putenv`. Fixes <https://savannah.gnu.org/bugs/?68024>. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp: Refactor: migrate from putenv(3) to setenv(3) (4/4). (xputenv): Drop no-longer-used function. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp: Refactor: migrate from putenv(3) to setenv(3) (3/4). Drop global pointer variable `groff_bin_path`. (main): Stop initially populating `newpath` local variable with "PATH=" in favor of straightforward call to `xsetenv()` with `PATH` string literal and contents of `newpath` groff string as arguments. Because, unlike putenv(3), setenv(3) makes its own copies of its C string operands, we no longer need to reserve statically allocated storage for them to keep them from going out of scope. (xexit): Stop free(3)ing abandoned variable `groff_bin_path`. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp: Refactor: migrate from putenv(3) to setenv(3) (2/4). Drop global pointer variable `saved_path`. (main): Drop local string `g` containing shell-like environment variable assignment in favor of straightforward call to `xsetenv()` with `GROFF_PATH__` string literal and existing `path` (populated by `getenv("PATH")`) as arguments. Because, unlike putenv(3), setenv(3) makes its own copies of its C string operands, we no longer need to reserve statically allocated storage for them to keep them from going out of scope. (xexit): Stop free(3)ing abandoned variable `saved_path`. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp: Refactor: migrate from putenv(3) to setenv(3) (1/4). Drop global pointer variable `groff_font_path`. (xsetenv): New function wraps setenv(3) as `xputenv()` does putenv(3). (main): Replace local string `e` containing shell-like environment variable assignment with string `value` containing only the value to be assigned. Call `xsetenv()` with `GROFF_FONT_PATH` string literal and `value` as arguments. Because, unlike putenv(3), setenv(3) makes its own copies of its C string operands, we no longer need to reserve statically allocated storage for them to keep them from going out of scope. (xexit): Stop free(3)ing abandoned variable `groff_font_path`. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> Refactor: migrate from putenv(3) to setenv(3). * src/devices/grops/ps.cpp (main): * src/devices/grops/psrm.cpp (resource_manager::output_prolog): * src/roff/troff/input.cpp (main): Do it. Begins fixing <https://savannah.gnu.org/bugs/?68024>. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/groff.cpp (main): Slightly refactor. Rename local variable `f` to `newpath` to make its purpose clearer. Reduce number of string operations. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/device.c (find_file): Initialize local pointer variables with explicit null pointer constant. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> Trivially refactor. Compare C/C++ objects of `char` type to character literals instead of punning them down to Booleans. * src/devices/xditview/device.c (find_file): * src/libs/libgroff/device.cpp (device_init): * src/libs/libgroff/searchpath.cpp (search_path::search_path): * src/roff/groff/groff.cpp (main): * src/roff/troff/input.cpp (interpolate_environment_variable): Do it. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> Trivially refactor. Compare return value of getenv(3) to explicit null pointer constants appropriate for C or C++ instead punning that return value down to a Boolean. * src/devices/grops/psrm.cpp (resource_manager::output_prolog): * src/devices/grotty/tty.cpp (main): * src/devices/xditview/device.c (find_file): * src/libs/libgroff/curtime.cpp (current_time): * src/libs/libgroff/device.cpp (device_init): * src/libs/libgroff/relocate.cpp (set_current_prefix): * src/libs/libgroff/searchpath.cpp (search_path::search_path): * src/preproc/html/pre-html.cpp (scanArguments): * src/preproc/refer/refer.cpp (possibly_load_default_database): * src/roff/groff/groff.cpp (main): * src/utils/lkbib/lkbib.cpp (main): Do it. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/grops/psrm.cpp (resource_manager::output_prolog): Handle getenv(3) failure. Having just written successfully to the process environment (else we `fatal()`ed out), assert(3) that reading back the same environment variable with getenv(3) returned a non-null pointer. If it didn't and `NDEBUG` is defined, `fatal()` out in this case too. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/device.c: Slightly refactor. Convert C/C++ preprocessor macro `FONTPATH_ENV_VAR` to global `const` object named `FONT_ENV_VAR`, for consistency with identical usage in "src/libs/libgroff/fontfile.cpp". (find_file): Update getenv(3) call to use the new symbol name. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * m4/groff.m4 (GROFF_USE_GROFF_ALLOCATOR): Update help string to mark `--enable-groff-allocator` configuration option as deprecated. (GROFF_ALLOCATOR_NOTICE): New macro warns user of option's planned withdrawal if they have selected it. * configure.ac: Interpolate `GROFF_ALLOCATOR_NOTICE` macro. * NEWS: Document this. Fixes <https://savannah.gnu.org/bugs/?67936>. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * configure.ac: Stop reporting use of libgroff's custom memory allocator in configuration summary. Begins fixing <https://savannah.gnu.org/bugs/?67936>. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/devices/xditview/xditview.c (main): Avoid invalid memory access by checking that `argc` is greater than 1 before trying to access `argv[1]`. Fixes <https://savannah.gnu.org/bugs/?68152>. 2026-03-15 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/reg.cpp (rename_register_request): Fix typo in warning diagnostic message. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (is_conditional_expression_true): Keep track of how many leading `!` complementation operators appear in a conditional expression. If multiple occur and the formatter is in AT&T compatibility mode, warn in category "syntax" that the construction is not portable to AT&T troffs. * doc/groff.texi.in (Other Differences): * man/groff_diff.7.man (Other differences): Document this syntactical difference. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/preproc/html/pushback.cpp (pushBackBuffer::readInt): Eliminate initialized-and-written-to-but-never-read local variable `c`. Thanks to Bjarni Ingi Gislason for the report in <https://\ lists.gnu.org/archive/html/groff/2026-03/msg00072.html>. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp: Throw warning in category "style" if {a} centering or right-alignment is configured while a temporary indentation is in effect; or {b} a temporary indentation request is issued while centering or right-alignment are in effect. (cancel_temporary_indentation): New function--which I suspect should be a member function of class `environment`, but that would be confusingly inconsistent with existing practice--sets the temporary indentation amount to zero and sets the property of having a temporary indent off. (Those are not the same thing. Temporary indentation _overrides_ ordinary indentation, so a temporary indent of zero is often sensible.) Also tell the mysterious MTSM machine that temporary indentation is off. (center): Throw the aforementioned warning from this `ce` request handler. (right_justify): Throw the aforementioned warning from this `rj` request handler. (temporary_indent): Throw the aforementioned warning from this `ti` request handler. * src/roff/troff/env.h (class environment): Declare new non-OO `cancel_temporary_indentation()` function as a friend. {Eighties-style. <finger guns>} * doc/groff.texi.in (Warnings): * src/roff/troff/troff.1.man (Warnings): Document it. Fixes <https://savannah.gnu.org/bugs/?64275>. See <https://lists.gnu.org/archive/html/groff/2023-05/msg00079.html> for illustration of the ill-defined composition of these features and (brief) discussion. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/env.cpp (environment::space): Throw warning in category "style" if the end of a sentence is detected before the end of a text line. * doc/groff.texi.in (Warnings): * src/roff/troff/troff.1.man (Warnings): Document it. Fixes <https://savannah.gnu.org/bugs/?68151>. Thanks to Alex Colomar for the original feature request. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (struct warning_category): Add new warning category, "style" (with symbol `WARN_STYLE`). Suppress its enablement from the `-wall` command-line option shorthand, as categories "di", "mac", and "reg" are. I expect much noise from historical documents in forthcoming applications of this category, and AT&T troff syntax is too loosely specified to support some of the ideas I have in mind. * doc/groff.texi.in (Warnings): * src/roff/troff/troff.1.man (Warnings): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?62776>. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (read_request): Boolify and rename some local variables. `reading_from_terminal` becomes `is_reading_from_terminal`. `nl` becomes `saw_newline`. Assign to `is_reading_from_terminal`, `had_prompt`, and `saw_newline` with Boolean, not integer, literals. Use idiomatic test expressions. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (word_space_node::need_reread) (hmotion_node::need_reread): Assign to `bool`-typed `unformat` member variable with Boolean, not integer, literals. Continues commit 8ab4aa8bec, 2024-08-14. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Boolify and rename `global_diverted_space`. * src/roff/troff/input.cpp: Demote global variable `global_diverted_space` from `int` to `bool` and rename it to `have_global_diverted_space`. Annotate it as used only by the MTSM feature, which I hope some day either to better understand, or to remove. * src/roff/troff/env.cpp: Update an `extern` declaration. * src/roff/troff/input.cpp (diverted_space_node::need_reread): * src/roff/troff/env.cpp (environment::do_break): Update sites of use. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> * src/utils/pfbtops/pfbtops.am (nodist_EXTRA_pfbtops_SOURCES): Drop "trick" macro definition 'to force the use of C++ compiler[.] See the Automake manual, "Libtool Convenience Libraries"'. However, groff doesn't use Libtool. The nonexistent dummy file declared here impedes the use of Automake's "tags" target. After this change, "pfbtops"'s linkage is unchanged per ldd(1), the smoke test still passes, and the program appears to still operate fine on the file "build/font/devpdf/symbolsl.pfb"...on amd64 GNU/Linux with a GNU toolchain. 2026-03-14 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Implement `.ul` register for orthogonal completeness with respect to the `.ce`, `.rj`, and `.it` registers. * src/roff/troff/env.h (class environment): Declare new accessor member function `get_underlined_line_count()`. * src/roff/troff/env.cpp (environment::get_underlined_line_count): Define it. (init_env_requests): Wire up `.ul` register name to `get_underlined_line_count()`. * src/roff/groff/tests/dot-ul-register-works.sh: Test it. * src/roff/groff/groff.am (groff_TESTS): Run test. * doc/groff.texi.in (Artificial Fonts): * man/groff.7.man (Read-only registers): * man/groff_diff.7.man (New registers): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?68130>. 2026-03-10 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #68137. * src/roff/troff/number.cpp (is_valid_expression): When interpreting a parenthesized sub-expression within a numeric expression while in AT&T compatibility mode, throw a warning in category "syntax" if encountering a space in the input, advising of non-portability to that family of formatters. GNU troff continues to interpret the expression as it always has. Fixes <https://savannah.gnu.org/bugs/?68137>. 2026-03-10 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Make `want_att_compat` global. This is a prerequisite to fixing Savannah #68137. * src/roff/troff/input.cpp: Drop its `static` storage class. * src/roff/troff/token.h: Declare it with `extern` storage class. 2026-01-28 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (token::description): Refer to C0 control characters (that are valid as GNU troff input) and Basic Latin DEL (all unprintable) by their decimal code points, not by writing them out literally. Noted while working on Savannah #67978. 2026-01-02 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Fix Savannah #64300. * src/roff/troff/div.cpp (space_request): If the `sp` request is invoked with the no-break control character (that is, "'sp") when the drawing position is off the page (as when the formatter starts up), ignore the request and throw an error diagnostic. This is inconsistent with AT&T troffs (including Heirloom Doctools) and neatroff, but precisely consistent with all other requests that have different semantics when invoked with the no-break control character. When invoked thus, none of `br`, `brp`, `ce`, `cf`, `fi`, `fl`, `in`, `nf`, or `rj` move the drawing position onto the page. * tmac/doc-old.tmac (Sh): If the formatter claims groff compatibility, simply emit a `br` request to start the (next) document instead of invoking `sp` with the no-break control character. * doc/groff.texi (Manipulating Spacing): Document that invoking `'sp` in the top-level diversion when the vertical drawing position is negative is an error. * doc/groff.texi (Other Differences): * man/groff_diff.7.man: (Other differences): Document difference between AT&T and GNU troffs. (AT&T troff's behavior looks buggy to me...) Fixes <https://savannah.gnu.org/bugs/?64300>. Thanks to Dave Kemper for the discussion. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Add `WARN_ESCAPE` to default warning "mask". (It's not a mask, just a vector--or a set.) * doc/groff.texi (Warnings): * src/roff/troff/troff.1.man (Warnings): * NEWS: Document it. Fixes <https://savannah.gnu.org/bugs/?67783>. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Add `WARN_SYNTAX` to default warning "mask". (It's not a mask, just a vector--or a set.) * doc/groff.texi (Warnings): * src/roff/troff/troff.1.man (Warnings): * NEWS: Document it. Begins fixing <https://savannah.gnu.org/bugs/?67783>. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Add `WARN_DELIM` to default warning "mask". (It's not a mask, just a vector--or a set.) * doc/groff.texi (Warnings): * src/roff/troff/troff.1.man (Warnings): * NEWS: Document it. Continues the long process of fixing Savannah #66009. 2026-03-10 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (do_device_extension): Emit error diagnostic instead of accessing invalid memory if an attempt is made to output a device extension command while the currently selected font is invalid. In the future we might not need the current font to be valid to format a device extension node, but for now we do because it "dirties", and therefore has to subsequently restore, several bits of font-related state. We might be able to make this unnecessary with a parameterized extension to the `fl` request. See Savannah #66187. Fixes <https://savannah.gnu.org/bugs/?67978>. Thanks to Bruno Haible for the report. 2026-03-09 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/groff/tests/\ do-not-crash-on-mismatched-diversion-request.sh: Revise and expand test. Stop checking for core dump file, as this is not necessarily reliable. Instead seed test input with formattable words that we expect to be present or not, depending on whether and where the formatter exits abnormally. Check that closing a `box` diversion with `di` is not a fatal error, and that closing a `di` diversion with `box` _is_ fatal. Thanks to Bob Friesenhahn and Zack Weinberg for the suggestion to not rely on core dump files as failure oracles. 2026-03-05 G. Branden Robinson <g.branden.robinson@gmail.com> [groff]: Regression-test Savannah #67978. * src/roff/groff/tests/\ do-not-crash-on-backslash-X-if-font-invalid.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2026-03-05 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Add internal mechanism for distinguishing validity of font mounting positions as _values_ from their usability to format output. A font mounting position is an integer with a limited range. The font information associated with that {otherwise valid} position can nevertheless not be usable for formatting output. While the formatter attempts to prevent input documents from contorting themselves into this situation, it also affords a means of egress from valid state. See the `sty` (counter-)example in groff's Texinfo manual <https://\ www.gnu.org/software/groff/manual/groff.html.node/ Font-Families.html>. Add new function taking an integer mounting position and returning a Boolean. * src/roff/troff/node.h: Declare it. * src/roff/troff/node.cpp (is_valid_font): Define it. 2026-03-05 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Refactor resolution of currently selected font (which can be an abstract style, or even invalid) to a font mounting position. * src/roff/troff/node.h: Declare function `resolve_current_font_to_mounting_position()`; we'll need access to it from "input.cpp" in a forthcoming change. * src/roff/troff/node.cpp (env_resolve_font): Drop `static` storage class and `inline`. "[T]he ... otherwise perfectly logical combination of external linkage and inlining is banned to make life simpler for compiler writers[.]" (_The C++ Programming Language, Special Edition_, Stroustrup, 1997, p. 199) And rename this... (resolve_current_font_to_mounting_position): ...to this. Annotate purpose. (device_extension_node::device_extension_node) (make_composite_node) (make_glyph_node) (env_space_width) (env_sentence_space_width) (env_half_narrow_space_width) (env_narrow_space_width): Update call sites. 2026-03-07 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Boolify `psbb_locator::skip_to_trailer()`. Its lone call site tests only for a positive return value and discards it. (class psbb_locator): Demote return type from `int` to `bool` in declaration. (psbb_locator::psbb_locator): Simplify test of return value. (psbb_locator::skip_to_trailer): Demote return type from `int` to `bool` in definition. Return Boolean rather than integer literals or the otherwise unused `status` local value. 2026-03-07 G. Branden Robinson <g.branden.robinson@gmail.com> [troff]: Make warning diagnostic functions return `void`. Change return type of `warning()` and `output_warning()` functions from `int` to `void`. I would have boolified them, but nothing ever checks their return values. * src/roff/troff/troff.h: Update declarations. * src/roff/troff/input.cpp (warning, output_warning): Update definitions. Simplify function bodies. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp: Rename several warning-relatd data for clarity. Rename preprocessor macro `DEFAULT_WARNING_MASK` to `DEFAULT_WARNING_CATEGORY_SET`. Rename file-scoped variable `warning_mask` to `desired_warnings` (it's a _set_ type, not a "mask", which is more specific). (enable_warning): (disable_warning): (warning): (output_warning): Update site of dereference. (init_input_requests) <.warn>: Update wire-uppery. (do_expr_test): Rename local variable `saved_warning_mask` to `saved_desired_warnings`. (enable_warning, disable_warning): Rename local variable `mask` to `category`. (set_warning_mask_request): Rename this... (configure_desired_warnings_request): ...to this. (init_input_requests) <warn>: Update wire-uppery. (struct warning_category): Rename `mask` member to `set`. (lookup_warning): Track rename of structure member. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/input.cpp (do_expr_test) (string_iterator::fill): Fix code style nit: assign to variable of unsigned integral type with explicitly unsigned integer literal. 2026-03-06 G. Branden Robinson <g.branden.robinson@gmail.com> * src/roff/troff/node.cpp: Fix typoed variable name; rename `is_output_supressed` to `is_output_suppressed`. (glyph_node::asciify): (kern_pair_node::asciify): (dbreak_node::asciify): (ligrature_node::asciify): (break_char_node::asciify): (italic_corrected_node::asciify): (left_italic_corrected_node::asciify): (space_char_hmotion_node::asciify): (word_space_node::asciify): (unbreakable_space_node::asciify): (suppress_node::asciify): (zero_width_node::asciify): (composite_node::asciify): Update sites of dereference. Fixes <https://savannah.gnu.org/bugs/?68071>. 2026-03-07 G. Branden Robinson <g.branden.robinson@gmail.com> * Makefile.am (.version, dist-hook): Use `AM_V_GEN` macro in production of targets, to make them less garish in builds without `V=1` specified as a make(1) argument. ________________________________________________________________________ ##### License Copyright 2026 G. Branden Robinson Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. ##### Editor settings Local Variables: fill-column: 72 mode: change-log version-control: never End: vim:set autoindent textwidth=72: