737 changes: 368 additions & 369 deletions po/af.po

Large diffs are not rendered by default.

733 changes: 365 additions & 368 deletions po/am.po

Large diffs are not rendered by default.

999 changes: 571 additions & 428 deletions po/ast.po

Large diffs are not rendered by default.

775 changes: 397 additions & 378 deletions po/az.po

Large diffs are not rendered by default.

968 changes: 549 additions & 419 deletions po/be.po

Large diffs are not rendered by default.

832 changes: 440 additions & 392 deletions po/bg.po

Large diffs are not rendered by default.

1,111 changes: 658 additions & 453 deletions po/ca.po

Large diffs are not rendered by default.

972 changes: 551 additions & 421 deletions po/cs.po

Large diffs are not rendered by default.

1,257 changes: 778 additions & 479 deletions po/da.po

Large diffs are not rendered by default.

1,309 changes: 816 additions & 493 deletions po/de.po

Large diffs are not rendered by default.

1,041 changes: 602 additions & 439 deletions po/el.po

Large diffs are not rendered by default.

1,182 changes: 721 additions & 461 deletions po/en_GB.po

Large diffs are not rendered by default.

1,272 changes: 788 additions & 484 deletions po/es.po

Large diffs are not rendered by default.

946 changes: 532 additions & 414 deletions po/et.po

Large diffs are not rendered by default.

822 changes: 432 additions & 390 deletions po/eu.po

Large diffs are not rendered by default.

987 changes: 562 additions & 425 deletions po/fi.po

Large diffs are not rendered by default.

1,388 changes: 880 additions & 508 deletions po/fr.po

Large diffs are not rendered by default.

977 changes: 554 additions & 423 deletions po/gl.po

Large diffs are not rendered by default.

879 changes: 477 additions & 402 deletions po/gu.po

Large diffs are not rendered by default.

886 changes: 480 additions & 406 deletions po/hi.po

Large diffs are not rendered by default.

1,126 changes: 671 additions & 455 deletions po/hu.po

Large diffs are not rendered by default.

1,120 changes: 665 additions & 455 deletions po/id.po

Large diffs are not rendered by default.

1,366 changes: 861 additions & 505 deletions po/it.po

Large diffs are not rendered by default.

984 changes: 563 additions & 421 deletions po/ja_JP.po

Large diffs are not rendered by default.

1,003 changes: 574 additions & 429 deletions po/kn.po

Large diffs are not rendered by default.

1,219 changes: 745 additions & 474 deletions po/ko.po

Large diffs are not rendered by default.

1,083 changes: 636 additions & 447 deletions po/lt.po

Large diffs are not rendered by default.

871 changes: 469 additions & 402 deletions po/lv.po

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions po/meson.build
@@ -1 +1,9 @@
i18n.gettext('hexchat', preset: 'glib')

validate_translations = find_program('validate-textevent-translations')
test('Validate translations', validate_translations,
args: [
join_paths(meson.current_source_dir(), 'LINGUAS'),
meson.current_source_dir(),
]
)
961 changes: 543 additions & 418 deletions po/mk.po

Large diffs are not rendered by default.

736 changes: 367 additions & 369 deletions po/ml.po

Large diffs are not rendered by default.

756 changes: 383 additions & 373 deletions po/ms.po

Large diffs are not rendered by default.

1,252 changes: 773 additions & 479 deletions po/nb.po

Large diffs are not rendered by default.

976 changes: 552 additions & 424 deletions po/nl.po

Large diffs are not rendered by default.

743 changes: 373 additions & 370 deletions po/no.po

Large diffs are not rendered by default.

785 changes: 405 additions & 380 deletions po/pa.po

Large diffs are not rendered by default.

1,233 changes: 758 additions & 475 deletions po/pl.po

Large diffs are not rendered by default.

1,219 changes: 745 additions & 474 deletions po/pt.po

Large diffs are not rendered by default.

