]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
stty: arbitrary or non-a priori known speed_t support
authorH. Peter Anvin <hpa@zytor.com>
Mon, 16 Jun 2025 21:58:01 +0000 (14:58 -0700)
committerPádraig Brady <P@draigBrady.com>
Wed, 18 Jun 2025 13:20:17 +0000 (14:20 +0100)
Support the case where speed_t is simply a number, and in that case
assume that arbitrary values can be passed.  This is assumed to be the
case when all known speed_t macros equal their own value.

Try to probe for a variety of speed_t constants by trying to coax
$(CC) into emitting macro definitions (-E -dM).  If this is not
supported, use a fairly extensive list of constants as a
fallback.  This both improves the test for arbitrary speed support, as
well as allowing proper operation in the case where the constants are
not plain numbers and allows for handing enumerated speed constants
that were not known a priori when the source code was written.

A simple shell script (mostly using sed) is used to turn the list of
constants (probed and predefined) into a pair of conversion functions,
baud_to_value() and value_to_baud(); string_to_baud() is then
reimplemented as a wrapper around the latter.

* src/.gitignore: Add generated speedlist.h.
* src/local.mk: Generate speedlist.h.
* src/speedgen: Portable shell script to generate speedlist.h.
* src/stty.c: Adjust string_to_baud to
convert from arbitrary numeric values.
* src/termios.c: A helper used when generating speedlist.h
* NEWS: Mention the improvement.

NEWS
src/.gitignore
src/local.mk
src/speedgen [new file with mode: 0755]
src/stty.c
src/termios.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 1f498269f89ca110428a68aa25cf47536441126e..8f9ac26d06bc85d7092bbb84ad5263a65e7e0f93 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,13 @@ GNU coreutils NEWS                                    -*- outline -*-
   'sort +0.18446744073709551615R input' on 64 bit systems.
   [bug introduced in coreutils-7.2]
 
+** Improvements
+
+  stty supports setting arbitrary baud rates on supported systems,
+  like Hurd, Linux with glibc >= 2.42, and some BSDs.
+  Also on other systems the full set of supported baud rates
+  are determined at build time.
+
 
 * Noteworthy changes in release 9.7 (2025-04-09) [stable]
 
index 55f9660c62d8e87568315048998ce02b62bcf8bf..2fffa349be21ef74043f5b538fa1670706ba6101 100644 (file)
@@ -87,6 +87,7 @@ shred
 shuf
 sleep
 sort
+speedlist.h
 split
 stat
 stdbuf
index 57692c0ce0ae7ca6471c302e16d94a6464f47a74..45dffd8bf624a326017050c88301fc98e4fc390c 100644 (file)
@@ -72,7 +72,8 @@ EXTRA_DIST +=         \
   src/primes.h         \
   src/crctab.c         \
   src/tac-pipe.c       \
-  src/extract-magic
+  src/extract-magic    \
+  src/speedgen
 
 CLEANFILES += $(SCRIPTS)
 
@@ -693,6 +694,20 @@ src/version.h: Makefile
        $(AM_V_at)chmod a-w $@t
        $(AM_V_at)mv $@t $@
 
+# Target-specific termios baud rate file. This is opportunistic;
+# if cc -E doesn't support -dM, the speedgen script still includes
+# an extensive fallback list of common constants.
+CLEANFILES += src/speedlist.h
+src/speedlist.h: src/termios.c lib/config.h src/speedgen
+       $(AM_V_GEN)rm -f $@
+       $(AM_V_at)${MKDIR_P} src
+       $(AM_V_at)$(COMPILE) -E -dM $< 2>/dev/null |            \
+                 $(SHELL) $(srcdir)/src/speedgen $@t
+       $(AM_V_at)chmod a-w $@t
+       $(AM_V_at)mv $@t $@
+
+src/stty.$(OBJEXT): src/speedlist.h
+
 # Generates a list of macro invocations like:
 #   SINGLE_BINARY_PROGRAM(program_name_str, main_name)
 # once for each program list on $(single_binary_progs). Note that
