diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index af533531..5af2f7aa 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -127,6 +127,7 @@ jobs:
exit 1
fi
fi
+ ./sources/$binname -vv
- name: Upload binary as artifact
if: steps.check-portability.outcome == 'success' && steps.check-portability.conclusion == 'success'
diff --git a/doc/form.1 b/doc/form.1
index 26f0b620..b62c3d17 100644
--- a/doc/form.1
+++ b/doc/form.1
@@ -96,6 +96,9 @@ the program.
.BR "-v"
Only the version will be printed. The program terminates immediately after it.
.TP
+.BR "-vv"
+Same as \fB-v\fR, but additionally prints a list indicating which features are supported.
+.TP
.BR "-w"
This should be followed immediately by a number without any space. The number
indicates the number of worker threads for \fBtform\fR. All other versions of
diff --git a/doc/manual/startup.tex b/doc/manual/startup.tex
index cf9765e1..4b56d0d4 100644
--- a/doc/manual/startup.tex
+++ b/doc/manual/startup.tex
@@ -62,6 +62,8 @@ \chapter{Running FORM}
information see the "On TotalSize;" statement~\ref{ontotalsize}.
\item[-v] Only the version will be printed. The program terminates
immediately after it.
+\item[-vv] Same as -v, but additionally prints a list indicating
+ which features are supported.
\item[-w] This should be followed immediately by a number. The
number indicates the number of worker threads for \TFORM\@. All other
versions of \FORM\ ignore this parameter. It should be noted that \TFORM\
diff --git a/sources/Makefile.am b/sources/Makefile.am
index 01ea914c..ec0e5cfe 100644
--- a/sources/Makefile.am
+++ b/sources/Makefile.am
@@ -15,6 +15,7 @@ SRCBASE = \
execute.c \
extcmd.c \
factor.c \
+ features.cc \
findpat.c \
form3.h \
fsizes.h \
diff --git a/sources/declare.h b/sources/declare.h
index cc0f2c2e..119f1f90 100644
--- a/sources/declare.h
+++ b/sources/declare.h
@@ -671,6 +671,7 @@ extern int Product(UWORD *,WORD *,WORD);
extern void PrtLong(UWORD *,WORD,UBYTE *);
extern void PrtTerms(void);
extern void PrintDeprecation(const char *,const char *);
+extern void PrintFeatureList(void);
extern void PrintRunningTime(void);
extern LONG GetRunningTime(void);
extern int PutBracket(PHEAD WORD *);
diff --git a/sources/features.cc b/sources/features.cc
new file mode 100644
index 00000000..a24f554d
--- /dev/null
+++ b/sources/features.cc
@@ -0,0 +1,212 @@
+/** @file features.cc
+ *
+ * Utility code for printing available features.
+ */
+/* #[ License : */
+/*
+ * Copyright (C) 1984-2026 J.A.M. Vermaseren
+ * When using this file you are requested to refer to the publication
+ * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
+ * This is considered a matter of courtesy as the development was paid
+ * for by FOM the Dutch physics granting agency and we would like to
+ * be able to track its scientific use to convince FOM of its value
+ * for the community.
+ *
+ * This file is part of FORM.
+ *
+ * FORM is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FORM. If not, see .
+ */
+/* #] License : */
+// #[ Includes :
+
+extern "C" {
+#include "form3.h"
+}
+
+#include
+#include
+#include
+
+#ifdef WITHFLINT
+#include
+#endif
+
+#ifdef WITHGMP
+#include
+#endif
+
+#ifdef WITHMPFR
+#include
+#endif
+
+#ifdef WITHZLIB
+#include
+#endif
+
+#ifdef WITHZSTD
+#include
+#endif
+
+// #] Includes :
+// #[ PrintFeatureList :
+
+/**
+ * Prints a list of strings in multiple columns to fit within the line length.
+ *
+ * @param lines List of strings to print.
+ */
+static void PrintMulticolumn(const std::vector& lines)
+{
+ const std::size_t n_lines = lines.size();
+
+ if ( n_lines == 0 ) return;
+
+ const std::size_t line_len = AC.LineLength > 0 ? AC.LineLength : 79;
+ constexpr std::size_t max_cols = 6;
+ constexpr std::size_t col_spacing = 2;
+
+ std::vector col_lens(max_cols);
+
+ for ( std::size_t n_cols = max_cols; n_cols >= 2; n_cols-- ) {
+ const std::size_t n_rows = (n_lines - 1) / n_cols + 1;
+
+ // Check whether `n_cols` columns fit within the line length.
+ // Columns are filled top-to-bottom, then left-to-right.
+
+ std::fill(col_lens.begin(),col_lens.begin()+n_cols,0);
+
+ std::size_t total_len = 0;
+ for ( std::size_t j = 0; j < n_cols; j++ ) {
+ for ( std::size_t i = 0; i < n_rows; i++ ) {
+ const std::size_t k = i + j * n_rows;
+ if ( k >= n_lines ) break;
+ col_lens[j] = std::max(col_lens[j],lines[k].size());
+ }
+ total_len += col_lens[j];
+ }
+ total_len += (n_cols - 1) * col_spacing;
+
+ if ( total_len > line_len ) continue;
+
+ // Output using `n_cols` columns.
+
+ std::string line;
+ line.reserve(line_len);
+ for ( std::size_t i = 0; i < n_rows; i++ ) {
+ line.clear();
+ for ( std::size_t j = 0; j < n_cols; j++ ) {
+ const std::size_t k = i + j * n_rows;
+ if ( k >= n_lines ) break;
+ line += lines[k];
+ if ( j < n_cols - 1 ) {
+ line += std::string(col_lens[j]-lines[k].size()+col_spacing,' ');
+ }
+ }
+ MesPrint("%s",line.c_str());
+ }
+ return;
+ }
+
+ // Fallback to a single column.
+
+ for ( const auto& f : lines ) {
+ MesPrint("%s",f.c_str());
+ }
+}
+
+/**
+ * Prints the list of available features.
+ */
+void PrintFeatureList(void)
+{
+ std::vector feature_list = {
+
+#ifdef ENABLE_BACKTRACE
+ "+backtrace",
+#else
+ "-backtrace",
+#endif
+
+#ifdef DEBUGGING
+ "+debugging",
+#else
+ "-debugging",
+#endif
+
+#ifdef WITHFLINT
+ "+flint=" + std::string(flint_version),
+#else
+ "-flint",
+#endif
+
+#ifdef WITHFLOAT
+ "+float",
+#else
+ "-float",
+#endif
+
+#ifdef WITHGMP
+ "+gmp=" + std::string(gmp_version),
+#else
+ "-gmp",
+#endif
+
+#ifdef WITHMPFR
+ "+mpfr=" + std::string(mpfr_get_version()),
+#else
+ "-mpfr",
+#endif
+
+#ifdef WITHMPI
+ "+mpi",
+#else
+ "-mpi",
+#endif
+
+#ifdef UNIX
+ "+posix",
+#else
+ "-posix",
+#endif
+
+#ifdef WITHPTHREADS
+ "+pthreads",
+#else
+ "-pthreads",
+#endif
+
+#ifdef WINDOWS
+ "+windows",
+#else
+ "-windows",
+#endif
+
+#ifdef WITHZLIB
+ "+zlib=" + std::string(zlibVersion()),
+#else
+ "-zlib",
+#endif
+
+#ifdef WITHZSTD
+ "+zstd=" + std::string(ZSTD_versionString()),
+#else
+ "-zstd",
+#endif
+
+ };
+
+ PrintMulticolumn(feature_list);
+}
+
+// #] PrintFeatureList :
diff --git a/sources/startup.c b/sources/startup.c
index db5f0da5..7f4a2a72 100644
--- a/sources/startup.c
+++ b/sources/startup.c
@@ -109,9 +109,12 @@
/**
* Prints the header line of the output.
*
- * @param with_full_info True for printing also runtime information.
+ * @param par Controls the output mode
+ * (0: default,
+ * 1: including runtime information,
+ * 2: including the feature list).
*/
-static void PrintHeader(int with_full_info)
+static void PrintHeader(int par)
{
#ifdef WITHMPI
if ( PF.me == MASTER && !AM.silent ) {
@@ -147,7 +150,7 @@ static void PrintHeader(int with_full_info)
s += snprintf(s,250-(s-buffer1)," %d-bits",(WORD)(sizeof(WORD)*16));
*s = 0;
*/
- if ( with_full_info ) {
+ if ( par == 1 ) {
#if defined(WITHPTHREADS) || defined(WITHMPI)
#if defined(WITHPTHREADS)
int nworkers = AM.totalnumberofthreads-1;
@@ -200,6 +203,10 @@ static void PrintHeader(int with_full_info)
MesPrint("%s",buffer1);
AC.LineLength = oldLineLength;
}
+
+ if ( par == 2 ) {
+ PrintFeatureList();
+ }
}
#ifdef WINDOWS
PrintDeprecation("the native Windows version", "issues/623");
@@ -414,12 +421,13 @@ int DoTail(int argc, UBYTE **argv)
break;
case 'T': /* Print the total size used at end of job */
AM.PrintTotalSize = 1; break;
- case 'v':
+ case 'v': /* Print version information */
+ if ( s[1] == 'v' ) {
+ PrintHeader(2);
+ return(1);
+ }
printversion:;
-#ifdef WITHMPI
- if ( PF.me == MASTER )
-#endif
- PrintHeader(0);
+ PrintHeader(0);
if ( onlyversion ) return(1);
goto NoFile;
case 'y': /* Preprocessor dumps output. No compilation. */