<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40012 >

Errors from PR#39831, due to my incomplete implementation of buffer
concatenation length checking.

===

Date:   Sat, 12 Jan 2008 17:52:27 +0100
From:   Joan Creus <[EMAIL PROTECTED]>

...

The message is "Assertion `len_dest<n' failed."

...

And, since we are at it, I think there is a "\n" missing at the end of
the string in line 1040 (after "see Port Facility").

===

As noted for many years in the file itself:

   FIXME:
   All these helptext_* functions have a pretty crappy interface:
   we just write to buf and hope that its long enough.
   But I'm not going to fix it right now --dwp.

Indeed, almost all of the functions pass the buffer size, but fail to
check things during concatenation.

Worse, helptext_unit() doesn't pass the size at all!

Therefore, update helptext_unit() parameters to match helptext_building()
and the others.  Finish the partially complete checks for helptext_tech(),
helptext_terrain(), and helptext_government().

Ensure the buffers are always '\0' terminated, use strlcat().

Fix some missing translation qualifiers for comma lists, both bare
("?blistmore:, ") and c-format ("?clistmore:, %s").

Fix a number of TRANS messages that were on the wrong line, and didn't
show up in the *.po files.

Fix several plural translations.

Index: client/gui-gtk-2.0/helpdlg.c
===================================================================
--- client/gui-gtk-2.0/helpdlg.c        (revision 14228)
+++ client/gui-gtk-2.0/helpdlg.c        (working copy)
@@ -818,7 +818,7 @@
                         utype_name_translation(utype->obsoleted_by));
     }
 
-    helptext_unit(buf, utype, pitem->text);
+    helptext_unit(buf, sizeof(buf), utype, pitem->text);
 
     gtk_text_buffer_set_text(help_text, buf, -1);
     gtk_widget_show(help_text_sw);
Index: client/gui-xaw/helpdlg.c
===================================================================
--- client/gui-xaw/helpdlg.c    (revision 14228)
+++ client/gui-xaw/helpdlg.c    (working copy)
@@ -863,7 +863,7 @@
                    utype_name_translation(punittype->obsoleted_by));
     }
     /* add text for transport_capacity, fuel, and flags: */
-    helptext_unit(buf, punittype, pitem->text);
+    helptext_unit(buf, sizeof(buf), punittype, pitem->text);
     XtVaSetValues(help_text, XtNstring, buf, NULL);
   }
   else {
Index: client/gui-win32/helpdlg.c
===================================================================
--- client/gui-win32/helpdlg.c  (revision 14228)
+++ client/gui-win32/helpdlg.c  (working copy)
@@ -734,7 +734,7 @@
                     utype_name_translation(utype->obsoleted_by));
     }
 
