]> git.draconx.ca Git - cdecl99.git/commitdiff
libcdecl: Replace uintmax_t with unsigned (long) long.
authorNick Bowler <nbowler@draconx.ca>
Thu, 18 Jan 2024 05:17:06 +0000 (00:17 -0500)
committerNick Bowler <nbowler@draconx.ca>
Fri, 19 Jan 2024 01:39:21 +0000 (20:39 -0500)
Using uintmax_t in a public header file is problematic.  The most
obvious problem is that we need to include either <inttypes.h> or
<stdint.h> to get it, and the current unconditional inclusion of
<stdint.h> breaks on e.g., HP-UX 11 which only has <inttypes.h>

The less obvious problem is that uintmax_t can depend on compiler
configuration, e.g., the Sun compiler for a 32-bit host provides a
32-bit uintmax_t in C89 mode.

Instead, just change it to "unsigned long long" if supported, otherwise
"unsigned long".  It is very likely to be binary compatible with prior
releases, since as far as I am aware prior releases don't actually build
on any implementation where it would not be the case.

19 files changed:
Makefile.am
common
configure.ac
doc/libcdecl.3
src/cdecl-internal.h
src/cdecl.h
src/cdecl99.c
src/declare.c
src/error.c
src/execute.gperf
src/fix-yytname.awk
src/intconv.h
src/output.c
src/parse.y
src/scan.l
t/declgen.c
t/rng.c
t/test.h
t/testlib.c

index 1aa6a5671a2124f4d2cd7b89350dbdb63994b817..cb50bfc3d5e95ead2c612ccdb14dab58100fe7d8 100644 (file)
@@ -39,7 +39,7 @@ noinst_DATA = $(MOFILES)
 
 nodist_include_HEADERS = src/config/cdecl.h
 src/config/cdecl.h: config.h src/cdecl.h $(DX_BASEDIR)/scripts/bake-config.awk
-       $(AM_V_at) $(AWK) -f $(DX_BASEDIR)/scripts/bake-config.awk \
+       $(AM_V_GEN) $(AWK) -f $(DX_BASEDIR)/scripts/bake-config.awk \
          config.h $(srcdir)/src/cdecl.h >$@-t
        $(AM_V_at) mv -f $@-t $@
 EXTRA_DIST += $(DX_BASEDIR)/scripts/bake-config.awk
diff --git a/common b/common
index aaaacebf1f04c364803f308c3b3e9670e216da09..2f638e51e602982648087e00c788a4751f1a4a8c 160000 (submodule)
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit aaaacebf1f04c364803f308c3b3e9670e216da09
+Subproject commit 2f638e51e602982648087e00c788a4751f1a4a8c
index 435eaf9b004482c3e232ac081642ac7ae1859d24..62715e8b899d5a92271a58b1a6c196ff086d6ffa 100644 (file)
@@ -28,6 +28,7 @@ AM_PROG_CC_C_O
 gl_EARLY
 
 AC_HEADER_ASSERT
+AC_TYPE_UNSIGNED_LONG_LONG_INT
 AC_C_FLEXIBLE_ARRAY_MEMBER
 AC_C_INLINE
 DX_C_FOR_DECLARATIONS
index 4a882a0b1e474213c7526737b2d7bc1cee0f8772..725566b2944fa8bbfcf2b9dc02460b820e4a8672 100644 (file)
@@ -1,4 +1,4 @@
-.Dd December 3, 2023
+.Dd January 17, 2024
 .Dt LIBCDECL \&3 "Cdecl99 Developer's Manual"
 .Os cdecl99
 .Sh NAME
@@ -29,8 +29,7 @@ see the
 manual page.
 .Pp
 .Nm
-is intended to be portable to any system with a working C implementation that
-at least makes an effort to support C99.
+is intended to be portable to any system with a standard C compiler.
 The library is thread-safe when appropriate facilities exist and are enabled at
 build time.
 .Sh NAMESPACE
@@ -248,9 +247,10 @@ member is non-null, then it points to the first element of a singly-linked list
 of type qualifiers.
 .Ss Array Declarators
 .Bd -literal -offset indent
+typedef unsigned long long cdecl_uintmax; /* depends on configuration */
 struct cdecl_array {
        char *vla;
-       uintmax_t length;
+       cdecl_uintmax length;
 };
 .Ed
 .Pp
@@ -265,6 +265,19 @@ Otherwise, if
 .Va length
 is positive, then this is an array declarator with the specified length.
 Otherwise, this is an incomplete array declarator.
+.Pp
+If the library was configured using a compiler which does not support
+.Vt unsigned long long ,
+then the
+.Vt cdecl_uintmax
+type is defined as
+.Vt unsigned long
+instead.
+Prior versions of
+.Nm
+used
+.Vt uintmax_t
+directly.
 .Ss Function Declarators
 .Bd -literal -offset indent
 typedef _Bool cdecl_bool; /* depends on configuration */