1,288 changes: 801 additions & 487 deletions po/pt_BR.po

Large diffs are not rendered by default.

1,076 changes: 632 additions & 444 deletions po/ru.po

Large diffs are not rendered by default.

733 changes: 365 additions & 368 deletions po/rw.po

Large diffs are not rendered by default.

903 changes: 494 additions & 409 deletions po/sk.po

Large diffs are not rendered by default.

935 changes: 520 additions & 415 deletions po/sl.po

Large diffs are not rendered by default.

1,271 changes: 791 additions & 480 deletions po/sq.po

Large diffs are not rendered by default.

962 changes: 543 additions & 419 deletions po/sr.po

Large diffs are not rendered by default.

912 changes: 502 additions & 410 deletions po/sr@latin.po

Large diffs are not rendered by default.

1,238 changes: 762 additions & 476 deletions po/sv.po

Large diffs are not rendered by default.

933 changes: 523 additions & 410 deletions po/th.po

Large diffs are not rendered by default.

1,254 changes: 773 additions & 481 deletions po/tr.po

Large diffs are not rendered by default.

989 changes: 564 additions & 425 deletions po/uk.po

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions po/validate-textevent-translations
@@ -0,0 +1,69 @@
#!/usr/bin/env python3

import os
import re
import sys

ret = 0


def log(prefix, suffix, fallback):
try:
print(prefix, suffix) # Non-utf8 output...
except UnicodeEncodeError:
print(prefix, fallback)


def validate_translation(input, translation):
if not translation:
return True

if re.findall(r'(?:(?<!%)%[^%OCRUHBIH]|\$[^at1234])', translation):
log('Translation includes invalid formatting:', translation, input)
return False

in_vars = re.findall(r'(\$(?:\d|t))', input)
if not all(var in translation for var in in_vars):
log('Translation does not contain all variables:', translation, input)
return False

in_ascii = re.findall(r'\$a(\d{3})', translation)
if any(int(i) > 256 for i in in_ascii):
log('Translation contains invalid ascii value:', translation, input)
return False

# We could try to validate colors but that is pretty flexible
return True


def validate_language(path):
global ret

print('Validating', path)

with open(path, 'r', encoding='utf-8') as f:
in_event = False
event_input = ''

for line in f:
if 'textevents.h' in line:
in_event = True
elif in_event is False:
continue
elif line.startswith('msgid'):
event_input = line[7:-2]
elif line.startswith('msgstr'):
if not validate_translation(event_input, line[8:-2]):
ret = 1
in_event = False
elif line == '\n':
print('Failed to find translation for', event_input)
in_event = False


with open(sys.argv[1], 'r') as linguas:
for lang in linguas:
path = os.path.join(sys.argv[2], lang.strip() + '.po')
validate_language(path)

sys.exit(ret)
1,109 changes: 664 additions & 445 deletions po/vi.po

Large diffs are not rendered by default.

747 changes: 376 additions & 371 deletions po/wa.po

Large diffs are not rendered by default.

1,049 changes: 619 additions & 430 deletions po/zh_CN.po

Large diffs are not rendered by default.

862 changes: 464 additions & 398 deletions po/zh_TW.po

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/common/outbound.c
Expand Up @@ -2610,7 +2610,7 @@ cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
}

