]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
examples: Add hello-c-http example.
authorBruno Haible <bruno@clisp.org>
Wed, 12 Feb 2025 09:54:08 +0000 (10:54 +0100)
committerBruno Haible <bruno@clisp.org>
Thu, 13 Feb 2025 05:12:33 +0000 (06:12 +0100)
* gettext-tools/examples/hello-c-http/INSTALL: New file.
* gettext-tools/examples/hello-c-http/autogen.sh: New file, based on
gettext-tools/examples/hello-c/autogen.sh.
* gettext-tools/examples/hello-c-http/autoclean.sh: New file, based on
gettext-tools/examples/hello-c/autoclean.sh.
* gettext-tools/examples/hello-c-http/hello-server.c: New file.
* gettext-tools/examples/hello-c-http/Makefile.am: New file, based on
gettext-tools/examples/hello-c/Makefile.am.
* gettext-tools/examples/hello-c-http/configure.ac: New file, based on
gettext-tools/examples/hello-c/configure.ac.
* gettext-tools/examples/hello-c-http/m4/Makefile.am: New file, copied from
gettext-tools/examples/hello-c/m4/Makefile.am.
* gettext-tools/examples/hello-c-http/po/POTFILES.in: New file.
* gettext-tools/examples/hello-c-http/po/Makevars: New file, copied from
gettext-tools/examples/hello-c/po/Makevars.
* gettext-tools/examples/hello-c-http/po/LINGUAS: New file, copied from
gettext-tools/examples/hello-c/po/LINGUAS.
* gettext-tools/examples/Makefile.am (EXAMPLESFILES, EXAMPLESDIRS): Add
hello-c-http.
* gettext-tools/examples/po/Makefile.am (POTFILES, SMALLPOTS): Update for
hello-c-http.
(hello-c-http.pot): New target.
(SMALLPOFILES_FOR_lang): Update for hello-c-http.
($(srcdir)/../hello-c-http/po/$(LL).po): New rule.
* gettext-tools/examples/check-examples (func_check_autoclean_all,
func_check_distclean_all, func_check_maintainerclean_all,
func_check_maintainerclean_vpath_all, func_check_dist_all,
func_check_dist_vpath_all, func_check_install_all, func_check_uninstall_all,
func_check_distcheck_all, func_check_all): Handle hello-c-http as well.
* gettext-tools/examples/README: Mention hello-c-http.
* NEWS: Likewise.

15 files changed:
NEWS
gettext-tools/examples/Makefile.am
gettext-tools/examples/README
gettext-tools/examples/check-examples
gettext-tools/examples/hello-c-http/INSTALL [new file with mode: 0644]
gettext-tools/examples/hello-c-http/Makefile.am [new file with mode: 0644]
gettext-tools/examples/hello-c-http/autoclean.sh [new file with mode: 0755]
gettext-tools/examples/hello-c-http/autogen.sh [new file with mode: 0755]
gettext-tools/examples/hello-c-http/configure.ac [new file with mode: 0644]
gettext-tools/examples/hello-c-http/hello-server.c [new file with mode: 0644]
gettext-tools/examples/hello-c-http/m4/Makefile.am [new file with mode: 0644]
gettext-tools/examples/hello-c-http/po/LINGUAS [new file with mode: 0644]
gettext-tools/examples/hello-c-http/po/Makevars [new file with mode: 0644]
gettext-tools/examples/hello-c-http/po/POTFILES.in [new file with mode: 0644]
gettext-tools/examples/po/Makefile.am

diff --git a/NEWS b/NEWS
index 507da43b181002fe532c700b1dc7328cfa4738f6..0d509e761970f83d59440cf101192d21cb0b4d83 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Version 0.24 - January 2025
+Version 0.24 - February 2025
 
 # Programming languages support:
   * JavaScript:
@@ -8,6 +8,9 @@ Version 0.24 - January 2025
     - 'msgfmt -c' now verifies the syntax of translations of Rust format
       strings.
     - A new example 'hello-rust' has been added.
+  * C:
+    - A new example 'hello-c-http' has been added, showing the use of
+      GNU gettext in a multithreaded web server.
   * C++:
     - A new example 'hello-c++-gnome3' has been added.
   * Ruby:
index 0559321e58b2fe7a34b02f6c4cdcaad1ecf3d649..82ceeadb30c88e99bee38d7c788363682e39f419 100644 (file)
@@ -72,6 +72,16 @@ EXAMPLESFILES = \
   hello-c-gnome3/po/Makevars \
   hello-c-gnome3/po/POTFILES.in \
   \
+  hello-c-http/INSTALL \
+  hello-c-http/autogen.sh \
+  hello-c-http/autoclean.sh \
+  hello-c-http/hello-server.c \
+  hello-c-http/Makefile.am \
+  hello-c-http/configure.ac \
+  hello-c-http/m4/Makefile.am \
+  hello-c-http/po/Makevars \
+  hello-c-http/po/POTFILES.in \
+  \
   hello-c++/INSTALL \
   hello-c++/autogen.sh \
   hello-c++/autoclean.sh \
@@ -424,6 +434,7 @@ EXAMPLESDIRS = \
   hello-c \
   hello-c-gnome2 \
   hello-c-gnome3 \
+  hello-c-http \
   hello-c++ \
   hello-c++20 \
   hello-c++-qt \