-    helptext_unit(buf, utype, pitem->text);
+    helptext_unit(buf, sizeof(buf), utype, pitem->text);
     set_help_text(buf);
   }
   else {
Index: client/gui-sdl/helpdlg.c
===================================================================
--- client/gui-sdl/helpdlg.c    (revision 14228)
+++ client/gui-sdl/helpdlg.c    (working copy)
@@ -837,7 +837,7 @@
   start_x = (area.x + 1 + scrollbar_width + 
pHelpDlg->pActiveWidgetList->size.w + adj_size(20));
 
   buffer[0] = '\0';
-  helptext_unit(buffer, utype_by_number(type_id), "");
+  helptext_unit(buffer, sizeof(buffer), utype_by_number(type_id), "");
   if (buffer[0] != '\0') {
     SDL_String16 *pStr = create_str16_from_char(buffer, adj_font(12));
     convert_string_to_const_surface_width(pStr, adj_size(640) - start_x - 
adj_size(20));
Index: client/helpdata.c
===================================================================
--- client/helpdata.c   (revision 14228)
+++ client/helpdata.c   (working copy)
@@ -44,6 +44,9 @@
 
 #include "helpdata.h"
 
+/* helper macro for easy conversion from snprintf and cat_snprintf */
+#define CATLSTR(_b, _s, _t) mystrlcat(_b, _t, _s)
+
 static const char * const help_type_names[] = {
   "(Any)", "(Text)", "Units", "Improvements", "Wonders",
   "Techs", "Terrain", "Governments", NULL
@@ -249,10 +252,9 @@
   /* FIXME: show other data like range and survives. */
 #define COREQ_APPEND(s)                                                        
    \
   (coreq_buf[0] != '\0'                                                        
    \
-   ? cat_snprintf(coreq_buf, sizeof(coreq_buf),        ", %s", (s))        \
+   ? cat_snprintf(coreq_buf, sizeof(coreq_buf), Q_("?clistmore:, %s"), (s))  \
    : sz_strlcpy(coreq_buf, (s)))
 
-
   improvement_iterate(pimprove) {
     requirement_vector_iterate(&pimprove->reqs, req) {
       if (are_universals_equal(psource, &req->source)) {
@@ -628,15 +630,6 @@
 
 /****************************************************************
   FIXME:
-  All these helptext_* functions have a pretty crappy interface:
-  we just write to buf and hope that its long enough.
-  But I'm not going to fix it right now --dwp.
-  
-  Could also reduce amount/length of strlen's by inserting
-  a few 'buf += strlen(buf)'.
-
-  These functions should always ensure final buf is null-terminated.
-  
   Also, in principle these could be auto-generated once, inserted
   into pitem->text, and then don't need to keep re-generating them.
   Only thing to be careful of would be changeable data, but don't
@@ -661,7 +654,7 @@
     .value = {.building = pimprove}
   };
 
-  assert(buf);
+  assert(NULL != buf && 0 < bufsz);
   buf[0] = '\0';
 
   if (NULL == pimprove) {
@@ -740,15 +733,17 @@
 {
   int count = 0;
 
-  assert(bufsz > 0);
+  assert(NULL != buf && 0 < bufsz);
   buf[0] = '\0';
+
   techs_with_flag_iterate(flag, tech_id) {
     const char *name = advance_name_for_player(game.player_ptr, tech_id);
 
     if (buf[0] == '\0') {
-      cat_snprintf(buf, bufsz, "%s", name);
+      CATLSTR(buf, bufsz, name);
     } else {
-      cat_snprintf(buf, bufsz, ", %s", name);
+      /* TRANS: continue list, in case comma is not the separator of choice. */
+      cat_snprintf(buf, bufsz, Q_("?clistmore:, %s"), name);
     }
     count++;
   } techs_with_flag_iterate_end;
@@ -760,300 +755,300 @@
   Append misc dynamic text for units.
   Transport capacity, unit flags, fuel.
 *****************************************************************/
-void helptext_unit(char *buf, struct unit_type *utype, const char *user_text)
+char *helptext_unit(char *buf, size_t bufsz, struct unit_type *utype,
+                   const char *user_text)
 {
-  assert(buf&&user_text);
+  assert(NULL != buf && 0 < bufsz && NULL != user_text);
+
   if (!utype) {
     freelog(LOG_ERROR, "Unknown unit!");
-    strcpy(buf, user_text);
-    return;
+    mystrlcpy(buf, user_text, bufsz);
+    return buf;
   }
-  
   buf[0] = '\0';
 
-  sprintf(buf + strlen(buf), _("* Belongs to %s units class.\n"),
-          uclass_name_translation(utype_class(utype)));
+  cat_snprintf(buf, bufsz,
+               _("* Belongs to %s units class.\n"),
+               uclass_name_translation(utype_class(utype)));
   if (uclass_has_flag(utype_class(utype), UCF_CAN_OCCUPY)
       && !utype_has_flag(utype, F_CIVILIAN)) {
-    sprintf(buf + strlen(buf), _("  * Can occupy empty enemy cities.\n"));
+    CATLSTR(buf, bufsz, _("  * Can occupy empty enemy cities.\n"));
   }
   if (!uclass_has_flag(utype_class(utype), UCF_TERRAIN_SPEED)) {
-    sprintf(buf + strlen(buf), _("  * Speed is not affected by terrain.\n"));
+    CATLSTR(buf, bufsz, _("  * Speed is not affected by terrain.\n"));
   }
   if (uclass_has_flag(utype_class(utype), UCF_DAMAGE_SLOWS)) {
-    sprintf(buf + strlen(buf), _("  * Slowed down while damaged\n"));
+    CATLSTR(buf, bufsz, _("  * Slowed down while damaged\n"));
   }
   if (uclass_has_flag(utype_class(utype), UCF_MISSILE)) {
-    sprintf(buf + strlen(buf),
-           _("  * Gets used up in making an attack.\n"));
+    CATLSTR(buf, bufsz, _("  * Gets used up in making an attack.\n"));
   }
   if (uclass_has_flag(utype_class(utype), UCF_UNREACHABLE)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("  * Is unreachable. Most units cannot attack this one.\n"));
   }
 
   if (utype->need_improvement) {
-    sprintf(buf + strlen(buf),
-           _("* Can only be built if there is %s in the city.\n"), 
-            improvement_name_translation(utype->need_improvement));
+    cat_snprintf(buf, bufsz,
+                 _("* Can only be built if there is %s in the city.\n"),
+                 improvement_name_translation(utype->need_improvement));
   }
 
   if (utype->need_government) {
-    sprintf(buf + strlen(buf),
-           _("* Can only be built with %s as government.\n"), 
-            government_name_translation(utype->need_government));
+    cat_snprintf(buf, bufsz,
+                 _("* Can only be built with %s as government.\n"),
+                 government_name_translation(utype->need_government));
   }
   
   if (utype_has_flag(utype, F_NOBUILD)) {
-    sprintf(buf + strlen(buf),
-           _("* May not be built in cities.\n"));
+    CATLSTR(buf, bufsz, _("* May not be built in cities.\n"));
   }
   if (utype_has_flag(utype, F_BARBARIAN_ONLY)) {
-    sprintf(buf + strlen(buf),
-           _("* Only barbarians may build this.\n"));
+    CATLSTR(buf, bufsz, _("* Only barbarians may build this.\n"));
   }
   if (utype_has_flag(utype, F_NOHOME)) {
-    sprintf(buf + strlen(buf), _("* Never has a home city.\n"));
+    CATLSTR(buf, bufsz, _("* Never has a home city.\n"));
   }
   if (utype_has_flag(utype, F_GAMELOSS)) {
-    sprintf(buf + strlen(buf),
-           _("* Losing this unit will lose you the game!\n"));
+    CATLSTR(buf, bufsz, _("* Losing this unit will lose you the game!\n"));
   }
   if (utype_has_flag(utype, F_UNIQUE)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Each player may only have one of this type of unit.\n"));
   }
   if (utype->pop_cost > 0) {
-    sprintf(buf + strlen(buf), _("* Requires %d population to build.\n"),
-           utype->pop_cost);
+    cat_snprintf(buf, bufsz,
+                 _("* Requires %d population to build.\n"),
+                 utype->pop_cost);
   }
   if (utype->transport_capacity > 0) {
-    sprintf(buf + strlen(buf),
-            PL_("* Can carry and refuel %d unit from classes:\n",
-                "* Can carry and refuel up to %d units from classes:\n",
-                utype->transport_capacity), utype->transport_capacity);
+    cat_snprintf(buf, bufsz,
+                 PL_("* Can carry and refuel %d unit from classes:\n",
+                     "* Can carry and refuel up to %d units from classes:\n",
+                     utype->transport_capacity),
+                 utype->transport_capacity);
     unit_class_iterate(uclass) {
       if (can_unit_type_transport(utype, uclass)) {
-        sprintf(buf + strlen(buf), _("  * %s units\n"),
-                                   uclass_name_translation(uclass));
+        cat_snprintf(buf, bufsz,
+                     _("  * %s units\n"),
+                     uclass_name_translation(uclass));
       }
     } unit_class_iterate_end
   }
   if (utype_has_flag(utype, F_TRADE_ROUTE)) {
     /* TRANS: "Manhattan" distance is the distance along gridlines, with
      * no diagonals allowed. */
-    sprintf(buf + strlen(buf), _("* Can establish trade routes (must travel "
-                                "to target city and must be at least 9 "
-                                "tiles [in Manhattan distance] from this "
-                                "unit's home city).\n"));
+    CATLSTR(buf, bufsz,
+           _("* Can establish trade routes (must travel to target city"
+             " and must be at least 9 tiles [in Manhattan distance] from"
+             " this unit's home city).\n"));
   }
   if (utype_has_flag(utype, F_HELP_WONDER)) {
-    sprintf(buf + strlen(buf),
-           _("* Can help build wonders (adds %d production).\n"),
-           utype_build_shield_cost(utype));
+    cat_snprintf(buf, bufsz,
+                _("* Can help build wonders (adds %d production).\n"),
+                utype_build_shield_cost(utype));
   }
   if (utype_has_flag(utype, F_UNDISBANDABLE)) {
-    sprintf(buf + strlen(buf), _("* May not be disbanded.\n"));
+    CATLSTR(buf, bufsz, _("* May not be disbanded.\n"));
   } else {
-    sprintf(buf + strlen(buf), _("* May be disbanded in a city to "
-                                "recover 50%% of the production cost.\n"));
+    CATLSTR(buf, bufsz,
+           _("* May be disbanded in a city to recover 50% of the"
+             " production cost.\n"));
   }
   if (utype_has_flag(utype, F_CITIES)) {
-    sprintf(buf + strlen(buf), _("* Can build new cities.\n"));
+    CATLSTR(buf, bufsz, _("* Can build new cities.\n"));
   }
   if (utype_has_flag(utype, F_ADD_TO_CITY)) {
-    sprintf(buf + strlen(buf), _("* Can add on %d population to "
-                                "cities of no more than size %d.\n"),
-           utype_pop_value(utype),
-           game.info.add_to_size_limit - utype_pop_value(utype));
+    cat_snprintf(buf, bufsz,
+                _("* Can add on %d population to cities of no more than"
+                  " size %d.\n"),
+                utype_pop_value(utype),
+                game.info.add_to_size_limit - utype_pop_value(utype));
   }
   if (utype_has_flag(utype, F_SETTLERS)) {
     char buf2[1024];
 
     /* Roads, rail, mines, irrigation. */
-    sprintf(buf + strlen(buf), _("* Can build roads and railroads.\n"));
-    sprintf(buf + strlen(buf), _("* Can build mines on tiles.\n"));
-    sprintf(buf + strlen(buf), _("* Can build irrigation on tiles.\n"));
+    CATLSTR(buf, bufsz, _("* Can build roads and railroads.\n"));
+    CATLSTR(buf, bufsz, _("* Can build mines on tiles.\n"));
+    CATLSTR(buf, bufsz, _("* Can build irrigation on tiles.\n"));
 
     /* Farmland. */
     switch (techs_with_flag_string(TF_FARMLAND, buf2, sizeof(buf2))) {
     case 0:
-      sprintf(buf + strlen(buf), _("* Can build farmland.\n"));
+      CATLSTR(buf, bufsz, _("* Can build farmland.\n"));
       break;
     case 1:
-      sprintf(buf + strlen(buf),
-             _("* Can build farmland (if %s is known).\n"), buf2);
+      cat_snprintf(buf, bufsz,
+                  _("* Can build farmland (if %s is known).\n"), buf2);
       break;
     default:
-      sprintf(buf + strlen(buf),
-             _("* Can build farmland (if any of the following are "
-               "known: %s).\n"), buf2);
+      cat_snprintf(buf, bufsz,
+                  _("* Can build farmland (if any of the following are"
+                    " known: %s).\n"), buf2);
       break;
     }
 
     /* Fortress. */
-    sprintf(buf + strlen(buf), _("* Can build fortresses.\n"));
+    CATLSTR(buf, bufsz, _("* Can build fortresses.\n"));
  
     /* Pollution, fallout. */
-    sprintf(buf + strlen(buf), _("* Can clean pollution from tiles.\n"));
-    sprintf(buf + strlen(buf),
-           _("* Can clean nuclear fallout from tiles.\n"));
+    CATLSTR(buf, bufsz, _("* Can clean pollution from tiles.\n"));
+    CATLSTR(buf, bufsz, _("* Can clean nuclear fallout from tiles.\n"));
   }
   if (utype_has_flag(utype, F_TRANSFORM)) {
-    sprintf(buf + strlen(buf), _("* Can transform tiles.\n"));
+    CATLSTR(buf, bufsz, _("* Can transform tiles.\n"));
   }
   if (is_ground_unittype(utype) && !utype_has_flag(utype, F_SETTLERS)) {
-    sprintf(buf + strlen(buf),
-           _("* May fortify, granting a 50%% defensive bonus.\n"));
+    CATLSTR(buf, bufsz, _("* May fortify, granting a 50% defensive bonus.\n"));
   }
   if (is_ground_unittype(utype)) {
-    sprintf(buf + strlen(buf),
-           _("* May pillage to destroy infrastructure from tiles.\n"));
+    CATLSTR(buf, bufsz, _("* May pillage to destroy infrastructure from 
tiles.\n"));
   }
   if (utype_has_flag(utype, F_DIPLOMAT)) {
     if (utype_has_flag(utype, F_SPY)) {
-      sprintf(buf + strlen(buf), _("* Can perform diplomatic actions,"
-                                  " plus special spy abilities.\n"));
+      CATLSTR(buf, bufsz, _("* Can perform diplomatic actions,"
+                           " plus special spy abilities.\n"));
     } else {
-      sprintf(buf + strlen(buf), _("* Can perform diplomatic actions.\n"));
+      CATLSTR(buf, bufsz, _("* Can perform diplomatic actions.\n"));
     }
   }
   if (utype_has_flag(utype, F_SUPERSPY)) {
-    sprintf(buf + strlen(buf), _("* Will never lose a "
-                                "diplomat-versus-diplomat fight.\n"));
+    CATLSTR(buf, bufsz, _("* Will never lose a diplomat-versus-diplomat 
fight.\n"));
   }
   if (utype_has_flag(utype, F_UNBRIBABLE)) {
-    sprintf(buf + strlen(buf), _("* May not be bribed.\n"));
+    CATLSTR(buf, bufsz, _("* May not be bribed.\n"));
   }
   if (utype_has_flag(utype, F_ATTACK_ANY)) {
-    sprintf(buf + strlen(buf), _("* Can attack otherwise unreachable enemy 
units.\n"));
+    CATLSTR(buf, bufsz, _("* Can attack otherwise unreachable enemy 
units.\n"));
   }
   if (utype_has_flag(utype, F_PARTIAL_INVIS)) {
-    sprintf(buf + strlen(buf), _("* Is invisible except when next to an"
-                                " enemy unit or city.\n"));
+    CATLSTR(buf, bufsz,
+            _("* Is invisible except when next to an enemy unit or city.\n"));
   }
   if (utype_has_flag(utype, F_NO_LAND_ATTACK)) {
-    sprintf(buf + strlen(buf), _("* Can only attack units on ocean squares"
-                                " (no land attacks).\n"));
+    CATLSTR(buf, bufsz,
+            _("* Can only attack units on ocean squares (no land 
attacks).\n"));
   }
   if (utype_has_flag(utype, F_MARINES)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Can attack from aboard sea units: against"
              " enemy cities and onto land squares.\n"));
   }
   if (utype_has_flag(utype, F_PARATROOPERS)) {
-    sprintf(buf + strlen(buf),
-           _("* Can be paradropped from a friendly city"
-             " (Range: %d).\n"), utype->paratroopers_range);
+    cat_snprintf(buf, bufsz,
+                _("* Can be paradropped from a friendly city"
+                  " (Range: %d).\n"),
+                utype->paratroopers_range);
   }
   if (utype_has_flag(utype, F_PIKEMEN)) {
-    sprintf(buf + strlen(buf), _("* Gets double defense against units"
-                                " specified as 'mounted'.\n"));
+    CATLSTR(buf, bufsz,
+            _("* Gets double defense against units specified as 
'mounted'.\n"));
   }
   if (utype_has_flag(utype, F_HORSE)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Counts as 'mounted' against certain defenders.\n"));
   }
   if (utype_has_flag(utype, F_HELICOPTER)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
             _("* Counts as 'helicopter' against certain attackers.\n"));
   }
   if (utype_has_flag(utype, F_FIGHTER)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
             _("* Very good at attacking 'helicopter' units.\n"));
   }
   if (utype_has_flag(utype, F_AIRUNIT)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
             _("* Very bad at attacking AEGIS units.\n"));
   }
   if (!uclass_has_flag(utype_class(utype), UCF_MISSILE)
       && utype_has_flag(utype, F_ONEATTACK)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Making an attack ends this unit's turn.\n"));
   }
   if (utype_has_flag(utype, F_NUCLEAR)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* This unit's attack causes a nuclear explosion!\n"));
   }
   if (utype_has_flag(utype, F_CITYBUSTER)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Gets double firepower when attacking cities.\n"));
   }
   if (utype_has_flag(utype, F_IGWALL)) {
-    sprintf(buf + strlen(buf), _("* Ignores the effects of city walls.\n"));
+    CATLSTR(buf, bufsz, _("* Ignores the effects of city walls.\n"));
   }
   if (utype_has_flag(utype, F_BOMBARDER)) {
-    sprintf(buf + strlen(buf),
-           _("* Does bombard attacks (%d per turn).  These attacks will "
-             "only damage (never kill) the defender but has no risk to "
-             "the attacker.\n"), utype->bombard_rate);
+    cat_snprintf(buf, bufsz,
+                _("* Does bombard attacks (%d per turn).  These attacks will"
+                  " only damage (never kill) the defender, but has no risk to"
+                  " the attacker.\n"),
+                utype->bombard_rate);
   }
   if (utype_has_flag(utype, F_AEGIS)) {
-    sprintf(buf + strlen(buf),
-           _("* Gets quintuple defence against missiles and aircraft.\n"));
+    CATLSTR(buf, bufsz,
+           _("* Gets quintuple defense against missiles and aircraft.\n"));
   }
   if (utype_has_flag(utype, F_IGTER)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Ignores terrain effects (treats all squares as roads).\n"));
   }
   if (utype_has_flag(utype, F_IGZOC)) {
-    sprintf(buf + strlen(buf), _("* Ignores zones of control.\n"));
+    CATLSTR(buf, bufsz, _("* Ignores zones of control.\n"));
   }
   if (utype_has_flag(utype, F_CIVILIAN)) {
-    sprintf(buf + strlen(buf), _("* A non-military unit"
-                                " (cannot attack; no martial law).\n"));
+    CATLSTR(buf, bufsz,
+            _("* A non-military unit (cannot attack; no martial law).\n"));
   }
   if (utype_has_flag(utype, F_FIELDUNIT)) {
-    sprintf(buf + strlen(buf), _("* A field unit: one unhappiness applies"
-                                " even when non-aggressive.\n"));
+    CATLSTR(buf, bufsz,
+            _("* A field unit: one unhappiness applies even when 
non-aggressive.\n"));
   }
   if (utype_has_flag(utype, F_NO_VETERAN)) {
-    sprintf(buf + strlen(buf),
-           _("* Will never achieve veteran status.\n"));
+    CATLSTR(buf, bufsz, _("* Will never achieve veteran status.\n"));
   } else {
     switch(utype_move_type(utype)) {
       case AIR_MOVING:
       case HELI_MOVING:
-        sz_strlcat(buf,
-          _("* Will be built as a veteran in cities with appropriate"
-            " training facilities (see Airport.)\n"));
-        sz_strlcat(buf,
-          _("* May be promoted after defeating an enemy unit.\n"));
+        CATLSTR(buf, bufsz,
+                _("* Will be built as a veteran in cities with appropriate"
+                  " training facilities (see Airport).\n"));
+        CATLSTR(buf, bufsz,
+                _("* May be promoted after defeating an enemy unit.\n"));
         break;
       case LAND_MOVING:
         if (utype_has_flag(utype, F_DIPLOMAT)||utype_has_flag(utype, F_SPY)) {
-          sz_strlcat(buf,
-            _("* Will be built as a veteran under communist governments.\n"));
-          sz_strlcat(buf,
-            _("* May be promoted after a successful mission.\n"));
+          CATLSTR(buf, bufsz,
+                  _("* Will be built as a veteran under communist 
governments.\n"));
+          CATLSTR(buf, bufsz,
+                  _("* May be promoted after a successful mission.\n"));
         } else {
-          sz_strlcat(buf,
-            _("* Will be built as a veteran in cities with appropriate"
-              " training facilities (see Barracks.)\n"));
-          sz_strlcat(buf,
-            _("* May be promoted after defeating an enemy unit.\n"));
+          CATLSTR(buf, bufsz,
+                  _("* Will be built as a veteran in cities with appropriate"
+                    " training facilities (see Barracks).\n"));
+          CATLSTR(buf, bufsz,
+                  _("* May be promoted after defeating an enemy unit.\n"));
         }
         break;
       case SEA_MOVING:
-        sz_strlcat(buf,
-          _("* Will be built as a veteran in cities with appropriate"
-            " training facilities (see Port Facility)."));
-        sz_strlcat(buf,
-          _("* May be promoted after defeating an enemy unit.\n"));
+        CATLSTR(buf, bufsz,
+                _("* Will be built as a veteran in cities with appropriate"
+                  " training facilities (see Port Facility).\n"));
+        CATLSTR(buf, bufsz,
+                _("* May be promoted after defeating an enemy unit.\n"));
         if (utype_has_flag(utype, F_TRIREME))
-          sz_strlcat(buf,
-            _("* May be promoted after survival on the high seas.\n"));
+          CATLSTR(buf, bufsz,
+                  _("* May be promoted after survival on the high seas.\n"));
         break;
       default:          /* should never happen in default rulesets */
-        sz_strlcat(buf,
-          _("* May be promoted through combat or training\n"));
+        CATLSTR(buf, bufsz,
+                _("* May be promoted through combat or training\n"));
         break;
     };
   }
   if (utype_has_flag(utype, F_TRIREME)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Must end turn in a city or next to land,"
-             " or has a 50%% risk of being lost at sea.\n"));
+             " or has a 50% risk of being lost at sea.\n"));
   }
   if (utype->fuel > 0) {
     char allowed_units[10][64];
@@ -1082,7 +1077,9 @@
       strcat(astr.str, allowed_units[j]);
 
       if (j == num_allowed_units - 2) {
-       deli_str = _(" or ");
+        /* TRANS: List of possible unit types has this between
+         *        last two elements */
+       deli_str = Q_(" or ");
       } else if (j < num_allowed_units - 1) {
        deli_str = Q_("?or:, ");
       }
@@ -1095,22 +1092,25 @@
     
     assert(num_allowed_units > 0);
 
-    sprintf(buf + strlen(buf),
-           PL_("* Unit has to be in a city, or on a %s"
-               " after %d turn.\n",
-               "* Unit has to be in a city, or on a %s"
-               " after %d turns.\n", utype->fuel),
-           astr.str, utype->fuel);
+    cat_snprintf(buf, bufsz,
+                 PL_("* Unit has to be in a city, or on a %s"
+                     " after %d turn.\n",
+                     "* Unit has to be in a city, or on a %s"
+                     " after %d turns.\n",
+                     utype->fuel),
+                 astr.str,
+                 utype->fuel);
     astr_free(&astr);
   }
   if (strlen(buf) > 0) {
-    sprintf(buf + strlen(buf), "\n");
+    CATLSTR(buf, bufsz, "\n");
   } 
   if (utype->helptext && utype->helptext[0] != '\0') {
-    sprintf(buf + strlen(buf), "%s\n\n", _(utype->helptext));
+    cat_snprintf(buf, bufsz, "%s\n\n", _(utype->helptext));
   }
-  strcpy(buf + strlen(buf), user_text);
+  CATLSTR(buf, bufsz, user_text);
   wordwrap_string(buf, 68);
+  return buf;
 }
 
 /****************************************************************
@@ -1124,81 +1124,83 @@
     .value = {.advance = vap}
   };
 
-  assert(buf&&user_text);
-  strcpy(buf, user_text);
+  assert(NULL != buf && 0 < bufsz && NULL != user_text);
+  strlcpy(buf, user_text, bufsz);
 
   if (NULL == vap) {
     freelog(LOG_ERROR, "Unknown tech %d.", i);
-    strcpy(buf, user_text);
     return;
   }
 
   if (player_invention_state(game.player_ptr, i) != TECH_KNOWN) {
     if (player_invention_state(game.player_ptr, i) == TECH_REACHABLE) {
-      sprintf(buf + strlen(buf),
-             _("If we would now start with %s we would need %d bulbs."),
-             advance_name_for_player(game.player_ptr, i),
-             base_total_bulbs_required(game.player_ptr, i));
+      cat_snprintf(buf, bufsz,
+                  _("If we would now start with %s we would need %d bulbs."),
+                  advance_name_for_player(game.player_ptr, i),
+                  base_total_bulbs_required(game.player_ptr, i));
     } else if (player_invention_is_ready(game.player_ptr, i)) {
-      sprintf(buf + strlen(buf),
-             _("To reach %s we need to obtain %d other "
-               "technologies first. The whole project "
-               "will require %d bulbs to complete."),
-             advance_name_for_player(game.player_ptr, i),
-             num_unknown_techs_for_goal(game.player_ptr, i) - 1,
-             total_bulbs_required_for_goal(game.player_ptr, i));
+      cat_snprintf(buf, bufsz,
+                  _("To reach %s we need to obtain %d other"
+                    " technologies first. The whole project"
+                    " will require %d bulbs to complete."),
+                  advance_name_for_player(game.player_ptr, i),
+                  num_unknown_techs_for_goal(game.player_ptr, i) - 1,
+                  total_bulbs_required_for_goal(game.player_ptr, i));
     } else {
-      sprintf(buf + strlen(buf),
+      CATLSTR(buf, bufsz,
              _("You cannot research this technology."));
     }
     if (!techs_have_fixed_costs()
      && player_invention_is_ready(game.player_ptr, i)) {
-      sprintf(buf + strlen(buf),
+      CATLSTR(buf, bufsz,
              _(" This number may vary depending on what "
                "other players will research.\n"));
     } else {
-      sprintf(buf + strlen(buf), "\n");
+      CATLSTR(buf, bufsz, "\n");
     }
   }
 
-  sprintf(buf + strlen(buf), "\n");
+  CATLSTR(buf, bufsz, "\n");
   insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
 
   if (advance_has_flag(i, TF_BONUS_TECH)) {
-    sprintf(buf + strlen(buf), _("* The first player to research %s gets "
-                                "an immediate advance.\n"),
-           advance_name_for_player(game.player_ptr, i));
+    cat_snprintf(buf, bufsz,
+                _("* The first player to research %s gets"
+                  " an immediate advance.\n"),
+                advance_name_for_player(game.player_ptr, i));
   }
   if (advance_has_flag(i, TF_POPULATION_POLLUTION_INC))
-    sprintf(buf + strlen(buf), _("* Increases the pollution generated by "
-                                "the population.\n"));
+    CATLSTR(buf, bufsz,
+            _("* Increases the pollution generated by the population.\n"));
 
   if (advance_has_flag(i, TF_BRIDGE)) {
     const char *units_str = role_units_translations(F_SETTLERS);
-    sprintf(buf + strlen(buf), _("* Allows %s to build roads on river "
-                                "squares.\n"), units_str);
+    cat_snprintf(buf, bufsz,
+                _("* Allows %s to build roads on river squares.\n"),
+                units_str);
     free((void *) units_str);
   }
 
   if (advance_has_flag(i, TF_RAILROAD)) {
     const char *units_str = role_units_translations(F_SETTLERS);
-    sprintf(buf + strlen(buf),
-           _("* Allows %s to upgrade roads to railroads.\n"), units_str);
+    cat_snprintf(buf, bufsz,
+                _("* Allows %s to upgrade roads to railroads.\n"),
+                units_str);
     free((void *) units_str);
   }
 
   if (advance_has_flag(i, TF_FARMLAND)) {
     const char *units_str = role_units_translations(F_SETTLERS);
-    sprintf(buf + strlen(buf),
-           _("* Allows %s to upgrade irrigation to farmland.\n"),
-           units_str);
+    cat_snprintf(buf, bufsz,
+                _("* Allows %s to upgrade irrigation to farmland.\n"),
+                units_str);
     free((void *) units_str);
   }
   if (vap->helptext && vap->helptext[0] != '\0') {
     if (strlen(buf) > 0) {
-      sprintf(buf + strlen(buf), "\n");
+      CATLSTR(buf, bufsz, "\n");
     }
-    sprintf(buf + strlen(buf), "%s\n", _(vap->helptext));
+    cat_snprintf(buf, bufsz, "%s\n", _(vap->helptext));
   }
 }
 
@@ -1212,8 +1214,10 @@
     .kind = VUT_TERRAIN,
     .value = {.terrain = pterrain}
   };
+
+  assert(NULL != buf && 0 < bufsz);
   buf[0] = '\0';
-  
+
   if (!pterrain) {
     freelog(LOG_ERROR, "Unknown terrain!");
     return;
@@ -1221,36 +1225,36 @@
 
   insert_allows(&source, buf + strlen(buf), bufsz - strlen(buf));
   if (terrain_has_flag(pterrain, TER_NO_POLLUTION)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Pollution cannot be generated on this terrain."));
-    strcat(buf, "\n");
+    CATLSTR(buf, bufsz, "\n");
   }
   if (terrain_has_flag(pterrain, TER_NO_CITIES)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* You cannot build cities on this terrain."));
-    strcat(buf, "\n");
+    CATLSTR(buf, bufsz, "\n");
   }
   if (terrain_has_flag(pterrain, TER_UNSAFE_COAST)
       && !terrain_has_flag(pterrain, TER_OCEANIC)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* The coastline of this terrain is unsafe."));
-    strcat(buf, "\n");
+    CATLSTR(buf, bufsz, "\n");
   }
   if (terrain_has_flag(pterrain, TER_OCEANIC)) {
-    sprintf(buf + strlen(buf),
+    CATLSTR(buf, bufsz,
            _("* Land units cannot travel on oceanic terrains."));
-    strcat(buf, "\n");
+    CATLSTR(buf, bufsz, "\n");
   }
 
   if (pterrain->helptext[0] != '\0') {
     if (buf[0] != '\0') {
-      strcat(buf, "\n");
+      CATLSTR(buf, bufsz, "\n");
     }
-    sprintf(buf + strlen(buf), "%s", _(pterrain->helptext));
+    CATLSTR(buf, bufsz, _(pterrain->helptext));
   }
   if (user_text && user_text[0] != '\0') {
-    strcat(buf, "\n\n");
-    strcat(buf, user_text);
+    CATLSTR(buf, bufsz, "\n\n");
+    CATLSTR(buf, bufsz, user_text);
   }
   wordwrap_string(buf, 68);
 }
@@ -1269,10 +1273,11 @@
     .value = {.govern = gov}
   };
 
+  assert(NULL != buf && 0 < bufsz);
   buf[0] = '\0';
 
   if (gov->helptext[0] != '\0') {
-    sprintf(buf, "%s\n\n", _(gov->helptext));
+    cat_snprintf(buf, bufsz, "%s\n\n", _(gov->helptext));
   }
 
   /* Add requirement text for government itself */
@@ -1281,7 +1286,7 @@
   } requirement_vector_iterate_end;
 
   /* Effects */
-  sprintf(buf + strlen(buf), _("Features:\n"));
+  CATLSTR(buf, bufsz, _("Features:\n"));
   insert_allows(&source, buf, bufsz);
   effect_list_iterate(get_req_source_effects(&source), peffect) {
     Output_type_id output_type = O_LAST;
@@ -1364,9 +1369,9 @@
           if (!harvested_only || pot->harvested) {
             if (prev2 != NULL) {
               astr_add(&outputs_or,  prev2);
-              astr_add(&outputs_or,  ", ");
+              astr_add(&outputs_or,  Q_("?or:, "));
               astr_add(&outputs_and, prev2);
-              astr_add(&outputs_and, ", ");
+              astr_add(&outputs_and, Q_("?and:, "));
             }
             prev2 = prev;
             prev = _(pot->name);
@@ -1376,11 +1381,11 @@
           astr_add(&outputs_or, prev2);
           /* TRANS: List of possible output types has this between
            *        last two elements */
-          astr_add(&outputs_or,  Q_("?outputlist: or "));
+          astr_add(&outputs_or,  Q_(" or "));
           astr_add(&outputs_and, prev2);
           /* TRANS: List of possible output types has this between
            *        last two elements */
-          astr_add(&outputs_and, Q_("?outputlist: and "));
+          astr_add(&outputs_and, Q_(" and "));
         }
         if (prev != NULL) {
           astr_add(&outputs_or, prev);
@@ -1392,133 +1397,153 @@
         }
       }
 
-    switch (peffect->type) {
+      switch (peffect->type) {
       case EFT_UNHAPPY_FACTOR:
-        if (peffect->value == 1) {
-          sprintf(buf + strlen(buf), _("* Military units away from home and "
-                  "field units will cause one citizen to become unhappy.\n"));
-        } else {
-          sprintf(buf + strlen(buf), _("* Military units away from home and "
-                  "field units will cause %d citizens to become unhappy.\n"),
-                  peffect->value);
-        }
+        cat_snprintf(buf, bufsz,
+                     PL_("* Military units away from home and field units"
+                         " will cause %d citizen to become unhappy.\n",
+                         "* Military units away from home and field units"
+                         " will cause %d citizens to become unhappy.\n",
+                         peffect->value),
+                     peffect->value);
         break;
       case EFT_MAKE_CONTENT:
       case EFT_FORCE_CONTENT:
-        sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                "%d unhappiness that would otherwise be caused by units.\n"),
-                peffect->value);
+        cat_snprintf(buf, bufsz,
+                     _("* Each of your cities will avoid %d unhappiness"
+                       " that would otherwise be caused by units.\n"),
+                     peffect->value);
         break;
       case EFT_UPKEEP_FACTOR:
         if (peffect->value > 1 && output_type != O_LAST) {
-          /* TRANS: %s is always only one output type, never list */
-          sprintf(buf + strlen(buf),
-                  _("* You pay %d times normal %s upkeep for your units.\n"),
-                  peffect->value, outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is the output type, like 'shield' or 
'gold'. */
+                       _("* You pay %d times normal %s upkeep for your 
units.\n"),
+                       peffect->value,
+                       outputs_and.str);
         } else if (peffect->value > 1) {
-          sprintf(buf + strlen(buf), _("* You pay %d times normal "
-                  "upkeep for your units.\n"), peffect->value);
-         } else if (peffect->value == 0 && output_type != O_LAST) {
-          /* TRANS: %s is output type */
-          sprintf(buf + strlen(buf),
-                  _("* You pay no %s upkeep for your units.\n"),
-                  outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       _("* You pay %d times normal upkeep for your units.\n"),
+                       peffect->value);
+        } else if (peffect->value == 0 && output_type != O_LAST) {
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is the output type, like 'shield' or 
'gold'. */
+                       _("* You pay no %s upkeep for your units.\n"),
+                       outputs_and.str);
         } else if (peffect->value == 0) {
-          /* TRANS: No upkeep of any type */
-          sprintf(buf + strlen(buf),
-                   _("* You pay no upkeep for your units.\n"));
+          CATLSTR(buf, bufsz,
+                  _("* You pay no upkeep for your units.\n"));
         }
         break;
       case EFT_UNIT_UPKEEP_FREE_PER_CITY:
         if (output_type != O_LAST) {
-         /* TRANS: %s is the output type, like 'shield' or 'gold'. There
-          * is currently no way to control the singular/plural version of
-          * this. */
-          sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                  "paying %d %s towards unit upkeep.\n"), peffect->value, 
-                  outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is the output type, like 'shield' or 
'gold'.
+                        * There is currently no way to control the
+                        * singular/plural version of these. */
+                       _("* Each of your cities will avoid paying %d %s"
+                         " upkeep for your units.\n"),
+                       peffect->value,
+                       outputs_and.str);
         } else {
-          /* TRANS: Amount is subtracted from upkeep cost in each upkeep
-           *        type. */
-          sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                  "paying %d towards unit upkeep.\n"), peffect->value);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: Amount is subtracted from upkeep cost
+                        * for each upkeep type. */
+                       _("* Each of your cities will avoid paying %d"
+                         " upkeep for your units.\n"),
+                       peffect->value);
         }
         break;
       case EFT_CIVIL_WAR_CHANCE:
-        sprintf(buf + strlen(buf), _("* Chance of civil war is %d%% if you "
-                "lose your capital.\n"), peffect->value);
+        cat_snprintf(buf, bufsz,
+                     _("* If you lose your capital,"
+                       " chance of civil war is %d%%.\n"),
+                     peffect->value);
         break;
       case EFT_EMPIRE_SIZE_BASE:
-        sprintf(buf + strlen(buf), _("* The first unhappy citizen in each "
-                     "city due to civilization size will appear when you have 
%d"
-                     " cities.\n"), peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %d should always be greater than 2. */
+                     _("* When you have %d cities, the first unhappy citizen"
+                       " will appear in each city due to civilization 
size.\n"),
+                     peffect->value);
         break;
       case EFT_EMPIRE_SIZE_STEP:
-        sprintf(buf + strlen(buf), _("* After the first unhappy citizen "
-                "due to civilization size, for each %d additional cities, "
-                "another unhappy citizen will appear.\n"), peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %d should always be greater than 2. */
+                     _("* After the first unhappy citizen due to"
+                       " civilization size, for each %d additional cities"
+                       " another unhappy citizen will appear.\n"),
+                     peffect->value);
         break;
       case EFT_MAX_RATES:
         if (peffect->value < 100 && game.info.changable_tax) {
-          sprintf(buf + strlen(buf), 
-                  _("* The maximum rate you can set for science, "
-                         "gold, or luxuries is %d%%.\n"), peffect->value);
+          cat_snprintf(buf, bufsz,
+                       _("* The maximum rate you can set for science,"
+                          " gold, or luxuries is %d%%.\n"),
+                       peffect->value);
         } else if (game.info.changable_tax) {
-          sprintf(buf + strlen(buf), 
+          CATLSTR(buf, bufsz, 
                   _("* Has unlimited science/gold/luxuries rates.\n"));
         }
         break;
       case EFT_MARTIAL_LAW_EACH:
-        if (peffect->value == 1) {
-          sprintf(buf + strlen(buf), _("* Your units may impose martial "
-                  "law. Each military unit inside a city will force an "
-                  "unhappy citizen to become content.\n"));
-        } else {
-          sprintf(buf + strlen(buf), _("* Your units may impose martial law. "
-                  "Each military unit inside a city will force %d unhappy "
-                  "citizens to become content.\n"), peffect->value);
-        }
+        cat_snprintf(buf, bufsz,
+                     PL_("* Your units may impose martial law."
+                         " Each military unit inside a city will force %d"
+                         " unhappy citizen to become content.\n",
+                         "* Your units may impose martial law."
+                         " Each military unit inside a city will force %d"
+                         " unhappy citizens to become content.\n",
+                         peffect->value),
+                     peffect->value);
         break;
       case EFT_MARTIAL_LAW_MAX:
         if (peffect->value < 100) {
-          sprintf(buf + strlen(buf), _("* A maximum of %d units in each city "
-                  "can enforce martial law.\n"), peffect->value);
+          cat_snprintf(buf, bufsz,
+                       PL_("* A maximum of %d unit in each city can enforce"
+                           " martial law.\n",
+                           "* A maximum of %d units in each city can enforce"
+                           " martial law.\n",
+                           peffect->value),
+                       peffect->value);
         }
         break;
       case EFT_RAPTURE_GROW:
-        sprintf(buf + strlen(buf), _("* You may grow your cities by "
-                "means of celebrations.  Your cities must be at least "
-                "size %d before they can grown in this manner.\n"),
-                peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %d should always be greater than 2. */
+                     _("* You may grow your cities by means of celebrations."
+                       " Your cities must be at least size %d.\n"),
+                     peffect->value);
         break;
       case EFT_UNBRIBABLE_UNITS:
-        sprintf(buf + strlen(buf), _("* Your units cannot be bribed.\n"));
+        CATLSTR(buf, bufsz, _("* Your units cannot be bribed.\n"));
         break;
       case EFT_NO_INCITE:
-        sprintf(buf + strlen(buf), _("* Your cities cannot be incited.\n"));
+        CATLSTR(buf, bufsz, _("* Your cities cannot be incited.\n"));
         break;
       case EFT_REVOLUTION_WHEN_UNHAPPY:
-        sprintf(buf + strlen(buf), _("* Government will fall into anarchy "
-                "if any city is in disorder for more than two turns in "
-                "a row.\n"));
+        CATLSTR(buf, bufsz,
+                _("* If any city is in disorder for more than two turns in a 
row,"
+                  " government will fall into anarchy.\n"));
         break;
       case EFT_HAS_SENATE:
-        sprintf(buf + strlen(buf), _("* Has a senate that may prevent "
-                "declaration of war.\n"));
+        CATLSTR(buf, bufsz,
+                _("* Has a senate that may prevent declaration of war.\n"));
         break;
       case EFT_INSPIRE_PARTISANS:
-        sprintf(buf + strlen(buf), _("* Allows partisans when cities are "
-                "taken by the enemy.\n"));
+        CATLSTR(buf, bufsz,
+                _("* Allows partisans when cities are taken by the enemy.\n"));
         break;
       case EFT_HAPPINESS_TO_GOLD:
-        sprintf(buf + strlen(buf), _("* Buildings that normally confer "
-                "bonuses against unhappiness will instead give gold.\n"));
+        CATLSTR(buf, bufsz,
+                _("* Buildings that normally confer bonuses against"
+                  " unhappiness will instead give gold.\n"));
         break;
       case EFT_FANATICS:
-        sprintf(buf + strlen(buf), _("* Pays no upkeep for fanatics.\n"));
+        CATLSTR(buf, bufsz, _("* Pays no upkeep for fanatics.\n"));
         break;
       case EFT_NO_UNHAPPY:
-        sprintf(buf + strlen(buf), _("* Has no unhappy citizens.\n"));
+        CATLSTR(buf, bufsz, _("* Has no unhappy citizens.\n"));
         break;
       case EFT_VETERAN_BUILD:
         /* FIXME: There could be both class and flag requirement.
@@ -1527,77 +1552,96 @@
          *        flag related string to be at least qualified to allow
          *        different translations? */
         if (unitclass) {
-          sprintf(buf + strlen(buf), _("* Veteran %s units.\n"),
-                  uclass_name_translation(unitclass));
+          cat_snprintf(buf, bufsz,
+                       _("* Veteran %s units.\n"),
+                       uclass_name_translation(unitclass));
         } else if (unittype != NULL) {
-          sprintf(buf + strlen(buf), _("* Veteran %s units.\n"),
-                  utype_name_translation(unittype));
+          cat_snprintf(buf, bufsz,
+                       _("* Veteran %s units.\n"),
+                       utype_name_translation(unittype));
         } else if (unitflag != F_LAST) {
-          sprintf(buf + strlen(buf), _("* Veteran %s units.\n"),
-                  unit_flag_rule_name(unitflag));
+          cat_snprintf(buf, bufsz,
+                       _("* Veteran %s units.\n"),
+                       unit_flag_rule_name(unitflag));
         } else {
-          sprintf(buf + strlen(buf), _("* Veteran units.\n"));
+          CATLSTR(buf, bufsz, _("* Veteran units.\n"));
         }
         break;
       case EFT_OUTPUT_PENALTY_TILE:
-        /* TRANS: %s is list of output types, with 'or' */
-        sprintf(buf + strlen(buf), _("* Each worked tile that gives more "
-                "than %d %s will suffer a -1 penalty when not "
-                "celebrating.\n"), peffect->value, outputs_or.str);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %s is list of output types, with 'or' */
+                     _("* Each worked tile that gives more than %d %s will"
+                       " suffer a -1 penalty unless celebrating.\n"),
+                     peffect->value,
+                     outputs_or.str);
         break;
       case EFT_OUTPUT_INC_TILE_CELEBRATE:
-        /* TRANS: %s is list of output types, with 'or' */
-        sprintf(buf + strlen(buf), _("* Each worked tile with at least 1 "
-                "%s will yield %d more of it when celebrating.\n"),
-                outputs_or.str, peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %s is list of output types, with 'or' */
+                     _("* Each worked tile with at least 1 %s will yield"
+                       " %d more of it while celebrating.\n"),
+                     outputs_or.str,
+                     peffect->value);
         break;
       case EFT_OUTPUT_INC_TILE:
-        /* TRANS: %s is list of output types, with 'or' */
-        sprintf(buf + strlen(buf), _("* Each worked tile with at least 1 "
-                "%s will yield %d more of it.\n"), outputs_or.str, 
-                peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %s is list of output types, with 'or' */
+                     _("* Each worked tile with at least 1 %s will yield"
+                       " %d more of it.\n"),
+                     outputs_or.str,
+                     peffect->value);
         break;
       case EFT_OUTPUT_BONUS:
       case EFT_OUTPUT_BONUS_2:
-        /* TRANS: %s is list of output types, with 'and' */
-        sprintf(buf + strlen(buf), _("* %s production is increased %d%%.\n"),
-                outputs_and.str, peffect->value);
+        cat_snprintf(buf, bufsz,
+                     /* TRANS: %s is list of output types, with 'and' */
+                     _("* %s production is increased %d%%.\n"),
+                     outputs_and.str,
+                     peffect->value);
         break;
       case EFT_OUTPUT_WASTE:
         if (peffect->value > 30) {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "massive waste.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s production will suffer massive waste.\n"),
+                       outputs_and.str);
         } else if (peffect->value >= 15) {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "some waste.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s production will suffer some waste.\n"),
+                       outputs_and.str);
         } else {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "a small amount of waste.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s production will suffer a small amount of 
waste.\n"),
+                       outputs_and.str);
         }
         break;
       case EFT_OUTPUT_WASTE_BY_DISTANCE:
         if (peffect->value >= 3) {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s waste will increase quickly "
-                  "with distance from capital.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s waste will increase quickly"
+                         " with distance from capital.\n"),
+                       outputs_and.str);
         } else if (peffect->value == 2) {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s waste will increase "
-                  "with distance from capital.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s waste will increase"
+                         " with distance from capital.\n"),
+                       outputs_and.str);
         } else {
-          /* TRANS: %s is list of output types, with 'and' */
-          sprintf(buf + strlen(buf), _("* %s waste will increase slowly "
-                  "with distance from capital.\n"), outputs_and.str);
+          cat_snprintf(buf, bufsz,
+                       /* TRANS: %s is list of output types, with 'and' */
+                       _("* %s waste will increase slowly"
+                         " with distance from capital.\n"),
+                       outputs_and.str);
         }
       default:
         break;