#ifdef USE_PLUGIN
if (g_str_has_suffix (word[2], "."G_MODULE_SUFFIX))
if (g_str_has_suffix (word[2], "."PLUGIN_SUFFIX))
{
arg = NULL;
if (word_eol[3][0])
Expand Down Expand Up @@ -3616,7 +3616,7 @@ cmd_unload (struct session *sess, char *tbuf, char *word[], char *word_eol[])
#ifdef USE_PLUGIN
gboolean by_file = FALSE;

if (g_str_has_suffix (word[2], "."G_MODULE_SUFFIX))
if (g_str_has_suffix (word[2], "."PLUGIN_SUFFIX))
by_file = TRUE;

switch (plugin_kill (word[2], by_file))
Expand All @@ -3641,7 +3641,7 @@ cmd_reload (struct session *sess, char *tbuf, char *word[], char *word_eol[])
#ifdef USE_PLUGIN
gboolean by_file = FALSE;

if (g_str_has_suffix (word[2], "."G_MODULE_SUFFIX))
if (g_str_has_suffix (word[2], "."PLUGIN_SUFFIX))
by_file = TRUE;

switch (plugin_reload (sess, word[2], by_file))
Expand Down
6 changes: 3 additions & 3 deletions src/common/plugin.c
Expand Up @@ -464,10 +464,10 @@ plugin_auto_load (session *sess)
for_files (lib_dir, "hcwinamp.dll", plugin_auto_load_cb);
for_files (lib_dir, "hcsysinfo.dll", plugin_auto_load_cb);
#else
for_files (lib_dir, "*."G_MODULE_SUFFIX, plugin_auto_load_cb);
for_files (lib_dir, "*."PLUGIN_SUFFIX, plugin_auto_load_cb);
#endif

for_files (sub_dir, "*."G_MODULE_SUFFIX, plugin_auto_load_cb);
for_files (sub_dir, "*."PLUGIN_SUFFIX, plugin_auto_load_cb);

g_free (sub_dir);
}
Expand Down Expand Up @@ -2010,7 +2010,7 @@ hexchat_pluginpref_list (hexchat_plugin *pl, char* dest)
else /* existing config file, get list of settings */
{
strcpy (dest, ""); /* clean up garbage */
while (fscanf (fpIn, " %[^\n]", bufp) != EOF) /* read whole lines including whitespaces */
while (fscanf (fpIn, " %511[^\n]", bufp) != EOF) /* read whole lines including whitespaces */
{
token = strtok (buffer, "=");
g_strlcat (dest, g_strchomp (token), 4096); /* Dest must not be smaller than this */
Expand Down
8 changes: 8 additions & 0 deletions src/common/plugin.h
Expand Up @@ -181,4 +181,12 @@ int plugin_show_help (session *sess, char *cmd);
void plugin_command_foreach (session *sess, void *userdata, void (*cb) (session *sess, void *userdata, char *name, char *usage));
session *plugin_find_context (const char *servname, const char *channel, server *current_server);

/* On macOS, G_MODULE_SUFFIX says "so" but meson uses "dylib"
* https://github.com/mesonbuild/meson/issues/1160 */
#if defined(__APPLE__)
# define PLUGIN_SUFFIX "dylib"
#else
# define PLUGIN_SUFFIX G_MODULE_SUFFIX
#endif

#endif
4 changes: 3 additions & 1 deletion src/common/proto-irc.c
Expand Up @@ -53,7 +53,9 @@ irc_login (server *serv, char *user, char *realname)

if (serv->password[0] && serv->loginmethod == LOGIN_PASS)
{
tcp_sendf (serv, "PASS %s\r\n", serv->password);
tcp_sendf (serv, "PASS %s%s\r\n",
(serv->password[0] == ':' || strchr (serv->password, ' ')) ? ":" : "",
serv->password);
}

tcp_sendf (serv,
Expand Down
2 changes: 1 addition & 1 deletion src/common/server.c
Expand Up @@ -748,14 +748,14 @@ server_connect_success (server *serv)

/* it'll be a memory leak, if connection isn't terminated by
server_cleanup() */
serv->ssl = _SSL_socket (serv->ctx, serv->sok);
if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify, NULL)))
{
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, err, NULL,
NULL, NULL, 0);
server_cleanup (serv); /* ->connecting = FALSE */
return;
}
serv->ssl = _SSL_socket (serv->ctx, serv->sok);
/* FIXME: it'll be needed by new servers */
/* send(serv->sok, "STLS\r\n", 6, 0); sleep(1); */
set_nonblocking (serv->sok);
Expand Down
2 changes: 1 addition & 1 deletion src/common/textevents.in
Expand Up @@ -325,7 +325,7 @@ pevt_generic_none_help
DCC Malformed
XP_TE_MALFORMED
pevt_malformed_help
%C20*%O$tReceived a malformed DCC request from %C18$1%O.%010%C23*%O$tContents of packet: %C23$2%O
%C20*%O$tReceived a malformed DCC request from %C18$1%O.$a010%C23*%O$tContents of packet: %C23$2%O
2