index 04d81b2d3511b0414a46a8e248f480af23f826d7..8505cef681650ad117298b0ae22f2b4b22d265b9 100644 (file)
@@ -8,6 +8,7 @@ environment.
     hello-c                    C
     hello-c-gnome2             C                 GNOME 2 (obsolete)
     hello-c-gnome3             C                 GNOME 3.10 or later
+    hello-c-http               C                 web browser
     hello-c++                  C++
     hello-c++20                C++ 20
     hello-c++-qt               C++               Qt
@@ -60,6 +61,7 @@ Makefile types:
     hello-c                    .gmo                    Makefile.in.in
     hello-c-gnome2             .gmo                    Makefile.in.in
     hello-c-gnome3             .gmo                    Makefile.in.in
+    hello-c-http               .gmo                    Makefile.in.in
     hello-c++                  .gmo                    Makefile.in.in
     hello-c++20                .gmo                    Makefile.in.in
     hello-c++-kde              .gmo                    Makefile.in.in
index b35c2b3793c44b0aa64340dcd1b08631f0191ca9..df3e2188f65ded0d141e27a2a03d7d8e5819ee25 100755 (executable)
@@ -131,6 +131,7 @@ func_check_autoclean_all ()
   func_check_autoclean hello-c
   func_check_autoclean hello-c-gnome2
   func_check_autoclean hello-c-gnome3
+  func_check_autoclean hello-c-http
   func_check_autoclean hello-c++
   func_check_autoclean hello-c++20
   func_check_autoclean hello-c++-qt
@@ -206,6 +207,7 @@ func_check_distclean_all ()
   func_check_distclean hello-c
  #func_check_distclean hello-c-gnome2
  #func_check_distclean hello-c-gnome3
+  func_check_distclean hello-c-http
   func_check_distclean hello-c++
   func_check_distclean hello-c++20
  #func_check_distclean hello-c++-qt
@@ -283,6 +285,7 @@ func_check_maintainerclean_all ()
   func_check_maintainerclean hello-c
  #func_check_maintainerclean hello-c-gnome2
  #func_check_maintainerclean hello-c-gnome3
+  func_check_maintainerclean hello-c-http
   func_check_maintainerclean hello-c++
   func_check_maintainerclean hello-c++20
  #func_check_maintainerclean hello-c++-qt
@@ -363,6 +366,7 @@ func_check_maintainerclean_vpath_all ()
   func_check_maintainerclean_vpath hello-c
  #func_check_maintainerclean_vpath hello-c-gnome2
  #func_check_maintainerclean_vpath hello-c-gnome3
+  func_check_maintainerclean_vpath hello-c-http
   func_check_maintainerclean_vpath hello-c++
   func_check_maintainerclean_vpath hello-c++20
  #func_check_maintainerclean_vpath hello-c++-qt
@@ -450,6 +454,7 @@ func_check_dist_all ()
   func_check_dist hello-c
  #func_check_dist hello-c-gnome2
  #func_check_dist hello-c-gnome3
+  func_check_dist hello-c-http
   func_check_dist hello-c++
   func_check_dist hello-c++20
  #func_check_dist hello-c++-qt
@@ -537,6 +542,7 @@ func_check_dist_vpath_all ()
   func_check_dist_vpath hello-c
  #func_check_dist_vpath hello-c-gnome2
  #func_check_dist_vpath hello-c-gnome3
+  func_check_dist_vpath hello-c-http
   func_check_dist_vpath hello-c++
   func_check_dist_vpath hello-c++20
  #func_check_dist_vpath hello-c++-qt
@@ -616,6 +622,7 @@ func_check_install_all ()
   func_check_install hello-c
  #func_check_install hello-c-gnome2
  #func_check_install hello-c-gnome3
+  func_check_install hello-c-http
   func_check_install hello-c++
   func_check_install hello-c++20
  #func_check_install hello-c++-qt
@@ -692,6 +699,7 @@ func_check_uninstall_all ()
   func_check_uninstall hello-c
  #func_check_uninstall hello-c-gnome2
  #func_check_uninstall hello-c-gnome3
+  func_check_uninstall hello-c-http
   func_check_uninstall hello-c++
   func_check_uninstall hello-c++20
  #func_check_uninstall hello-c++-qt
@@ -770,6 +778,7 @@ func_check_distcheck_all ()
   func_check_distcheck hello-c
  #func_check_distcheck hello-c-gnome2
  #func_check_distcheck hello-c-gnome3
+  func_check_distcheck hello-c-http
   func_check_distcheck hello-c++
   func_check_distcheck hello-c++20
  #func_check_distcheck hello-c++-qt
@@ -831,6 +840,7 @@ func_check_all ()
   func_check hello-c
   func_check hello-c-gnome2
   func_check hello-c-gnome3
+  func_check hello-c-http
   func_check hello-c++
   func_check hello-c++20
   func_check hello-c++-qt