+      };
     }
 
-    }
-
     astr_clear(&outputs_or);
     astr_clear(&outputs_and);
 
@@ -1605,12 +1649,13 @@
 
   unit_type_iterate(utype) {
     if (utype->need_government == gov) {
-      sprintf(buf + strlen(buf),
-             _("* Allows you to build %s.\n"),
-             utype_name_translation(utype));
+      cat_snprintf(buf, bufsz,
+                   _("* Allows you to build %s.\n"),
+                   utype_name_translation(utype));
     }
   } unit_type_iterate_end;
-  strcat(buf, user_text);
+
+  CATLSTR(buf, bufsz, user_text);
   wordwrap_string(buf, 68);
 }
 
@@ -1633,7 +1678,7 @@
     if (utype->upkeep[o] > 0) {
       /* TRANS: "2 Food" or ", 1 shield" */
       cat_snprintf(buf, sizeof(buf), _("%s%d %s"),
-             (any > 0 ? ", " : ""), utype->upkeep[o],
+             (any > 0 ? Q_("?blistmore:, ") : ""), utype->upkeep[o],
              get_output_name(o));
       any++;
     }
@@ -1641,7 +1686,7 @@
   if (utype->happy_cost > 0) {
     /* TRANS: "2 unhappy" or ", 1 unhappy" */
     cat_snprintf(buf, sizeof(buf), _("%s%d unhappy"),
-           (any > 0 ? ", " : ""), utype->happy_cost);
+           (any > 0 ? Q_("?blistmore:, ") : ""), utype->happy_cost);
     any++;
   }
 
Index: client/helpdata.h
===================================================================
--- client/helpdata.h   (revision 14228)
+++ client/helpdata.h   (working copy)
@@ -41,8 +41,8 @@
 char *helptext_building(char *buf, size_t bufsz,
                        struct impr_type *pimprove,
                        const char *user_text);
-void helptext_unit(char *buf, struct unit_type *punittype,
-                  const char *user_text);
+char *helptext_unit(char *buf, size_t bufsz, struct unit_type *putype,
+                   const char *user_text);
 void helptext_tech(char *buf, size_t bufsz, int i, const char *user_text);
 void helptext_terrain(char *buf, size_t bufsz, struct terrain *pterrain,
                      const char *user_text);
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to