@@ -439,7 +452,7 @@ is non-zero, the resulting string is '\\0' terminated even if it was truncated.
 .Sh AUTHORS
 Nick Bowler <nbowler@draconx.ca>
 .Sh COPYRIGHT
-Copyright \(co 2011\(en2012, 2021, 2023 Nick Bowler
+Copyright \(co 2011\(en2012, 2021, 2023\(en2024 Nick Bowler
 .Pp
 Permission is granted to copy, distribute and/or modify this manual under the
 terms of the GNU General Public License as published by the Free Software
index b586dd45e5b00b7c08d2da83b211cce8f3556293..dc8d1f0642780d80b7d678ad40cffa38b61acb90 100644 (file)
@@ -83,7 +83,7 @@ struct output_state {
 
 size_t cdecl__advance(struct output_state *dst, size_t amount);
 size_t cdecl__emit(struct output_state *dst, const char *src);
-size_t cdecl__emit_uint(struct output_state *dst, uintmax_t val);
+size_t cdecl__emit_uint(struct output_state *dst, cdecl_uintmax val);
 size_t cdecl__strlcpy(char *dst, const char *src, size_t len);
 
 const char *cdecl__emit_specs(struct output_state *dst,
index 63034f4bf11d946bbb9fb98bb66ffc7f92a66343..952b0aab6020a6b4a5bc68cf6b12356d2bc77026 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011, 2021, 2023 Nick Bowler
+ * Copyright © 2011, 2021, 2023-2024 Nick Bowler
  *
  * 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
@@ -18,8 +18,7 @@
 #ifndef CDECL_H_
 #define CDECL_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <stddef.h> /* for size_t */
 
 #if __GNUC__
 #  define CDECL__INLINE __inline
@@ -34,6 +33,12 @@ typedef _Bool cdecl_bool;
 typedef signed char cdecl_bool;
 #endif
 
+#if HAVE_UNSIGNED_LONG_LONG_INT
+typedef unsigned long long cdecl_uintmax;
+#else
+typedef unsigned long cdecl_uintmax;
+#endif
+
 /* Declaration specifier kinds. */
 enum {
        CDECL_SPEC_TYPE = 256,
@@ -98,7 +103,7 @@ struct cdecl {
                        } pointer;
                        struct cdecl_array {
                                char *vla;
-                               uintmax_t length;
+                               cdecl_uintmax length;
                        } array;
                        struct cdecl_function {
                                struct cdecl *parameters;
index 3eb7cc1b4c99ef2815eb11d3360c8a119b0166a9..1d8dff0b840a3d07a596e91cc0aeedbe38f3424b 100644 (file)
@@ -26,6 +26,7 @@
 #include <locale.h>
 #include <assert.h>
 #include <stdarg.h>
+#include <inttypes.h>
 
 #include <getopt.h>
 #include <gettext.h>
index 5c11381d150b92ad20ccfc3f93d4a4d0a56ac8f9..c5a1f236953a887a6d056b9dcf22f4b4e805fce2 100644 (file)
@@ -19,7 +19,6 @@
 #include <config.h>
 #include <stdio.h>
 #include <stdbool.h>
-#include <inttypes.h>
 #include <assert.h>
 
 #include "cdecl.h"
index a2d705a8d15885f8e8d20986c5f99098a48bb786..94987af6a90f3a7eb83ce661a4d87b3c75efb7a8 100644 (file)
@@ -180,7 +180,7 @@ void cdecl__err(const char *fmt, const char *arg)
 retry:
        rc = fmt_err(state, fmt, arg);
        if (rc >= state->nstr) {
-               assert(try++ == 0 && rc < SIZE_MAX / 4);
+               assert(try++ == 0 && rc < (size_t)-1 / 4);
 
                state = alloc_err_state(state, (size_t)(rc+1u) * 3 / 2);
                if (!state)
index a45413d69f7466019c87e8793e04fb5114d1eb9b..51443ba0b31b46f10be9a395b75f7d2585821f8f 100644 (file)
@@ -1,6 +1,6 @@
 %{
 /*
- * Copyright © 2021, 2023 Nick Bowler
+ * Copyright © 2021, 2023-2024 Nick Bowler
  *
  * 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
@@ -19,7 +19,7 @@
 #include <config.h>
 #include <stdio.h>
 #include <string.h>
-#include <stdint.h>
+#include <inttypes.h>
 #include <assert.h>
 
 #include "cdecl99.h"
index 8a417b32c2d20d3a46adb775574b46b593a35392..4f46871c19c93ccd7f99dd5690dee1ea081fa370 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/awk -f
 #
-# Copyright © 2023 Nick Bowler
+# Copyright © 2023-2024 Nick Bowler
 #
 # Hackjob to improve the horrible yytname array generated by Bison.
 #
@@ -95,7 +95,7 @@ in_table {
 # At the end of the yytname definition, output our replacement function.
 in_table && $0 ~ /^};/ {
   print "#if !defined(UINT_LEAST8_MAX) || !defined(UINT_LEAST16_MAX)";
-  print "#  include <stdint.h>";
+  print "#  include <inttypes.h>";
   print "#endif";
   print "#ifndef assert";
   print "#  include <assert.h>";
index 17644f2cdc70a0745c0f9d5ccdd018b67dfcda6a..6cc7819bb67fd02288778b9450736a06cbe51358 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2023 Nick Bowler
+ * Copyright © 2023-2024 Nick Bowler
  *
  * 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
@@ -20,7 +20,6 @@
 
 #include <string.h>
 #include <stdbool.h>
-#include <inttypes.h>
 
 enum { INTCONV_OCTAL = 8, INTCONV_DECIMAL = 10, INTCONV_HEXADECIMAL = 16 };
 
@@ -28,14 +27,14 @@ enum { INTCONV_OCTAL = 8, INTCONV_DECIMAL = 10, INTCONV_HEXADECIMAL = 16 };
  * Multiply *v by base, which must be one of the above enumeration constants,
  * and add digit, updating *v with the result.
  *
- * If the result does not fit in uintmax_t, then 0 is returned.  Otherwise,
+ * If the result does not fit in cdecl_uintmax, then 0 is returned.  Otherwise,
  * a non-zero result is returned.
  */
-static inline bool intconv_shift(uintmax_t *v, unsigned base, unsigned digit)
+static inline bool intconv_shift(cdecl_uintmax *v, unsigned base, unsigned digit)
 {
-       uintmax_t old_v = *v;
+       cdecl_uintmax old_v = *v;
 
-       if (old_v > (uintmax_t)-1 / base)
+       if (old_v > (cdecl_uintmax)-1 / base)
                return false;
        old_v *= base;
 
index c0438e602753fb0b37c4bdaeaba0be457481b5da..634b79e2906a622dddb7bc595d9f8cdaed2d88f6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Helper functions for outputting text.
- *  Copyright © 2011, 2021, 2023 Nick Bowler
+ *  Copyright © 2011, 2021, 2023-2024 Nick Bowler
  *
  *  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
@@ -17,6 +17,7 @@
  */
 #include <config.h>
 #include <stdio.h>
+#include <limits.h>
 #include <assert.h>
 
 #include "cdecl.h"
@@ -55,29 +56,15 @@ size_t cdecl__emit(struct output_state *dst, const char *src)
        return cdecl__advance(dst, rc);
 }
 
-/*
- * 31 decimal digits is enough for values up to 2^102 - 1.
- * 63 decimal digits is enough for values up to 2^209 - 1.
- *
- * We can't portably write numbers this large in preprocessor conditionals,
- * but since the maximum values of unsigned integer types are always one
- * less than a power of two, we can use a sequence of small shifts to infer
- * the bounds.
- *
- * All known implementations have 64-bit uintmax_t.  Leave some headroom
- * to support a possible future implementatons with 128-bit uintmax_t.
- */
 enum {
-#if (UINTMAX_MAX >> 27 >> 27 >> 26 >> 26) == 0
-       MAX_UINT_DIGITS = 31
-#elif (UINTMAX_MAX >> 27 >> 26 >> 26 >> 26 >> 26 >> 26 >> 26 >> 26) == 0
-       MAX_UINT_DIGITS = 63
-#else
-#      error UINTMAX_MAX is too large, please report a bug.
-#endif
+       /*
+        * upper bound on number of decimal digits required to convert
+        * cdecl_uintmax.
+        */
+       MAX_UINT_DIGITS = (CHAR_BIT * sizeof (cdecl_uintmax) + 2)/3
 };
 
-size_t cdecl__emit_uint(struct output_state *dst, uintmax_t val)
+size_t cdecl__emit_uint(struct output_state *dst, cdecl_uintmax val)
 {
        char buf[MAX_UINT_DIGITS + 1], *p = &buf[sizeof buf];
 
index 8abc21a7b0450c28aa223d62359ba9805dd944d3..80801c3b7d041c1e94952fa9b6123e23f221ff43 100644 (file)
@@ -32,7 +32,6 @@
 #include <stdbool.h>
 
 #include "scan.h"
-#include "cdecl.h"
 #include "cdecl-internal.h"
 #include "errmsg.h"
 
@@ -120,6 +119,7 @@ static size_t yytnamerr_copy(char *dst, const char *src)
 %code requires {
 #include <inttypes.h>
 #include <stdbool.h>
+#include "cdecl.h"
 }
 
 %code provides {
@@ -129,7 +129,7 @@ const char *cdecl__token_name(unsigned token);
 }
 
 %union {
-       uintmax_t uintval;
+       cdecl_uintmax uintval;
        unsigned spectype;
        bool boolval;
        struct cdecl_declspec *declspec;
index f7e1b03eb158f44c3dbbc83ea950d4aedf02540c..5d460ed0366dd15ca3c20c6e9ac4ec21c74b6e95 100644 (file)
@@ -183,7 +183,7 @@ IDENT [_[:alpha:]][-_[:alnum:]]*
 [1-9][0-9]* { intconv_base = INTCONV_DECIMAL; goto int_parse; }
 0[Xx][[:xdigit:]]+ {
        unsigned char d;
-       uintmax_t v;
+       cdecl_uintmax v;
 
        yytext += 2;
        intconv_base = INTCONV_HEXADECIMAL;
index 630ab907a66532ab5d3342526f5ce2f1a199a30d..9824c40ba8c1fb33a754a872f69b9ab6d75c28fa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Generate random C declarations for testing.
- *  Copyright © 2012, 2021-2023 Nick Bowler
+ *  Copyright © 2012, 2021-2024 Nick Bowler
  *
  *  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
@@ -288,10 +288,10 @@ gen_declspecs(struct test_rng *rng, unsigned flags)
        return gen_randomize_specs(rng, s);
 }
 
-static uintmax_t gen_uintmax(struct test_rng *rng)
+static cdecl_uintmax gen_uintmax(struct test_rng *rng)
 {
+       cdecl_uintmax ret = 0;
        unsigned char tmp;
-       uintmax_t ret = 0;
        size_t i;
 
        for (i = 0; i < sizeof ret; i++) {
diff --git a/t/rng.c b/t/rng.c
index e442d994fe5cbcd2fef22930e7ed133d3727e76a..a827b2e05f610255de5368784750b6b5a737b787 100644 (file)
--- a/t/rng.c
+++ b/t/rng.c
@@ -24,7 +24,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <inttypes.h>
 #include <errno.h>
 #include <limits.h>
 
@@ -75,11 +74,11 @@ static unsigned long long splitmix64(unsigned long long *state)
 #if !TEST_RNG_NO_EXTERNAL_API
 struct test_rng *test_rng_alloc(const char *seed_str)
 {
+       test_uintmax limit, seed_val;
        unsigned long long seed;
-       uintmax_t limit, seed_val;
        struct test_rng *rng;
 
-       limit  = (uintmax_t)0xffffffff;
+       limit  = 0xffffffff;
        limit |= (limit << 16 << 16);
 
        if (!test_strtoumax(&seed_val, seed_str, limit)) {
index 2e7882de5bf9cbdd6c74929c3bbdb8034745a285..32b4e8cb959fdf4600eeddb779c104089d53e6be 100644 (file)
--- a/t/test.h
+++ b/t/test.h
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
+/*
+ * The test_uintmax typedef is interchangeable with cdecl_uintmax defined in
+ * <cdecl.h>, but we may want to avoid including that header for some tests.
+ */
+#if HAVE_UNSIGNED_LONG_LONG_INT
+typedef unsigned long long test_uintmax;
+#else
+typedef unsigned long test_uintmax;
+#endif
+
 struct cdecl_declspec;
 struct option;
 struct cdecl;
@@ -41,11 +51,11 @@ void print_error(const char *fmt, ...);
 void *malloc_nofail(size_t size);
 void *realloc_nofail(void *ptr, size_t size);
 
-bool test_strtoumax(uintmax_t *out, const char *s, uintmax_t limit);
+bool test_strtoumax(test_uintmax *out, const char *s, test_uintmax limit);
 
 static inline bool test_strtoul(unsigned long *val, const char *str)
 {
-       uintmax_t v;
+       test_uintmax v;
        bool rc;
 
        rc = test_strtoumax(&v, str, (unsigned long)-1);
index dd9e0736a06e31687894cb121a1f2cf1f46072b9..c4178396b85ed5bf795a2f85ce87cda9d05e9aeb 100644 (file)
@@ -76,11 +76,11 @@ static unsigned intconv_base(const char **str)
        return INTCONV_DECIMAL;
 }
 
-bool test_strtoumax(uintmax_t *out, const char *s, uintmax_t limit)
+bool test_strtoumax(cdecl_uintmax *out, const char *s, cdecl_uintmax limit)
 {
        static const char idx[] = "0123456789abcdef0123456789ABCDEF";
        unsigned base = intconv_base(&s);
-       uintmax_t v;
+       cdecl_uintmax v;
        char *c, d;
 
        for (v = 0; (d = *s++);) {