DCC Offer
Expand Down
4 changes: 2 additions & 2 deletions src/fe-gtk/maingui.c
Expand Up @@ -1585,8 +1585,8 @@ mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
static gboolean
mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event)
{
/* middle-click or shift-click to close a tab */
if (((prefs.hex_gui_tab_middleclose && event->button == 2) || (event->button == 1 && event->state & STATE_SHIFT))
/* middle-click to close a tab */
if (((prefs.hex_gui_tab_middleclose && event->button == 2))
&& event->type == GDK_BUTTON_PRESS)
{
mg_xbutton_cb (cv, ch, tag, ud);
Expand Down
1 change: 1 addition & 0 deletions src/fe-gtk/plugin-tray.c
Expand Up @@ -359,6 +359,7 @@ tray_toggle_visibility (gboolean force_hide)
if (fullscreen)
gtk_window_fullscreen (win);
gtk_widget_show (GTK_WIDGET (win));
gtk_window_deiconify (win);
gtk_window_present (win);
}

Expand Down
4 changes: 2 additions & 2 deletions src/fe-gtk/plugingui.c
Expand Up @@ -162,7 +162,7 @@ plugingui_load (void)
char *sub_dir = g_build_filename (get_xdir(), "addons", NULL);

gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, current_sess,
sub_dir, "*."G_MODULE_SUFFIX";*.lua;*.pl;*.py;*.tcl;*.js", FRF_FILTERISINITIAL|FRF_EXTENSIONS);
sub_dir, "*."PLUGIN_SUFFIX";*.lua;*.pl;*.py;*.tcl;*.js", FRF_FILTERISINITIAL|FRF_EXTENSIONS);

g_free (sub_dir);
}
Expand All @@ -185,7 +185,7 @@ plugingui_unload (GtkWidget * wid, gpointer unused)
FILEPATH_COLUMN, &file, -1))
return;

if (g_str_has_suffix (file, "."G_MODULE_SUFFIX))
if (g_str_has_suffix (file, "."PLUGIN_SUFFIX))
{
if (plugin_kill (modname, FALSE) == 2)
fe_message (_("That plugin is refusing to unload.\n"), FE_MSG_ERROR);
Expand Down
27 changes: 2 additions & 25 deletions src/fe-gtk/xtext.c
Expand Up @@ -157,10 +157,7 @@ static char * gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** r
#define EMPH_BOLD 2
#define EMPH_HIDDEN 4
static PangoAttrList *attr_lists[4];
#ifdef G_OS_WIN32
/* the fontwidths variable is used on Windows only. */
static int fontwidths[4][128];
#endif

static PangoAttribute *
xtext_pango_attr (PangoAttribute *attr)
Expand All @@ -173,10 +170,7 @@ xtext_pango_attr (PangoAttribute *attr)
static void
xtext_pango_init (GtkXText *xtext)
{
int i;
#ifdef G_OS_WIN32
int j;
#endif
int i, j;
char buf[2] = "\000";

if (attr_lists[0])
Expand Down Expand Up @@ -208,7 +202,6 @@ xtext_pango_init (GtkXText *xtext)
break;
}

#ifdef G_OS_WIN32
/* Now initialize fontwidths[i] */
pango_layout_set_attributes (xtext->layout, attr_lists[i]);
for (j = 0; j < 128; j++)
Expand All @@ -217,14 +210,8 @@ xtext_pango_init (GtkXText *xtext)
pango_layout_set_text (xtext->layout, buf, 1);
pango_layout_get_pixel_size (xtext->layout, &fontwidths[i][j], NULL);
}
#endif
}