diff --git a/src/speedgen b/src/speedgen
new file mode 100755 (executable)
index 0000000..f1647d9
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh -e
+
+out="$1"
+tmp="$out.tmp"
+
+if [ -z "$out" ]; then
+    echo "Usage: $0 outfile" 2>&1
+    exit 1
+fi
+
+s='[[:space:]]'                        # For brevity's sake
+
+trap "rm -f '$tmp'" EXIT
+trap "rm -f '$tmp' '$out'" ERR HUP INT QUIT TERM
+
+# Fallback list of speeds that are always tested for
+defspeeds="0 50 75 110 134 150 200 300 600 1200 1800 2400 4800 7200 9600 \
+14400 19200 28800 33600 38400 57600 76800 115200 153600 230400 307200 \
+460800 500000 576000 614400 921600 1000000 1152000 1500000 \
+2000000 2500000 3000000 3500000 4000000 5000000 10000000"
+(
+    sed -n -e "s/^$s*\#$s*define$s$s*B\\([1-9][0-9]*\\)$s.*\$/\\1/p"
+    for s in $defspeeds; do echo "$s"; done
+) | sort -n | uniq > "$tmp"
+
+cat > "$out" <<'EOF'
+#ifndef SPEEDLIST_H
+# define SPEEDLIST_H 1
+
+# if 1 \
+EOF
+
+sed -e 's/^.*$/ \&\& (!defined(B&) || B& == &) \\/' < "$tmp" >> "$out"
+
+cat >> "$out" <<'EOF'
+
+#  define TERMIOS_SPEED_T_SANE 1
+
+# endif
+
+ATTRIBUTE_CONST
+static unsigned long int
+baud_to_value (speed_t speed)
+{
+# ifdef TERMIOS_SPEED_T_SANE
+  return speed;
+# else
+  switch (speed)
+    {
+EOF
+
+sed -e 's/^.*$/#  ifdef B&\n      case B&: return &;\n#  endif/' \
+    < "$tmp" >> "$out"
+
+cat >> "$out" <<'EOF'
+      default: return -1;
+    }
+# endif
+}
+
+ATTRIBUTE_CONST
+static speed_t
+value_to_baud (unsigned long int value)
+{
+# ifdef TERMIOS_SPEED_T_SANE
+  speed_t speed = value;
+  if (speed != value)
+    speed = (speed_t) -1;      /* Unrepresentable (overflow?) */
+  return speed;
+# else
+  switch (value)
+    {
+EOF
+
+sed -e 's/^.*$/#  ifdef B&\n      case &: return B&;\n#  endif/' \
+    < "$tmp" >> "$out"
+
+cat >> "$out" <<'EOF'
+      default: return (speed_t) -1;
+    }
+# endif
+}
+
+#endif
+EOF
index 133b33cfc312d0ec9d9703cc70d84a4d2fbf913b..561de1c1a05ff4f4b6bda188d6c2ce5604a14e2b 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "system.h"
 #include "assure.h"
+#include "c-ctype.h"
 #include "fd-reopen.h"
 #include "quote.h"
 #include "xdectoint.h"
@@ -2172,100 +2173,66 @@ recover_mode (char const *arg, struct termios *mode)
   return true;
 }
 