diff --git a/gettext-tools/examples/hello-c-http/INSTALL b/gettext-tools/examples/hello-c-http/INSTALL
new file mode 100644 (file)
index 0000000..33dd0ac
--- /dev/null
@@ -0,0 +1,83 @@
+This example implements a simple multithreaded web server.
+
+Platforms
+---------
+
+It supports systems with GNU libc.
+It may also work, with some adaptations:
+on Unix platforms (which have POSIX threads)
+other than NetBSD (which does not have uselocale() nor gettext_l).
+
+Dependencies
+------------
+
+It relies just on gettext-runtime (and libc, of course).
+
+Preparations
+------------
+
+To install the needed locales on glibc systems:
+
+$ sudo localedef -i af_ZA -f UTF-8 af_ZA.UTF-8
+$ sudo localedef -i ast_ES -f UTF-8 ast_ES.UTF-8
+$ sudo localedef -i bg_BG -f UTF-8 bg_BG.UTF-8
+$ sudo localedef -i ca_ES -f UTF-8 ca_ES.UTF-8
+$ sudo localedef -i cs_CZ -f UTF-8 cs_CZ.UTF-8
+$ sudo localedef -i da_DK -f UTF-8 da_DK.UTF-8
+$ sudo localedef -i de_DE -f UTF-8 de_DE.UTF-8
+$ sudo localedef -i el_GR -f UTF-8 el_GR.UTF-8
+$ sudo localedef -i en_US -f UTF-8 en_US.UTF-8
+$ sudo localedef -i eo    -f UTF-8 eo
+$ sudo localedef -i es_ES -f UTF-8 es_ES.UTF-8
+$ sudo localedef -i fi_FI -f UTF-8 fi_FI.UTF-8
+$ sudo localedef -i fr_FR -f UTF-8 fr_FR.UTF-8
+$ sudo localedef -i ga_IE -f UTF-8 ga_IE.UTF-8
+$ sudo localedef -i gl_ES -f UTF-8 gl_ES.UTF-8
+$ sudo localedef -i hr_HR -f UTF-8 hr_HR.UTF-8
+$ sudo localedef -i hu_HU -f UTF-8 hu_HU.UTF-8
+$ sudo localedef -i id_ID -f UTF-8 id_ID.UTF-8
+$ sudo localedef -i it_IT -f UTF-8 it_IT.UTF-8
+$ sudo localedef -i ja_JP -f UTF-8 ja_JP.UTF-8
+$ sudo localedef -i ka_GE -f UTF-8 ka_GE.UTF-8
+$ sudo localedef -i ky_KG -f UTF-8 ky_KG
+$ sudo localedef -i lv_LV -f UTF-8 lv_LV.UTF-8
+$ sudo localedef -i ms_MY -f UTF-8 ms_MY.UTF-8
+$ sudo localedef -i mt_MT -f UTF-8 mt_MT.UTF-8
+$ sudo localedef -i nb_NO -f UTF-8 nb_NO.UTF-8
+$ sudo localedef -i nl_NL -f UTF-8 nl_NL.UTF-8
+$ sudo localedef -i nn_NO -f UTF-8 nn_NO.UTF-8
+$ sudo localedef -i pl_PL -f UTF-8 pl_PL.UTF-8
+$ sudo localedef -i pt_PT -f UTF-8 pt_PT.UTF-8
+$ sudo localedef -i pt_BR -f UTF-8 pt_BR.UTF-8
+$ sudo localedef -i ro_RO -f UTF-8 ro_RO.UTF-8
+$ sudo localedef -i ru_RU -f UTF-8 ru_RU.UTF-8
+$ sudo localedef -i sk_SK -f UTF-8 sk_SK.UTF-8
+$ sudo localedef -i sl_SI -f UTF-8 sl_SI.UTF-8
+$ sudo localedef -i sq_AL -f UTF-8 sq_AL.UTF-8
+$ sudo localedef -i sr_RS -f UTF-8 sr_RS
+$ sudo localedef -i sv_SE -f UTF-8 sv_SE.UTF-8
+$ sudo localedef -i ta_IN -f UTF-8 ta_IN
+$ sudo localedef -i tr_TR -f UTF-8 tr_TR.UTF-8
+$ sudo localedef -i uk_UA -f UTF-8 uk_UA.UTF-8
+$ sudo localedef -i vi_VN -f UTF-8 vi_VN
+$ sudo localedef -i zh_CN -f UTF-8 zh_CN.UTF-8
+$ sudo localedef -i zh_HK -f UTF-8 zh_HK.UTF-8
+$ sudo localedef -i zh_TW -f UTF-8 zh_TW.UTF-8
+
+On Debian and Debian-based systems, if you want these locales to be
+persistent across automatic system updates, the approach is different:
+There, you need to enable the locales in the file /etc/locale.gen and
+then run
+$ sudo locale-gen
+
+Building
+--------
+
+Installation:
+  ./autogen.sh
+  ./configure --prefix=/some/prefix
+  make
+  make install
+Cleanup:
+  make distclean
+  ./autoclean.sh
diff --git a/gettext-tools/examples/hello-c-http/Makefile.am b/gettext-tools/examples/hello-c-http/Makefile.am
new file mode 100644 (file)
index 0000000..4409b2f
--- /dev/null
@@ -0,0 +1,26 @@
+# Example for use of GNU gettext.
+# This file is in the public domain.
+#
+# Makefile configuration - processed by automake.
+
+# General automake options.
+AUTOMAKE_OPTIONS = foreign no-dependencies
+ACLOCAL_AMFLAGS = -I m4
+
+# The list of subdirectories containing Makefiles.
+SUBDIRS = m4 po
+
+# The list of programs that are built.
+bin_PROGRAMS = hello-server
+
+# The source files of the 'hello-server' program.
+hello_server_SOURCES = hello-server.c
+
+# Define a C macro LOCALEDIR indicating where catalogs will be installed.
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+# Link time dependencies.
+LDADD = @LIBINTL@
+
+# Additional files to be distributed.
+EXTRA_DIST = autogen.sh autoclean.sh
diff --git a/gettext-tools/examples/hello-c-http/autoclean.sh b/gettext-tools/examples/hello-c-http/autoclean.sh
new file mode 100755 (executable)
index 0000000..9b8332f
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Example for use of GNU gettext.
+# This file is in the public domain.
+#
+# Script for cleaning all autogenerated files.
+
+test ! -f Makefile || make distclean
+rm -rf autom4te.cache
+
+# Brought in by autopoint.
+rm -f ABOUT-NLS
+rm -f config.rpath
+rm -f m4/gettext.m4
+rm -f m4/build-to-host.m4
+rm -f m4/host-cpu-c-abi.m4
+rm -f m4/iconv.m4
+rm -f m4/intlmacosx.m4
+rm -f m4/lib-ld.m4
+rm -f m4/lib-link.m4
+rm -f m4/lib-prefix.m4
+rm -f m4/nls.m4
+rm -f m4/po.m4
+rm -f m4/progtest.m4
+rm -f po/Makefile.in.in
+rm -f po/remove-potcdate.sed
+
+# Generated by aclocal.
+rm -f aclocal.m4
+
+# Generated by autoconf.
+rm -f configure
+
+# Generated or brought in by automake.
+rm -f Makefile.in
+rm -f m4/Makefile.in
+rm -f compile
+rm -f install-sh
+rm -f missing
+rm -f config.guess
+rm -f config.sub
+rm -f po/*.pot
+rm -f po/stamp-po
+rm -f po/*.gmo
diff --git a/gettext-tools/examples/hello-c-http/autogen.sh b/gettext-tools/examples/hello-c-http/autogen.sh
new file mode 100755 (executable)
index 0000000..c589e7d
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+# Example for use of GNU gettext.
+# This file is in the public domain.
+#
+# Script for regenerating all autogenerated files.
+
+if test -r ../Makefile.am; then
+  # Inside the gettext source directory.
+  GETTEXT_TOPSRCDIR=../../..
+else
+  if test -r ../Makefile; then
+    # Inside a gettext build directory.
+    GETTEXT_TOOLS_SRCDIR=`sed -n -e 's,^top_srcdir *= *\(.*\)$,\1,p' ../Makefile`
+    # Adjust a relative top_srcdir.
+    case $GETTEXT_TOOLS_SRCDIR in
+      /*) ;;
+      *) GETTEXT_TOOLS_SRCDIR=../$GETTEXT_TOOLS_SRCDIR ;;
+    esac
+    GETTEXT_TOPSRCDIR=$GETTEXT_TOOLS_SRCDIR/../..
+  else
+    # Installed under ${prefix}/share/doc/gettext/examples.
+    . ../installpaths
+  fi
+fi
+
+autopoint -f # was: gettextize -f -c
+rm po/Makevars.template
+rm po/Rules-quot
+rm po/boldquot.sed
+rm po/en@boldquot.header
+rm po/en@quot.header
+rm po/insert-header.sed
+rm po/quot.sed
+
+aclocal -I m4
+
+autoconf
+
+automake -a -c
+
+cd po
+for f in *.po; do
+  if test -r "$f"; then
+    lang=`echo $f | sed -e 's,\.po$,,'`
+    msgfmt -c -o $lang.gmo $lang.po
+  fi
+done
+cd ..
diff --git a/gettext-tools/examples/hello-c-http/configure.ac b/gettext-tools/examples/hello-c-http/configure.ac
new file mode 100644 (file)
index 0000000..b7f4aef
--- /dev/null
@@ -0,0 +1,17 @@
+dnl Example for use of GNU gettext.
+dnl This file is in the public domain.
+dnl
+dnl Configuration file - processed by autoconf.
+
+AC_INIT([hello-c-http], [0])
+AC_CONFIG_SRCDIR([hello-server.c])
+AM_INIT_AUTOMAKE([1.11])
+
+AC_PROG_CC
+AM_GNU_GETTEXT([external])
+AM_GNU_GETTEXT_VERSION([0.23])
+
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([m4/Makefile])
+AC_CONFIG_FILES([po/Makefile.in])
+AC_OUTPUT
diff --git a/gettext-tools/examples/hello-c-http/hello-server.c b/gettext-tools/examples/hello-c-http/hello-server.c
new file mode 100644 (file)
index 0000000..c4bffc5
--- /dev/null
@@ -0,0 +1,429 @@
+/* Example for use of GNU gettext.
+   This file is in the public domain.
+
+   Source code of the C program.  */
+
+/* This example implements a simple multithreaded web server.
+
+   In order to get translations via gettext(), a locale must be installed on
+   the server system for each language that should be served.  For example,
+   in order to get French translations, you need to install the fr_FR.UTF-8
+   locale.  You find the list of locales below and the installation instructions
+   in the INSTALL file.
+   This may seem strange to people who think "why is this necessary? why should
+   reading a .mo file need a locale?"  The rationale is that servers who produce
+   French text for a web page most often also need French number formatting,
+   French sorting (for lists and UI elements), etc. â€” and these functionalities
+   rely on the locale.
+
+   Since the server is multithreaded, different requests may be served in
+   different threads.  And different requests can come from different users,
+   that have declared different language preferences in their web browser.
+   Therefore, while at a certain moment one thread may produce a French
+   translation (and thus work with a French locale), another thread may be
+   producing a Spanish translation (and thus work with a Spanish locale)
+   at the same time.
+   Using the global locale (via setlocale()) would require locking, so that
+   different threads don't influence each other; but this would severely limit
+   the possible throughput of the server (which is the motivation for making
+   the server multithreaded in the first place).
+   Therefore the server does not use setlocale(), but instead works with
+   locale_t objects, that can become the "current locale" of a thread, via
+   uselocale().
+
+   While it would be possible to allocate the locale_t objects lazily (upon
+   the first request that needs the particular locale), here we allocate
+   them all up-front, so that the response time for a given request is fast
+   and so that there is no contention between the threads.  */
+
+
+/* Persuade glibc to declare asprintf().  */
+#define _GNU_SOURCE 1
+
+/* Get textdomain(), bindtextdomain(), gettext() declarations.  */
+#include <libintl.h>
+
+/* Get locale_t, newlocale(), uselocale() declarations.  */
+#include <locale.h>
+
+/* Get pthread_create(), pthread_join() declarations.  */
+#include <pthread.h>
+
+/* Get asprintf(), dprintf() declarations.  */
+#include <stdio.h>
+
+/* Get abort(), free() declarations.  */
+#include <stdlib.h>
+
+/* Get memset(), strchr(), strcasecmp(), strncasecmp() declarations.  */
+#include <string.h>
+
+/* Get nanosleep().  */
+#include <time.h>
+
+/* Get close() declaration.  */
+#include <unistd.h>
+
+/* Get socket(), setsockopt(), bind(), listen(), accept(), recv() declarations.  */
+#include <sys/socket.h>
+
+/* Get IPPROTO_TCP, INADDR_ANY, in6addr_any.  */
+#include <netinet/in.h>
+
+
+/* Mapping from language to locale.  */
+struct language_support
+{
+  const char *language;
+  const char *locale_name;
+  locale_t locale; /* NULL when the locale is not installed on the system */
+};
+static struct language_support all_languages[] =
+{
+  /* The locale names here must all be UTF-8 locales.  */
+  { "af", "af_ZA.UTF-8" },
+  { "ast", "ast_ES.UTF-8" },
+  { "bg", "bg_BG.UTF-8" },
+  { "ca", "ca_ES.UTF-8" },
+  { "cs", "cs_CZ.UTF-8" },
+  { "da", "da_DK.UTF-8" },
+  { "de", "de_DE.UTF-8" },
+  { "el", "el_GR.UTF-8" },
+  { "en", "en_US.UTF-8" },
+  { "eo", "eo" },
+  { "es", "es_ES.UTF-8" },
+  { "fi", "fi_FI.UTF-8" },
+  { "fr", "fr_FR.UTF-8" },
+  { "ga", "ga_IE.UTF-8" },
+  { "gl", "gl_ES.UTF-8" },
+  { "hr", "hr_HR.UTF-8" },
+  { "hu", "hu_HU.UTF-8" },
+  { "id", "id_ID.UTF-8" },
+  { "it", "it_IT.UTF-8" },
+  { "ja", "ja_JP.UTF-8" },
+  { "ka", "ka_GE.UTF-8" },
+  { "ky", "ky_KG" },
+  { "lv", "lv_LV.UTF-8" },
+  { "ms", "ms_MY.UTF-8" },
+  { "mt", "mt_MT.UTF-8" },
+  { "nb", "nb_NO.UTF-8" },
+  { "nl", "nl_NL.UTF-8" },
+  { "nn", "nn_NO.UTF-8" },
+  { "pl", "pl_PL.UTF-8" },
+  { "pt", "pt_PT.UTF-8" },
+  { "pt_BR", "pt_BR.UTF-8" },
+  { "ro", "ro_RO.UTF-8" },
+  { "ru", "ru_RU.UTF-8" },
+  { "sk", "sk_SK.UTF-8" },
+  { "sl", "sl_SI.UTF-8" },
+  { "sq", "sq_AL.UTF-8" },
+  { "sr", "sr_RS" },
+  { "sv", "sv_SE.UTF-8" },
+  { "ta", "ta_IN" },
+  { "tr", "tr_TR.UTF-8" },
+  { "uk", "uk_UA.UTF-8" },
+  { "vi", "vi_VN" },
+  { "zh_CN", "zh_CN.UTF-8" },
+  { "zh_HK", "zh_HK.UTF-8" },
+  { "zh_TW", "zh_TW.UTF-8" }
+};
+
+/* Get the locale that exactly matches a given language.  */
+static locale_t
+get_locale_from_language (const char *language)
+{
+  size_t i;
+  for (i = 0; i < sizeof (all_languages) / sizeof (all_languages[0]); i++)
+    if (strcasecmp (all_languages[i].language, language) == 0)
+      return all_languages[i].locale;
+  return NULL;
+}
+
+/* Get the locale that can be used for a given language.  */
+static locale_t
+get_locale_matching_language (char *language)
+{
+  /* Convert '-' to '_'.  */
+  char *dash = strchr (language, '-');
+  if (dash != NULL)
+    *dash = '_';
+
+  locale_t result = get_locale_from_language (language);
+  if (result == NULL && dash != NULL)
+    {
+      /* Truncate the language at the dash's position.  */
+      *dash = '\0';
+      result = get_locale_from_language (language);
+    }
+
+  /* Restore language.  */
+  if (dash != NULL)
+    *dash = '-';
+
+  return result;
+}
+
+/* Get the locale for an 'Accept-Language' request header field element.  */
+static locale_t
+get_locale_matching_element (char *element_start, char *element_end)
+{
+  char *p;
+
+  /* Ignore the element part that starts with a semicolon.  */
+  for (p = element_start; p < element_end && *p != ';'; p++)
+    ;
+  element_end = p;
+
+  /* Trim the element.  */
+  while (element_start < element_end && element_end[-1] == ' ')
+    element_end--;
+  while (element_start < element_end && element_start[0] == ' ')
+    element_start++;
+  if (element_start == element_end)
+    return NULL;
+
+  char saved = *element_end;
+  *element_end = '\0';
+  locale_t result = get_locale_matching_language (element_start);
+  *element_end = saved;
+
+  return result;
+}
+
+/* Get the locale for an 'Accept-Language' request header field.  */
+static locale_t
+get_locale_matching_field (char *field_start, char *field_end)
+{
+  /* The field's value is a comma-separated list of "lang [; q=...]" elements.
+     Each lang is of the form ll-CC, not a BCP 47 string.
+     Therefore, for Chinese, expect zh-CN, zh-TW, etc.  See
+     <https://stackoverflow.com/questions/69709824/>  */
+  char *element_start = field_start;
+  for (;;)
+    {
+      char *p;
+
+      for (p = element_start; p < field_end && *p != ','; p++)
+        ;
+      char *element_end = p;
+
+      locale_t locale_for_element =
+        get_locale_matching_element (element_start, element_end);
+      /* If the locale is not supported on this system, skip this element and
+         continue with the next one.  */
+      if (locale_for_element != NULL)
+        return locale_for_element;
+
+      if (element_end == field_end)
+        break;
+      element_start = element_end + 1;
+    }
+  return NULL;
+}
+
+/* Extract the desired locale from the value of the 'Accept-Language'
+   request header field.  */
+static locale_t
+get_locale_from_header (char *header_start, char *header_end)
+{
+  char *line_start = header_start;
+  while (line_start < header_end)
+    {
+      char *line_end = strchr (line_start, '\r');
+      if (line_end == NULL)
+        abort ();
+      if (line_end - line_start >= 16
+          && strncasecmp (line_start, "Accept-Language:", 16) == 0)
+        {
+          char *field_start = line_start + 16;
+          char *field_end = line_end;
+          return get_locale_matching_field (field_start, field_end);
+        }
+      line_start = line_end + 2;
+    }
+  return NULL;
+}
+
+
+/* This function defines what each thread does.  */
+
+static void *
+server_thread (void *arg)
+{
+  int server_socket = *(int const *) arg;
+  enum { BUFFER_SIZE = 4096 };
+
+  for (;;)
+    {
+      /* Accept an incoming connection.  */
+      struct sockaddr_storage addr;
+      socklen_t addrlen = sizeof (addr);
+      int connected_socket =
+        accept (server_socket, (struct sockaddr *) &addr, &addrlen);
+      if (connected_socket >= 0)
+        {
+          /* Receive the initial part of an HTTP request.  */
+          char request[BUFFER_SIZE + 1];
+          int req_len = recv (connected_socket, request, BUFFER_SIZE, 0);
+          if (req_len >= 0)
+            {
+              /* Determine the extent of the HTTP request header.
+                 We are not interested in the message body.  */
+              char *header_start;
+              char *header_end;
+              {
+                request[req_len] = '\0';
+                char *line_start = request;
+                char *line_end = strchr (line_start, '\r');
+                if (line_end != NULL && line_end[1] == '\n')
+                  {
+                    header_start = line_start = line_end + 2;
+                    for (;;)
+                      {
+                        line_end = strchr (line_start, '\r');
+                        if (!(line_end != NULL && line_end[1] == '\n'
+                              && line_end > line_start))
+                          /* An empty line ends the header and starts the body.  */
+                          break;
+                        line_start = line_end + 2;
+                      }
+                    header_end = line_start;
+                  }
+                else
+                  header_start = header_end = request;
+              }
+              /* Determine the locale.  */
+              locale_t locale =
+                get_locale_from_header (header_start, header_end);
+              /* Set the locale on this thread.
+                 If locale == NULL, we use the thread's default locale, which
+                 is the global locale, which is "C".  */
+              if (locale != NULL)
+                uselocale (locale);
+
+              /* Get the localized HTTP response body.  */
+              char *response_body;
+              /* Some HTML could be added here.  */
+              if (asprintf (&response_body, "%s\n", gettext ("Hello, world!")) >= 0)
+                {
+                  /* Writing to the connected_socket via send() is the same as
+                     via write().  So, we can use dprintf().  Alternatively,
+                     one could use fdopen() and fprintf().  */
+                  dprintf (connected_socket,
+                           "HTTP/1.1 200 OK\r\n"
+                           "Content-Type: text/plain; charset=UTF-8\r\n"
+                           "Content-Length: %lu\r\n"
+                           "Connection: close\r\n"
+                           "\r\n"
+                           "%s",
+                           (unsigned long) strlen (response_body),
+                           response_body);
+                  free (response_body);
+                }
+
+              /* Restore the previous locale.  */
+              if (locale != NULL)
+                uselocale (LC_GLOBAL_LOCALE);
+            }
+          close (connected_socket);
+
+          /* Enable this to ensure that different threads get actually used.  */
+          if (0)
+            {
+              struct timespec duration = { .tv_sec = 60, .tv_nsec = 0 };
+              nanosleep (&duration, NULL);
+            }
+        }
+    }
+  return NULL;
+}
+
+
+/* Main program.  */
+
+#define PORT 8080
+
+/* The IPv4 server socket.  */
+int server_socket4;
+/* The IPv6 server socket.  */
+int server_socket6;
+
+/* Number of threads per socket.  */
+#define NUM_THREADS 10
+
+int
+main ()
+{
+  textdomain ("hello-c-http");
+  bindtextdomain ("hello-c-http", LOCALEDIR);
+
+  /* Initialize all_languages.  */
+  unsigned int i;
+  for (i = 0; i < sizeof (all_languages) / sizeof (all_languages[0]); i++)
+    all_languages[i].locale =
+      newlocale (LC_ALL_MASK, all_languages[i].locale_name, NULL);
+
+  /* Initialize an IPv4 server socket.  */
+  {
+    server_socket4 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (server_socket4 < 0)
+      return 1;
+
+    /* Avoid an EADDRINUSE error in the next bind() call.  */
+    unsigned int flag = 1;
+    setsockopt (server_socket4, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (flag));
+
+    struct sockaddr_in server_addr;
+    memset (&server_addr, 0, sizeof (server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_addr.s_addr = INADDR_ANY;
+    server_addr.sin_port = htons (PORT);
+    if (bind (server_socket4, (struct sockaddr *) &server_addr, sizeof (server_addr)) < 0)
+      return 2;
+
+    if (listen (server_socket4, 10) < 0)
+      return 3;
+  }
+
+  /* Initialize an IPv6 server socket.  */
+  {
+    server_socket6 = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+    if (server_socket6 >= 0)
+      {
+        /* Avoid an EADDRINUSE error in the next bind() call.  */
+        unsigned int flag = 1;
+        setsockopt (server_socket6, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (flag));
+        /* We don't want dual-socket support here.  */
+        setsockopt (server_socket6, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof (flag));
+
+        struct sockaddr_in6 server_addr;
+        memset (&server_addr, 0, sizeof (server_addr));
+        server_addr.sin6_family = AF_INET6;
+        server_addr.sin6_addr = in6addr_any;
+        server_addr.sin6_port = htons (PORT);
+        if (bind (server_socket6, (struct sockaddr *) &server_addr, sizeof (server_addr)) < 0)
+          return 4;
+
+        if (listen (server_socket6, 10) < 0)
+          return 5;
+      }
+  }
+
+  printf ("Server is listening on port %d...\n", PORT);
+
+  pthread_t thread;
+  for (i = 0; i < NUM_THREADS; i++)
+    {
+      if (pthread_create (&thread, NULL, server_thread, &server_socket4) < 0)
+        return 6;
+    }
+  if (server_socket6 >= 0)
+    for (i = 0; i < NUM_THREADS; i++)
+      {
+        if (pthread_create (&thread, NULL, server_thread, &server_socket6) < 0)
+          return 6;
+      }
+
+  /* Wait forever.  */
+  pthread_join (thread, NULL);
+}
diff --git a/gettext-tools/examples/hello-c-http/m4/Makefile.am b/gettext-tools/examples/hello-c-http/m4/Makefile.am
new file mode 100644 (file)
index 0000000..7d516f0
--- /dev/null
@@ -0,0 +1,4 @@
+EXTRA_DIST = \
+  gettext.m4 build-to-host.m4 host-cpu-c-abi.m4 \
+  iconv.m4 intlmacosx.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 \
+  nls.m4 po.m4 progtest.m4
diff --git a/gettext-tools/examples/hello-c-http/po/LINGUAS b/gettext-tools/examples/hello-c-http/po/LINGUAS
new file mode 100644 (file)
index 0000000..dc4a82a
--- /dev/null
@@ -0,0 +1,5 @@
+# Example for use of GNU gettext.
+# This file is in the public domain.
+#
+# Set of available languages.
+af ast bg ca cs da de el eo es fi fr ga gl hr hu id it ja ka ky lv ms mt nb nl nn pl pt pt_BR ro ru sk sl sq sr sv ta tr uk vi zh_CN zh_HK zh_TW
diff --git a/gettext-tools/examples/hello-c-http/po/Makevars b/gettext-tools/examples/hello-c-http/po/Makevars
new file mode 100644 (file)
index 0000000..16af4d2
--- /dev/null
@@ -0,0 +1,83 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+#
+# Copyright (C) 2003-2019 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to use, copy, distribute, and modify it.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = \
+  --keyword=_ --flag=_:1:pass-c-format \
+  --keyword=N_ --flag=N_:1:pass-c-format
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file.  Set this to the copyright holder of the surrounding
+# package.  (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.)  Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright.  The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Yoyodyne, Inc.
+
+# This tells whether or not to prepend "GNU " prefix to the package
+# name that gets inserted into the header of the $(DOMAIN).pot file.
+# Possible values are "yes", "no", or empty.  If it is empty, try to
+# detect it automatically by scanning the files in $(top_srcdir) for
+# "GNU packagename" string.
+PACKAGE_GNU = no
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+#   in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+#   understood.
+# - Strings which make invalid assumptions about notation of date, time or
+#   money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS = bug-gettext@gnu.org
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used.  It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
+
+# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'
+# context.  Possible values are "yes" and "no".  Set this to yes if the
+# package uses functions taking also a message context, like pgettext(), or
+# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.
+USE_MSGCTXT = no
+
+# These options get passed to msgmerge.
+# Useful options are in particular:
+#   --previous            to keep previous msgids of translated messages
+MSGMERGE_OPTIONS =
+
+# These options get passed to msginit.
+# If you want to disable line wrapping when writing PO files, add
+# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and
+# MSGINIT_OPTIONS.
+MSGINIT_OPTIONS =
+
+# This tells whether or not to regenerate a PO file when $(DOMAIN).pot
+# has changed.  Possible values are "yes" and "no".  Set this to no if
+# the POT file is checked in the repository and the version control
+# program ignores timestamps.
+PO_DEPENDS_ON_POT = yes
+
+# This tells whether or not to forcibly update $(DOMAIN).pot and
+# regenerate PO files on "make dist".  Possible values are "yes" and
+# "no".  Set this to no if the POT file and PO files are maintained
+# externally.
+DIST_DEPENDS_ON_UPDATE_PO = yes
diff --git a/gettext-tools/examples/hello-c-http/po/POTFILES.in b/gettext-tools/examples/hello-c-http/po/POTFILES.in
new file mode 100644 (file)
index 0000000..8215af4
--- /dev/null
@@ -0,0 +1,5 @@
+# Example for use of GNU gettext.
+# This file is in the public domain.
+#
+# List of files which contain translatable strings.
+hello-server.c
index 9901db39e827c437de5f6baba7ea0c4192e8436c..992ff0c2a313b2585c6abc94f67ce242aa591700 100644 (file)
@@ -29,6 +29,7 @@ POTFILES = \
   hello-c-gnome3/hello2.desktop.in.in \
   hello-c-gnome3/hello2.ui \
   hello-c-gnome3/hello2.gschema.xml \
+  hello-c-http/hello-server.c \
   hello-c++/hello.cc \
   hello-c++20/hello.cc \
   hello-c++-qt/hello.cc \
@@ -75,6 +76,7 @@ SMALLPOTS = \
   hello-c.pot \
   hello-c-gnome2.pot \
   hello-c-gnome3.pot \
+  hello-c-http.pot \
   hello-c++.pot \
   hello-c++20.pot \
   hello-c++-qt.pot \
@@ -259,6 +261,9 @@ hello-c-gnome2.pot : $(POTFILES_DEPS)
 hello-c-gnome3.pot : $(POTFILES_DEPS)
        $(USE_BUILT_PROGS) $(SHELL) '$(srcdir)/xsmallpot.sh' '$(srcdir)' hello-c-gnome3
 
+hello-c-http.pot : $(POTFILES_DEPS)
+       $(USE_BUILT_PROGS) $(SHELL) '$(srcdir)/xsmallpot.sh' '$(srcdir)' hello-c-http
+
 hello-c++.pot : $(POTFILES_DEPS)
        $(USE_BUILT_PROGS) $(SHELL) '$(srcdir)/xsmallpot.sh' '$(srcdir)' hello-c++
 
@@ -431,6 +436,7 @@ SMALLPOFILES_FOR_lang = \
   $(srcdir)/../hello-c/po/$$lang.po \
   $(srcdir)/../hello-c-gnome2/po/$$lang.po \
   $(srcdir)/../hello-c-gnome3/po/$$lang.po \
+  $(srcdir)/../hello-c-http/po/$$lang.po \
   $(srcdir)/../hello-c++/po/$$lang.po \
   $(srcdir)/../hello-c++20/po/$$lang.po \
   $(srcdir)/../hello-c++-qt/po/$$lang.po \
@@ -472,6 +478,9 @@ $(srcdir)/../hello-c-gnome2/po/$(LL).po: $(srcdir)/hello-c-gnome2.pot $(srcdir)/
 $(srcdir)/../hello-c-gnome3/po/$(LL).po: $(srcdir)/hello-c-gnome3.pot $(srcdir)/$(LL).po
        $(USE_BUILT_PROGS) cd $(srcdir) && $(SHELL) mmsmallpo.sh hello-c-gnome3 $(LL)
 
+$(srcdir)/../hello-c-http/po/$(LL).po: $(srcdir)/hello-c-http.pot $(srcdir)/$(LL).po
+       $(USE_BUILT_PROGS) cd $(srcdir) && $(SHELL) mmsmallpo.sh hello-c-http $(LL)
+
 $(srcdir)/../hello-c++/po/$(LL).po: $(srcdir)/hello-c++.pot $(srcdir)/$(LL).po
        $(USE_BUILT_PROGS) cd $(srcdir) && $(SHELL) mmsmallpo.sh hello-c++ $(LL)