|
|
@@ -1,19 +1,47 @@
|
|
|
#ifndef LIBINTL_H
|
|
|
#define LIBINTL_H
|
|
|
|
|
|
-char *gettext(const char *msgid);
|
|
|
-char *dgettext(const char *domainname, const char *msgid);
|
|
|
-char *dcgettext(const char *domainname, const char *msgid, int category);
|
|
|
-char *ngettext(const char *msgid1, const char *msgid2, unsigned long n);
|
|
|
-char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long n);
|
|
|
-char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long n, int category);
|
|
|
+/* BBS: __attribute__((format_arg(N))) tells GCC that this function
|
|
|
+ returns a format-string equivalent to argument N. -Wformat-security
|
|
|
+ uses this to see through gettext/dgettext/... calls and detect that
|
|
|
+ the msgid argument was a string literal. glibc's libintl.h ships the
|
|
|
+ same annotation; without it, programs that build with
|
|
|
+ -Werror=format-security (e.g. distro-style hardening flags) would
|
|
|
+ fail on every printf(_("fmt %d"), n). */
|
|
|
+#if defined __GNUC__ && __GNUC__ >= 3
|
|
|
+# define __libintl_format_arg(N) __attribute__ ((__format_arg__ (N)))
|
|
|
+#else
|
|
|
+# define __libintl_format_arg(N)
|
|
|
+#endif
|
|
|
+
|
|
|
+char *gettext(const char *msgid)
|
|
|
+ __libintl_format_arg(1);
|
|
|
+char *dgettext(const char *domainname, const char *msgid)
|
|
|
+ __libintl_format_arg(2);
|
|
|
+char *dcgettext(const char *domainname, const char *msgid, int category)
|
|
|
+ __libintl_format_arg(2);
|
|
|
+char *ngettext(const char *msgid1, const char *msgid2, unsigned long n)
|
|
|
+ __libintl_format_arg(1) __libintl_format_arg(2);
|
|
|
+char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long n)
|
|
|
+ __libintl_format_arg(2) __libintl_format_arg(3);
|
|
|
+char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long n, int category)
|
|
|
+ __libintl_format_arg(2) __libintl_format_arg(3);
|
|
|
|
|
|
char *textdomain(const char *domainname);
|
|
|
char *bind_textdomain_codeset(const char *domainname, const char *codeset);
|
|
|
char *bindtextdomain(const char *domainname, const char *dirname);
|
|
|
|
|
|
-#undef gettext_noop
|
|
|
-#define gettext_noop(X) X
|
|
|
+/* BBS: gettext_noop is just compile-time identity; many GNU programs
|
|
|
+ (e.g. elfutils' lib/system.h line 157) ship
|
|
|
+ #define gettext_noop(Str) Str
|
|
|
+ verbatim. Use the same token form so a redefinition is recognised
|
|
|
+ as identical by the preprocessor and does not trigger
|
|
|
+ -Werror=macro-redefined. Also gate with #ifndef so a consumer that
|
|
|
+ defines it first keeps its definition (which the previous
|
|
|
+ "#undef gettext_noop" would have wiped). */
|
|
|
+#ifndef gettext_noop
|
|
|
+#define gettext_noop(Str) Str
|
|
|
+#endif
|
|
|
|
|
|
#ifndef LIBINTL_NO_MACROS
|
|
|
/* if these macros are defined, configure checks will detect libintl as
|
|
|
@@ -22,31 +50,105 @@ char *bindtextdomain(const char *domainname, const char *dirname);
|
|
|
* checking for ngettext in libc ... yes
|
|
|
* the consequence is that -lintl will not be added to the LDFLAGS.
|
|
|
* so if for some reason you want that libintl.a gets linked,
|
|
|
- * add -DLIBINTL_NO_MACROS=1 to your CPPFLAGS. */
|
|
|
-
|
|
|
-#define gettext(X) ((char*) (X))
|
|
|
-#define dgettext(dom, X) ((void)(dom), (char*) (X))
|
|
|
-#define dcgettext(dom, X, cat) ((void)(dom), (void)(cat), (char*) (X))
|
|
|
-#define ngettext(X, Y, N) \
|
|
|
- ((char*) (((N) == 1) ? ((void)(Y), (X)) : ((void)(X), (Y))))
|
|
|
-#define dngettext(dom, X, Y, N) \
|
|
|
- ((dom), (char*) (((N) == 1) ? ((void)(Y), (X)) : ((void)(X), (Y))))
|
|
|
-#define dcngettext(dom, X, Y, N, cat) \
|
|
|
- ((dom), (cat), (char*) (((N) == 1) ? ((void)(Y), (X)) : ((void)(X), (Y))))
|
|
|
-#define bindtextdomain(X, Y) ((void)(X), (void)(Y), (char*) "/")
|
|
|
-#define bind_textdomain_codeset(dom, codeset) \
|
|
|
- ((void)(dom), (void)(codeset), (char*) 0)
|
|
|
-#define textdomain(X) ((void)(X), (char*) "messages")
|
|
|
+ * add -DLIBINTL_NO_MACROS=1 to your CPPFLAGS.
|
|
|
+ *
|
|
|
+ * BBS: the previous implementation expanded to comma expressions like
|
|
|
+ * #define dgettext(dom, X) ((void)(dom), (char*) (X))
|
|
|
+ * which silenced -Wunused-value on the unused dom argument but defeated
|
|
|
+ * -Wformat-security: GCC cannot see through a comma expression to
|
|
|
+ * decide that the result is still a string literal. Route through
|
|
|
+ * static-inline stubs decorated with __attribute__((format_arg))
|
|
|
+ * instead:
|
|
|
+ * - function-call semantics evaluate every argument, so side effects
|
|
|
+ * in callers like dgettext(read_dom(), "fmt") are preserved;
|
|
|
+ * - the inline body collapses to (char*) msgid at -O1+ for literal
|
|
|
+ * msgid, so there is zero runtime overhead in the common case;
|
|
|
+ * - format_arg tells -Wformat-security how to follow the call back
|
|
|
+ * to the original literal.
|
|
|
+ * The static-inline stub names match the convention __libintl_stub_*.
|
|
|
+ * The user-visible macros shadow the extern declarations only at call
|
|
|
+ * sites (foo(...)), so taking &gettext still resolves to the extern
|
|
|
+ * function declared above. */
|
|
|
+
|
|
|
+static inline __libintl_format_arg(1)
|
|
|
+char *__libintl_stub_gettext(const char *__msgid)
|
|
|
+{
|
|
|
+ return (char *) __msgid;
|
|
|
+}
|
|
|
+#define gettext(X) __libintl_stub_gettext(X)
|
|
|
+
|
|
|
+static inline __libintl_format_arg(2)
|
|
|
+char *__libintl_stub_dgettext(const char *__dom, const char *__msgid)
|
|
|
+{
|
|
|
+ (void) __dom;
|
|
|
+ return (char *) __msgid;
|
|
|
+}
|
|
|
+#define dgettext(dom, X) __libintl_stub_dgettext((dom), (X))
|
|
|
+
|
|
|
+static inline __libintl_format_arg(2)
|
|
|
+char *__libintl_stub_dcgettext(const char *__dom, const char *__msgid, int __cat)
|
|
|
+{
|
|
|
+ (void) __dom; (void) __cat;
|
|
|
+ return (char *) __msgid;
|
|
|
+}
|
|
|
+#define dcgettext(dom, X, cat) __libintl_stub_dcgettext((dom), (X), (cat))
|
|
|
+
|
|
|
+static inline __libintl_format_arg(1) __libintl_format_arg(2)
|
|
|
+char *__libintl_stub_ngettext(const char *__s1, const char *__s2, unsigned long __n)
|
|
|
+{
|
|
|
+ return (char *) ((__n == 1) ? __s1 : __s2);
|
|
|
+}
|
|
|
+#define ngettext(X, Y, N) __libintl_stub_ngettext((X), (Y), (N))
|
|
|
+
|
|
|
+static inline __libintl_format_arg(2) __libintl_format_arg(3)
|
|
|
+char *__libintl_stub_dngettext(const char *__dom, const char *__s1, const char *__s2, unsigned long __n)
|
|
|
+{
|
|
|
+ (void) __dom;
|
|
|
+ return (char *) ((__n == 1) ? __s1 : __s2);
|
|
|
+}
|
|
|
+#define dngettext(dom, X, Y, N) __libintl_stub_dngettext((dom), (X), (Y), (N))
|
|
|
+
|
|
|
+static inline __libintl_format_arg(2) __libintl_format_arg(3)
|
|
|
+char *__libintl_stub_dcngettext(const char *__dom, const char *__s1, const char *__s2, unsigned long __n, int __cat)
|
|
|
+{
|
|
|
+ (void) __dom; (void) __cat;
|
|
|
+ return (char *) ((__n == 1) ? __s1 : __s2);
|
|
|
+}
|
|
|
+#define dcngettext(dom, X, Y, N, cat) __libintl_stub_dcngettext((dom), (X), (Y), (N), (cat))
|
|
|
+
|
|
|
+static inline char *__libintl_stub_bindtextdomain(const char *__dom, const char *__dir)
|
|
|
+{
|
|
|
+ (void) __dom; (void) __dir;
|
|
|
+ return (char *) "/";
|
|
|
+}
|
|
|
+#define bindtextdomain(X, Y) __libintl_stub_bindtextdomain((X), (Y))
|
|
|
+
|
|
|
+static inline char *__libintl_stub_bind_textdomain_codeset(const char *__dom, const char *__codeset)
|
|
|
+{
|
|
|
+ (void) __dom; (void) __codeset;
|
|
|
+ return (char *) 0;
|
|
|
+}
|
|
|
+#define bind_textdomain_codeset(dom, codeset) __libintl_stub_bind_textdomain_codeset((dom), (codeset))
|
|
|
+
|
|
|
+static inline char *__libintl_stub_textdomain(const char *__dom)
|
|
|
+{
|
|
|
+ (void) __dom;
|
|
|
+ return (char *) "messages";
|
|
|
+}
|
|
|
+#define textdomain(X) __libintl_stub_textdomain(X)
|
|
|
|
|
|
#undef ENABLE_NLS
|
|
|
#undef DISABLE_NLS
|
|
|
#define DISABLE_NLS 1
|
|
|
|
|
|
-#if __GNUC__ +0 > 3
|
|
|
-/* most ppl call bindtextdomain() without using its return value
|
|
|
- thus we get tons of warnings about "statement with no effect" */
|
|
|
-#pragma GCC diagnostic ignored "-Wunused-value"
|
|
|
-#endif
|
|
|
+/* BBS: the previous version had
|
|
|
+ * #pragma GCC diagnostic ignored "-Wunused-value"
|
|
|
+ * here because the comma-expression macros produced a (char*)"..."
|
|
|
+ * trailing rvalue that callers discarded as a statement. With
|
|
|
+ * function-call semantics (static-inline stubs) the discarded return
|
|
|
+ * value is a regular function call statement, not an unused expression
|
|
|
+ * value, so -Wunused-value never fires and the pragma is no longer
|
|
|
+ * needed. */
|
|
|
|
|
|
#endif
|
|
|
|