Commit 181ece12623597be158e3b9d0338a7a5954667b6
1 parent
c47c5466
Exists in
master
and in
1 other branch
Fixing charset conversion
Updating build & install process
Showing
9 changed files
with
139 additions
and
36 deletions
Show diff stats
Makefile.in
| @@ -28,10 +28,12 @@ | @@ -28,10 +28,12 @@ | ||
| 28 | 28 | ||
| 29 | MODULE_NAME=ipc3270 | 29 | MODULE_NAME=ipc3270 |
| 30 | 30 | ||
| 31 | -SOURCES= \ | 31 | +CORE_SOURCES= \ |
| 32 | $(wildcard src/*.c) \ | 32 | $(wildcard src/*.c) \ |
| 33 | + $(wildcard src/@OSNAME@/*.c) | ||
| 34 | + | ||
| 35 | +PLUGIN_SOURCES= \ | ||
| 33 | $(wildcard src/plugin/*.c) \ | 36 | $(wildcard src/plugin/*.c) \ |
| 34 | - $(wildcard src/@OSNAME@/*.c) \ | ||
| 35 | $(wildcard src/@OSNAME@/*.rc) | 37 | $(wildcard src/@OSNAME@/*.rc) |
| 36 | 38 | ||
| 37 | TEST_SOURCES= \ | 39 | TEST_SOURCES= \ |
| @@ -49,6 +51,7 @@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ | @@ -49,6 +51,7 @@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ | ||
| 49 | XGETTEXT=@XGETTEXT@ | 51 | XGETTEXT=@XGETTEXT@ |
| 50 | MSGCAT=@MSGCAT@ | 52 | MSGCAT=@MSGCAT@ |
| 51 | WINDRES=@WINDRES@ | 53 | WINDRES=@WINDRES@ |
| 54 | +AR=@AR@ | ||
| 52 | 55 | ||
| 53 | #---[ Paths ]---------------------------------------------------------------------------- | 56 | #---[ Paths ]---------------------------------------------------------------------------- |
| 54 | 57 | ||
| @@ -177,21 +180,40 @@ all: \ | @@ -177,21 +180,40 @@ all: \ | ||
| 177 | Release: \ | 180 | Release: \ |
| 178 | $(BINRLS)/$(MODULE_NAME)@DLLEXT@ | 181 | $(BINRLS)/$(MODULE_NAME)@DLLEXT@ |
| 179 | 182 | ||
| 183 | +$(BINRLS)/lib$(MODULE_NAME).a: \ | ||
| 184 | + $(foreach SRC, $(basename $(CORE_SOURCES)), $(OBJRLS)/$(SRC).o) | ||
| 185 | + | ||
| 186 | + @$(MKDIR) `dirname $@` | ||
| 187 | + @echo $< ... | ||
| 188 | + | ||
| 189 | + @$(AR) rcs $@ $^ | ||
| 190 | + | ||
| 180 | $(BINRLS)/$(MODULE_NAME)@DLLEXT@: \ | 191 | $(BINRLS)/$(MODULE_NAME)@DLLEXT@: \ |
| 181 | - $(foreach SRC, $(basename $(SOURCES)), $(OBJRLS)/$(SRC).o) | 192 | + $(foreach SRC, $(basename $(PLUGIN_SOURCES)), $(OBJRLS)/$(SRC).o) \ |
| 193 | + $(BINRLS)/lib$(MODULE_NAME).a | ||
| 194 | + | ||
| 182 | 195 | ||
| 183 | @$(MKDIR) `dirname $@` | 196 | @$(MKDIR) `dirname $@` |
| 184 | @echo $< ... | 197 | @echo $< ... |
| 185 | @$(LD) \ | 198 | @$(LD) \ |
| 186 | -shared -Wl,-soname,$(@F) \ | 199 | -shared -Wl,-soname,$(@F) \ |
| 187 | -o $@ \ | 200 | -o $@ \ |
| 188 | - $(LDFLAGS) \ | ||
| 189 | - $(foreach SRC, $(basename $(SOURCES)), $(OBJRLS)/$(SRC).o) \ | ||
| 190 | -L$(BINRLS) \ | 201 | -L$(BINRLS) \ |
| 202 | + $(LDFLAGS) \ | ||
| 203 | + $(foreach SRC, $(basename $(PLUGIN_SOURCES)), $(OBJRLS)/$(SRC).o) \ | ||
| 204 | + -l$(MODULE_NAME) \ | ||
| 191 | $(LIBS) | 205 | $(LIBS) |
| 192 | 206 | ||
| 193 | #---[ Install Targets ]------------------------------------------------------------------ | 207 | #---[ Install Targets ]------------------------------------------------------------------ |
| 194 | 208 | ||
| 209 | +install-plugin: \ | ||
| 210 | + $(BINRLS)/$(MODULE_NAME)@DLLEXT@ | ||
| 211 | + | ||
| 212 | + @$(MKDIR) $(DESTDIR)$(libdir)/pw3270-plugins | ||
| 213 | + | ||
| 214 | + @$(INSTALL_PROGRAM) \ | ||
| 215 | + $(BINRLS)/$(MODULE_NAME)@DLLEXT@ \ | ||
| 216 | + $(DESTDIR)$(libdir)/pw3270-plugins | ||
| 195 | 217 | ||
| 196 | #---[ Misc Targets ]--------------------------------------------------------------------- | 218 | #---[ Misc Targets ]--------------------------------------------------------------------- |
| 197 | 219 | ||
| @@ -212,6 +234,15 @@ Debug: \ | @@ -212,6 +234,15 @@ Debug: \ | ||
| 212 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@ \ | 234 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@ \ |
| 213 | $(BINDBG)/$(MODULE_NAME)@EXEEXT@ | 235 | $(BINDBG)/$(MODULE_NAME)@EXEEXT@ |
| 214 | 236 | ||
| 237 | +$(BINDBG)/lib$(MODULE_NAME).a: \ | ||
| 238 | + $(foreach SRC, $(basename $(CORE_SOURCES)), $(OBJDBG)/$(SRC).o) | ||
| 239 | + | ||
| 240 | + @$(MKDIR) `dirname $@` | ||
| 241 | + @echo $< ... | ||
| 242 | + | ||
| 243 | + @$(AR) rcs $@ $^ | ||
| 244 | + | ||
| 245 | + | ||
| 215 | $(BINDBG)/$(MODULE_NAME)@EXEEXT@: \ | 246 | $(BINDBG)/$(MODULE_NAME)@EXEEXT@: \ |
| 216 | $(foreach SRC, $(basename $(TEST_SOURCES)), $(OBJDBG)/$(SRC).o) \ | 247 | $(foreach SRC, $(basename $(TEST_SOURCES)), $(OBJDBG)/$(SRC).o) \ |
| 217 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@ | 248 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@ |
| @@ -251,7 +282,8 @@ $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@: \ | @@ -251,7 +282,8 @@ $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@: \ | ||
| 251 | $@ | 282 | $@ |
| 252 | 283 | ||
| 253 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@: \ | 284 | $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@: \ |
| 254 | - $(foreach SRC, $(basename $(SOURCES)), $(OBJDBG)/$(SRC).o) | 285 | + $(foreach SRC, $(basename $(PLUGIN_SOURCES)), $(OBJDBG)/$(SRC).o) \ |
| 286 | + $(BINDBG)/lib$(MODULE_NAME).a | ||
| 255 | 287 | ||
| 256 | @$(MKDIR) `dirname $@` | 288 | @$(MKDIR) `dirname $@` |
| 257 | @echo $< ... | 289 | @echo $< ... |
| @@ -260,8 +292,9 @@ $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@.@PACKAGE_MIN | @@ -260,8 +292,9 @@ $(BINDBG)/@DLLPREFIX@$(MODULE_NAME)@DLLEXT@.@PACKAGE_MAJOR_VERSION@.@PACKAGE_MIN | ||
| 260 | -o $@ \ | 292 | -o $@ \ |
| 261 | -L$(BINDBG) \ | 293 | -L$(BINDBG) \ |
| 262 | $(LDFLAGS) \ | 294 | $(LDFLAGS) \ |
| 263 | - $(foreach SRC, $(basename $(SOURCES)), $(OBJDBG)/$(SRC).o) \ | ||
| 264 | - $(LIBS) | 295 | + $(foreach SRC, $(basename $(PLUGIN_SOURCES)), $(OBJDBG)/$(SRC).o) \ |
| 296 | + -l$(MODULE_NAME) \ | ||
| 297 | + $(LIBS) | ||
| 265 | 298 | ||
| 266 | #---[ Clean Targets ]-------------------------------------------------------------------- | 299 | #---[ Clean Targets ]-------------------------------------------------------------------- |
| 267 | 300 |
pw3270-plugin-ipc.cbp
| @@ -41,6 +41,9 @@ | @@ -41,6 +41,9 @@ | ||
| 41 | <Unit filename="src/constants.c"> | 41 | <Unit filename="src/constants.c"> |
| 42 | <Option compilerVar="CC" /> | 42 | <Option compilerVar="CC" /> |
| 43 | </Unit> | 43 | </Unit> |
| 44 | + <Unit filename="src/convert.c"> | ||
| 45 | + <Option compilerVar="CC" /> | ||
| 46 | + </Unit> | ||
| 44 | <Unit filename="src/include/config.h.in" /> | 47 | <Unit filename="src/include/config.h.in" /> |
| 45 | <Unit filename="src/include/lib3270/ipc.h" /> | 48 | <Unit filename="src/include/lib3270/ipc.h" /> |
| 46 | <Unit filename="src/linux/getproperties.c"> | 49 | <Unit filename="src/linux/getproperties.c"> |
| @@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
| 1 | +/* | ||
| 2 | + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 | ||
| 3 | + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a | ||
| 4 | + * aplicativos mainframe. Registro no INPI sob o nome G3270. | ||
| 5 | + * | ||
| 6 | + * Copyright (C) <2008> <Banco do Brasil S.A.> | ||
| 7 | + * | ||
| 8 | + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob | ||
| 9 | + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela | ||
| 10 | + * Free Software Foundation. | ||
| 11 | + * | ||
| 12 | + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER | ||
| 13 | + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO | ||
| 14 | + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para | ||
| 15 | + * obter mais detalhes. | ||
| 16 | + * | ||
| 17 | + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este | ||
| 18 | + * programa; se não, escreva para a Free Software Foundation, Inc., 51 Franklin | ||
| 19 | + * St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | + * | ||
| 21 | + * Este programa está nomeado como main.c e possui - linhas de código. | ||
| 22 | + * | ||
| 23 | + * Referências: | ||
| 24 | + * | ||
| 25 | + * https://github.com/joprietoe/gdbus/blob/master/gdbus-example-server.c | ||
| 26 | + * https://github.com/bratsche/glib/blob/master/gio/tests/gdbus-example-export.c | ||
| 27 | + * | ||
| 28 | + * Contatos: | ||
| 29 | + * | ||
| 30 | + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) | ||
| 31 | + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) | ||
| 32 | + * | ||
| 33 | + */ | ||
| 34 | + | ||
| 35 | +#include <lib3270.h> | ||
| 36 | +#include <lib3270/actions.h> | ||
| 37 | +#include <lib3270/ipc.h> | ||
| 38 | + | ||
| 39 | +gchar * ipc3270_convert_output_string(GObject *object, const gchar *string) | ||
| 40 | +{ | ||
| 41 | + return g_convert_with_fallback(string,-1,ipc3270_get_display_charset(object),"UTF-8","?",NULL,NULL,NULL); | ||
| 42 | +} | ||
| 43 | + |
src/include/lib3270/ipc.h
| @@ -55,6 +55,8 @@ | @@ -55,6 +55,8 @@ | ||
| 55 | GObject * ipc3270_new(); | 55 | GObject * ipc3270_new(); |
| 56 | GType ipc3270_get_type(void); | 56 | GType ipc3270_get_type(void); |
| 57 | void ipc3270_set_session(GObject *object, H3270 *hSession, const char *name, GError **error); | 57 | void ipc3270_set_session(GObject *object, H3270 *hSession, const char *name, GError **error); |
| 58 | + gchar * ipc3270_convert_output_string(GObject *object, const gchar *string); | ||
| 59 | + const gchar * ipc3270_get_display_charset(GObject *object); | ||
| 58 | 60 | ||
| 59 | G_END_DECLS | 61 | G_END_DECLS |
| 60 | 62 |
src/linux/getproperties.c
| @@ -40,13 +40,14 @@ | @@ -40,13 +40,14 @@ | ||
| 40 | #include <dbus/dbus-glib-bindings.h> | 40 | #include <dbus/dbus-glib-bindings.h> |
| 41 | 41 | ||
| 42 | GVariant * | 42 | GVariant * |
| 43 | -ipc3270_get_property (GDBusConnection *connection, | ||
| 44 | - const gchar *sender, | ||
| 45 | - const gchar *object_path, | ||
| 46 | - const gchar *interface_name, | ||
| 47 | - const gchar *property_name, | ||
| 48 | - GError **error, | ||
| 49 | - gpointer user_data) | 43 | +ipc3270_get_property ( |
| 44 | + G_GNUC_UNUSED GDBusConnection *connection, | ||
| 45 | + G_GNUC_UNUSED const gchar *sender, | ||
| 46 | + G_GNUC_UNUSED const gchar *object_path, | ||
| 47 | + G_GNUC_UNUSED const gchar *interface_name, | ||
| 48 | + const gchar *property_name, | ||
| 49 | + GError **error, | ||
| 50 | + gpointer user_data) | ||
| 50 | { | 51 | { |
| 51 | size_t ix; | 52 | size_t ix; |
| 52 | 53 |
src/linux/gobject.c
| @@ -72,7 +72,7 @@ static void ipc3270_init(ipc3270 *object) { | @@ -72,7 +72,7 @@ static void ipc3270_init(ipc3270 *object) { | ||
| 72 | 72 | ||
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | -GObject * ipc3270_new(GtkWidget *window, GtkWidget *terminal) { | 75 | +GObject * ipc3270_new() { |
| 76 | return g_object_new(GLIB_TYPE_IPC3270, NULL); | 76 | return g_object_new(GLIB_TYPE_IPC3270, NULL); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| @@ -270,5 +270,8 @@ void ipc3270_set_session(GObject *object, H3270 *hSession, const char *name, GEr | @@ -270,5 +270,8 @@ void ipc3270_set_session(GObject *object, H3270 *hSession, const char *name, GEr | ||
| 270 | 270 | ||
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | +const gchar * ipc3270_get_display_charset(GObject *object) { | ||
| 274 | + return lib3270_get_display_charset(IPC3270(object)->hSession); | ||
| 275 | +} | ||
| 273 | 276 | ||
| 274 | 277 |
src/linux/methods.c
| @@ -40,7 +40,7 @@ | @@ -40,7 +40,7 @@ | ||
| 40 | #include <dbus/dbus-glib-bindings.h> | 40 | #include <dbus/dbus-glib-bindings.h> |
| 41 | 41 | ||
| 42 | /// @brief Converts lib3270 string to UTF-8 sets the method response. | 42 | /// @brief Converts lib3270 string to UTF-8 sets the method response. |
| 43 | -void g_dbus_method_invocation_return_tn3270_string(ipc3270 *obj, GDBusMethodInvocation *invocation, char *string) { | 43 | +static void g_dbus_method_invocation_return_tn3270_string(ipc3270 *obj, GDBusMethodInvocation *invocation, char *string) { |
| 44 | 44 | ||
| 45 | if(!string) { | 45 | if(!string) { |
| 46 | g_autoptr (GError) error = NULL; | 46 | g_autoptr (GError) error = NULL; |
| @@ -59,14 +59,15 @@ void g_dbus_method_invocation_return_tn3270_string(ipc3270 *obj, GDBusMethodInvo | @@ -59,14 +59,15 @@ void g_dbus_method_invocation_return_tn3270_string(ipc3270 *obj, GDBusMethodInvo | ||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | void | 61 | void |
| 62 | -ipc3270_method_call (GDBusConnection *connection, | ||
| 63 | - const gchar *sender, | ||
| 64 | - const gchar *object_path, | ||
| 65 | - const gchar *interface_name, | ||
| 66 | - const gchar *method_name, | ||
| 67 | - GVariant *parameters, | ||
| 68 | - GDBusMethodInvocation *invocation, | ||
| 69 | - gpointer user_data) | 62 | +ipc3270_method_call ( |
| 63 | + G_GNUC_UNUSED GDBusConnection *connection, | ||
| 64 | + G_GNUC_UNUSED const gchar *sender, | ||
| 65 | + G_GNUC_UNUSED const gchar *object_path, | ||
| 66 | + G_GNUC_UNUSED const gchar *interface_name, | ||
| 67 | + const gchar *method_name, | ||
| 68 | + GVariant *parameters, | ||
| 69 | + GDBusMethodInvocation *invocation, | ||
| 70 | + gpointer user_data) | ||
| 70 | { | 71 | { |
| 71 | 72 | ||
| 72 | 73 | ||
| @@ -85,7 +86,8 @@ ipc3270_method_call (GDBusConnection *connection, | @@ -85,7 +86,8 @@ ipc3270_method_call (GDBusConnection *connection, | ||
| 85 | gchar *text = NULL; | 86 | gchar *text = NULL; |
| 86 | g_variant_get(parameters, "(&s)", &text); | 87 | g_variant_get(parameters, "(&s)", &text); |
| 87 | 88 | ||
| 88 | - if(lib3270_input_string(IPC3270(user_data)->hSession,(const unsigned char *) text) < 0) | 89 | + g_autofree gchar * converted = ipc3270_convert_output_string(G_OBJECT(user_data), text); |
| 90 | + if(lib3270_input_string(IPC3270(user_data)->hSession,(const unsigned char *) converted) < 0) | ||
| 89 | { | 91 | { |
| 90 | // Failed! | 92 | // Failed! |
| 91 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); | 93 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); |
| @@ -105,7 +107,8 @@ ipc3270_method_call (GDBusConnection *connection, | @@ -105,7 +107,8 @@ ipc3270_method_call (GDBusConnection *connection, | ||
| 105 | gchar *text = NULL; | 107 | gchar *text = NULL; |
| 106 | g_variant_get(parameters, "(ii&s)", &row, &col, &text); | 108 | g_variant_get(parameters, "(ii&s)", &row, &col, &text); |
| 107 | 109 | ||
| 108 | - if(lib3270_set_string_at(IPC3270(user_data)->hSession,row,col,(const unsigned char *) text) < 0) | 110 | + g_autofree gchar * converted = ipc3270_convert_output_string(G_OBJECT(user_data), text); |
| 111 | + if(lib3270_set_string_at(IPC3270(user_data)->hSession,row,col,(const unsigned char *) converted) < 0) | ||
| 109 | { | 112 | { |
| 110 | // Failed! | 113 | // Failed! |
| 111 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); | 114 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); |
| @@ -135,7 +138,8 @@ ipc3270_method_call (GDBusConnection *connection, | @@ -135,7 +138,8 @@ ipc3270_method_call (GDBusConnection *connection, | ||
| 135 | gchar *text = NULL; | 138 | gchar *text = NULL; |
| 136 | g_variant_get(parameters, "(i&s)", &addr, &text); | 139 | g_variant_get(parameters, "(i&s)", &addr, &text); |
| 137 | 140 | ||
| 138 | - if(lib3270_set_string_at_address(IPC3270(user_data)->hSession,addr,(unsigned char *) text) < 0) | 141 | + g_autofree gchar * converted = ipc3270_convert_output_string(G_OBJECT(user_data), text); |
| 142 | + if(lib3270_set_string_at_address(IPC3270(user_data)->hSession,addr,(unsigned char *) converted) < 0) | ||
| 139 | { | 143 | { |
| 140 | // Failed! | 144 | // Failed! |
| 141 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); | 145 | g_set_error(&error,IPC3270(user_data)->error_domain,errno,"%s: %s",method_name,strerror(errno)); |
src/linux/setproperties.c
| @@ -40,14 +40,15 @@ | @@ -40,14 +40,15 @@ | ||
| 40 | #include <dbus/dbus-glib-bindings.h> | 40 | #include <dbus/dbus-glib-bindings.h> |
| 41 | 41 | ||
| 42 | gboolean | 42 | gboolean |
| 43 | -ipc3270_set_property (GDBusConnection *connection, | ||
| 44 | - const gchar *sender, | ||
| 45 | - const gchar *object_path, | ||
| 46 | - const gchar *interface_name, | ||
| 47 | - const gchar *property_name, | ||
| 48 | - GVariant *value, | ||
| 49 | - GError **error, | ||
| 50 | - gpointer user_data) | 43 | +ipc3270_set_property ( |
| 44 | + G_GNUC_UNUSED GDBusConnection *connection, | ||
| 45 | + G_GNUC_UNUSED const gchar *sender, | ||
| 46 | + G_GNUC_UNUSED const gchar *object_path, | ||
| 47 | + G_GNUC_UNUSED const gchar *interface_name, | ||
| 48 | + const gchar *property_name, | ||
| 49 | + GVariant *value, | ||
| 50 | + GError **error, | ||
| 51 | + gpointer user_data) | ||
| 51 | { | 52 | { |
| 52 | // Check for property | 53 | // Check for property |
| 53 | size_t ix; | 54 | size_t ix; |
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +#!/bin/bash | ||
| 2 | +# | ||
| 3 | +# https://stackoverflow.com/questions/48648952/set-get-property-using-dbus-send | ||
| 4 | +# | ||
| 5 | + | ||
| 6 | +dbus-send \ | ||
| 7 | + --session \ | ||
| 8 | + --dest=br.com.bb.pw3270.a\ | ||
| 9 | + --print-reply \ | ||
| 10 | + "/br/com/bb/tn3270" \ | ||
| 11 | + "br.com.bb.tn3270.setString" | ||
| 12 | + string:${1} | ||
| 13 | + |