/* re-compute space_width without using fontwidths */
pango_layout_set_attributes (xtext->layout, attr_lists[0]);
buf[0] = ' ';
pango_layout_set_text (xtext->layout, buf, 1);
pango_layout_get_pixel_size (xtext->layout, &(xtext->space_width), NULL);
xtext->space_width = fontwidths[0][' '];
}

static void
Expand Down Expand Up @@ -302,10 +289,8 @@ static int
backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis)
{
int width;
#ifdef G_OS_WIN32
int deltaw;
int mbl;
#endif

if (*str == 0)
return 0;
Expand All @@ -316,8 +301,6 @@ backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis

width = 0;
pango_layout_set_attributes (xtext->layout, attr_lists[emphasis]);

#ifdef G_OS_WIN32
while (len > 0)
{
mbl = charlen(str);
Expand All @@ -332,12 +315,6 @@ backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis
str += mbl;
len -= mbl;
}
#else
/* This code is slow on Windows,
but it will get the correct width from pango on Linux. */
pango_layout_set_text (xtext->layout, str, len);
pango_layout_get_pixel_size (xtext->layout, &width, NULL);
#endif

return width;
}
Expand Down
12 changes: 7 additions & 5 deletions win32/copy/copy.vcxproj
Expand Up @@ -26,16 +26,18 @@
<None Include="$(DepsRoot)\bin\cert.pem" />
<None Include="$(DepsRoot)\bin\atk-1.0-0.dll" />
<None Include="$(DepsRoot)\bin\cairo.dll" />
<None Include="$(DepsRoot)\bin\freetype.dll" />
<None Include="$(DepsRoot)\bin\fribidi-0.dll" />
<None Include="$(DepsRoot)\bin\fontconfig.dll" />
<None Include="$(DepsRoot)\bin\gdk_pixbuf-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\gdk-win32-2.0.dll" />
<None Include="$(DepsRoot)\bin\gio-2.0.dll" />
<None Include="$(DepsRoot)\bin\glib-2.0.dll" />
<None Include="$(DepsRoot)\bin\gmodule-2.0.dll" />
<None Include="$(DepsRoot)\bin\gobject-2.0.dll" />
<None Include="$(DepsRoot)\bin\gio-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\glib-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\gmodule-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\gobject-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\gspawn-win$(PlatformArchitecture)-helper.exe" />
<None Include="$(DepsRoot)\bin\gspawn-win$(PlatformArchitecture)-helper-console.exe" />
<None Include="$(DepsRoot)\bin\gthread-2.0.dll" />
<None Include="$(DepsRoot)\bin\gthread-2.0-0.dll" />
<None Include="$(DepsRoot)\bin\gtk-win32-2.0.dll" />
<None Include="$(DepsRoot)\bin\iconv.dll" />
<None Include="$(DepsRoot)\bin\libeay32.dll" />
Expand Down
12 changes: 7 additions & 5 deletions win32/installer/hexchat.iss.tt
Expand Up @@ -119,21 +119,23 @@ Source: "etc\fonts\*"; DestDir: "{app}\etc\fonts"; Flags: ignoreversion createal

Source: "atk-1.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "cairo.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "freetype.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "fribidi-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "fontconfig.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gdk_pixbuf-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gdk-win32-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gio-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "glib-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gmodule-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gobject-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gio-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "glib-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gmodule-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gobject-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
#if APPARCH == "x64"
Source: "gspawn-win64-helper.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gspawn-win64-helper-console.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
#else
Source: "gspawn-win32-helper.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gspawn-win32-helper-console.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
#endif
Source: "gthread-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gthread-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "gtk-win32-2.0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "iconv.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "libeay32.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Expand Down