-struct speed_map
-{
-  char const *string;          /* ASCII representation. */
-  speed_t speed;               /* Internal form. */
-  unsigned long int value;     /* Numeric value. */
-};
-
-static struct speed_map const speeds[] =
-{
-  {"0", B0, 0},
-  {"50", B50, 50},
-  {"75", B75, 75},
-  {"110", B110, 110},
-  {"134", B134, 134},
-  {"134.5", B134, 134},
-  {"150", B150, 150},
-  {"200", B200, 200},
-  {"300", B300, 300},
-  {"600", B600, 600},
-  {"1200", B1200, 1200},
-  {"1800", B1800, 1800},
-  {"2400", B2400, 2400},
-  {"4800", B4800, 4800},
-  {"9600", B9600, 9600},
-  {"19200", B19200, 19200},
-  {"38400", B38400, 38400},
-  {"exta", B19200, 19200},
-  {"extb", B38400, 38400},
-#ifdef B57600
-  {"57600", B57600, 57600},
-#endif
-#ifdef B115200
-  {"115200", B115200, 115200},
-#endif
-#ifdef B230400
-  {"230400", B230400, 230400},
-#endif
-#ifdef B460800
-  {"460800", B460800, 460800},
-#endif
-#ifdef B500000
-  {"500000", B500000, 500000},
-#endif
-#ifdef B576000
-  {"576000", B576000, 576000},
-#endif
-#ifdef B921600
-  {"921600", B921600, 921600},
-#endif
-#ifdef B1000000
-  {"1000000", B1000000, 1000000},
-#endif
-#ifdef B1152000
-  {"1152000", B1152000, 1152000},
-#endif
-#ifdef B1500000
-  {"1500000", B1500000, 1500000},
-#endif
-#ifdef B2000000
-  {"2000000", B2000000, 2000000},
-#endif
-#ifdef B2500000
-  {"2500000", B2500000, 2500000},
-#endif
-#ifdef B3000000
-  {"3000000", B3000000, 3000000},
-#endif
-#ifdef B3500000
-  {"3500000", B3500000, 3500000},
-#endif
-#ifdef B4000000
-  {"4000000", B4000000, 4000000},
-#endif
-  {nullptr, 0, 0}
-};
+/* Autogenerated conversion functions to/from speed_t */
+#include "speedlist.h"
 
 ATTRIBUTE_PURE
 static speed_t
 string_to_baud (char const *arg)
 {
-  for (int i = 0; speeds[i].string != nullptr; ++i)
-    if (STREQ (arg, speeds[i].string))
-      return speeds[i].speed;
-  return (speed_t) -1;
-}
+  char *ep;
+  unsigned long value;
+  unsigned char c;
 
-ATTRIBUTE_PURE
-static unsigned long int
-baud_to_value (speed_t speed)
-{
-  for (int i = 0; speeds[i].string != nullptr; ++i)
-    if (speed == speeds[i].speed)
-      return speeds[i].value;
-  return 0;
+  /* Explicitly disallow negative numbers.  */
+  while (c_isspace (*arg))
+    arg++;
+  if (*arg == '-')
+    return (speed_t) -1;
+
+  value = strtoul (arg, &ep, 10);
+
+  c = *ep++;
+  if (c == '.')
+    {
+      /* Number includes a fraction. Round it to nearest-even.
+         Note in particular that 134.5 must round to 134! */
+      c = *ep++;
+      if (c)
+        {
+          c -= '0';
+          if (c > 9)
+            {
+              return (speed_t) -1; /* Garbage after otherwise valid number */
+            }
+          else if (c > 5)
+            {
+              value++;
+            }
+          else if (c == 5)
+            {
+              while ((c = *ep++) == '0')
+              ; /* Skip zeroes after .5 */
+
+              if (c >= '1' && c <= '9')
+                value++;                /* Nonzero digit, round up */
+              else
+                value += (value & 1);   /* Exactly in the middle, round even */
+            }
+        }
+    }
+  else if (c)
+    {
+      /* Not a valid number; check for legacy aliases "exta" and "extb" */
+      if (STREQ (arg, "exta"))
+        return B19200;
+      else if (STREQ (arg, "extb"))
+        return B38400;
+      else
+        return (speed_t) -1;
+    }
+
+  return value_to_baud (value);
 }
 
 static void
diff --git a/src/termios.c b/src/termios.c
new file mode 100644 (file)
index 0000000..f17e12e
--- /dev/null
@@ -0,0 +1,34 @@
+/* termios.c -- coax out Bxxx macros from termios.h
+
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* This simply #includes headers which may or may not provide Bxxx
+   constant macros.  This is run through the C preprocessor and defined
+   macros are extracted.
+
+   In the case where the C preprocessor isn't capable of doing so,
+   the script this is fed through contains a pre-defined set of common
+   constants. */
+
+#include <config.h>
+
+#ifdef TERMIOS_NEEDS_XOPEN_SOURCE
+# define _XOPEN_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <termios.h>
+#include <sys/ioctl.h>