<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