summaryrefslogtreecommitdiffstats
path: root/lib/tty/tty-slang.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
commitffccd5b2b05243e7976db80f90f453dccfae9886 (patch)
tree39a43152d27f7390d8f7a6fb276fa6887f87c6e8 /lib/tty/tty-slang.c
parentInitial commit. (diff)
downloadmc-ffccd5b2b05243e7976db80f90f453dccfae9886.tar.xz
mc-ffccd5b2b05243e7976db80f90f453dccfae9886.zip
Adding upstream version 3:4.8.30.upstream/3%4.8.30
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/tty/tty-slang.c')
-rw-r--r--lib/tty/tty-slang.c781
1 files changed, 781 insertions, 0 deletions
diff --git a/lib/tty/tty-slang.c b/lib/tty/tty-slang.c
new file mode 100644
index 0000000..3aa74de
--- /dev/null
+++ b/lib/tty/tty-slang.c
@@ -0,0 +1,781 @@
+/*
+ Interface to the terminal controlling library.
+ Slang wrapper.
+
+ Copyright (C) 2005-2023
+ Free Software Foundation, Inc.
+
+ Written by:
+ Andrew Borodin <aborodin@vmail.ru>, 2009
+ Egmont Koblinger <egmont@gmail.com>, 2010
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * \brief Source: S-Lang-based tty layer of Midnight Commander
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h> /* size_t */
+#include <unistd.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <termios.h>
+
+#include "lib/global.h"
+#include "lib/strutil.h" /* str_term_form */
+#include "lib/util.h" /* is_printable() */
+
+#include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
+#include "tty.h"
+#include "color.h"
+#include "color-slang.h"
+#include "color-internal.h"
+#include "mouse.h" /* Gpm_Event is required in key.h */
+#include "key.h" /* define_sequence */
+#include "win.h"
+
+
+/*** global variables ****************************************************************************/
+
+/* If true program softkeys (HP terminals only) on startup and after every
+ command ran in the subshell to the description found in the termcap/terminfo
+ database */
+int reset_hp_softkeys = 0;
+
+/*** file scope macro definitions ****************************************************************/
+
+#ifndef SLTT_MAX_SCREEN_COLS
+#define SLTT_MAX_SCREEN_COLS 512
+#endif
+
+#ifndef SLTT_MAX_SCREEN_ROWS
+#define SLTT_MAX_SCREEN_ROWS 512
+#endif
+
+/*** file scope type declarations ****************************************************************/
+
+/*** forward declarations (file scope functions) *************************************************/
+
+/*** file scope variables ************************************************************************/
+
+/* Various saved termios settings that we control here */
+static struct termios boot_mode;
+static struct termios new_mode;
+
+/* Controls whether we should wait for input in tty_lowlevel_getch */
+static gboolean no_slang_delay;
+
+static gboolean slsmg_active = FALSE;
+
+/* This table describes which capabilities we want and which values we
+ * assign to them.
+ */
+static const struct
+{
+ int key_code;
+ const char *key_name;
+} key_table[] =
+{
+ /* *INDENT-OFF* */
+ { KEY_F (0), "k0" },
+ { KEY_F (1), "k1" },
+ { KEY_F (2), "k2" },
+ { KEY_F (3), "k3" },
+ { KEY_F (4), "k4" },
+ { KEY_F (5), "k5" },
+ { KEY_F (6), "k6" },
+ { KEY_F (7), "k7" },
+ { KEY_F (8), "k8" },
+ { KEY_F (9), "k9" },
+ { KEY_F (10), "k;" },
+ { KEY_F (11), "F1" },
+ { KEY_F (12), "F2" },
+ { KEY_F (13), "F3" },
+ { KEY_F (14), "F4" },
+ { KEY_F (15), "F5" },
+ { KEY_F (16), "F6" },
+ { KEY_F (17), "F7" },
+ { KEY_F (18), "F8" },
+ { KEY_F (19), "F9" },
+ { KEY_F (20), "FA" },
+ { KEY_IC, "kI" },
+ { KEY_NPAGE, "kN" },
+ { KEY_PPAGE, "kP" },
+ { KEY_LEFT, "kl" },
+ { KEY_RIGHT, "kr" },
+ { KEY_UP, "ku" },
+ { KEY_DOWN, "kd" },
+ { KEY_DC, "kD" },
+ { KEY_BACKSPACE, "kb" },
+ { KEY_HOME, "kh" },
+ { KEY_END, "@7" },
+ { 0, NULL }
+ /* *INDENT-ON* */
+};
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+tty_setup_sigwinch (void (*handler) (int))
+{
+ (void) SLsignal (SIGWINCH, handler);
+ tty_create_winch_pipe ();
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+sigwinch_handler (int dummy)
+{
+ ssize_t n = 0;
+
+ (void) dummy;
+
+ n = write (sigwinch_pipe[1], "", 1);
+ (void) n;
+
+ (void) SLsignal (SIGWINCH, sigwinch_handler);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
+ elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
+ consequence is that function keys don't work in MC sometimes...
+ Unfortunately I don't now the one and only escape sequence to turn off.
+ softkeys (elm uses three different capabilities to turn on softkeys and two.
+ capabilities to turn them off)..
+ Among other things elm uses the pair we already use in slang_keypad. That's.
+ the reason why I call slang_reset_softkeys from slang_keypad. In lack of
+ something better the softkeys are programmed to their defaults from the
+ termcap/terminfo database.
+ The escape sequence to program the softkeys is taken from elm and it is.
+ hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
+ sequence. -- Norbert
+ */
+
+static void
+slang_reset_softkeys (void)
+{
+ int key;
+ static const char display[] = " ";
+ char tmp[BUF_SMALL];
+
+ for (key = 1; key < 9; key++)
+ {
+ char *send;
+
+ g_snprintf (tmp, sizeof (tmp), "k%d", key);
+ send = SLtt_tgetstr (tmp);
+ if (send != NULL)
+ {
+ g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key,
+ (int) (sizeof (display) - 1), (int) strlen (send), display, send);
+ SLtt_write_string (tmp);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+do_define_key (int code, const char *strcap)
+{
+ char *seq;
+
+ seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap);
+ if (seq != NULL)
+ define_sequence (code, seq, MCKEY_NOACTION);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+load_terminfo_keys (void)
+{
+ int i;
+
+ for (i = 0; key_table[i].key_code; i++)
+ do_define_key (key_table[i].key_code, key_table[i].key_name);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+int
+mc_tty_normalize_lines_char (const char *str)
+{
+ char *str2;
+ int res;
+
+ struct mc_tty_lines_struct
+ {
+ const char *line;
+ int line_code;
+ } const lines_codes[] = {
+ {"\342\224\214", SLSMG_ULCORN_CHAR},
+ {"\342\224\220", SLSMG_URCORN_CHAR},
+ {"\342\224\224", SLSMG_LLCORN_CHAR},
+ {"\342\224\230", SLSMG_LRCORN_CHAR},
+ {"\342\224\234", SLSMG_LTEE_CHAR},
+ {"\342\224\244", SLSMG_RTEE_CHAR},
+ {"\342\224\254", SLSMG_UTEE_CHAR},
+ {"\342\224\264", SLSMG_DTEE_CHAR},
+ {"\342\224\200", SLSMG_HLINE_CHAR},
+ {"\342\224\202", SLSMG_VLINE_CHAR},
+ {"\342\224\274", SLSMG_PLUS_CHAR},
+
+ {NULL, 0}
+ };
+
+ if (!str)
+ return (int) ' ';
+
+ for (res = 0; lines_codes[res].line; res++)
+ {
+ if (strcmp (str, lines_codes[res].line) == 0)
+ return lines_codes[res].line_code;
+ }
+
+ str2 = mc_tty_normalize_from_utf8 (str);
+ res = g_utf8_get_char_validated (str2, -1);
+
+ if (res < 0)
+ res = (unsigned char) str2[0];
+ g_free (str2);
+
+ return res;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_init (gboolean mouse_enable, gboolean is_xterm)
+{
+ SLtt_Ignore_Beep = 1;
+
+ SLutf8_enable (-1); /* has to be called first before any of the other functions. */
+ SLtt_get_terminfo ();
+ /*
+ * If the terminal in not in terminfo but begins with a well-known
+ * string such as "linux" or "xterm" S-Lang will go on, but the
+ * terminal size and several other variables won't be initialized
+ * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
+ * small screen dimensions.
+ */
+ if ((COLS < 10) || (LINES < 5)
+#if SLANG_VERSION < 20303
+ /* Beginning from pre2.3.3-8 (55f58798c267d76a1b93d0d916027b71a10ac1ee),
+ these limitations were eliminated. */
+ || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS)
+#endif
+ )
+ {
+ fprintf (stderr,
+ _("Screen size %dx%d is not supported.\n"
+ "Check the TERM environment variable.\n"), COLS, LINES);
+ exit (EXIT_FAILURE);
+ }
+
+ tcgetattr (fileno (stdin), &boot_mode);
+ /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
+ SLang_init_tty (XCTRL ('g'), 1, 0);
+
+ if (mc_global.tty.ugly_line_drawing)
+ SLtt_Has_Alt_Charset = 0;
+
+ tcgetattr (SLang_TT_Read_FD, &new_mode);
+
+ tty_reset_prog_mode ();
+ load_terminfo_keys ();
+
+ SLtt_Blink_Mode = (tty_use_256colors (NULL) || tty_use_truecolors (NULL)) ? 1 : 0;
+
+ tty_start_interrupt_key ();
+
+ /* It's the small part from the previous init_key() */
+ init_key_input_fd ();
+
+ /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang
+ * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display
+ * detected - but checking TERM would fail under screen, OR running xterm
+ * with allowC1Printable).
+ */
+ tty_display_8bit (FALSE);
+
+ SLsmg_init_smg ();
+ slsmg_active = TRUE;
+ if (!mouse_enable)
+ use_mouse_p = MOUSE_DISABLED;
+ tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
+ tty_enter_ca_mode ();
+ tty_keypad (TRUE);
+ tty_nodelay (FALSE);
+
+ tty_setup_sigwinch (sigwinch_handler);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_shutdown (void)
+{
+ char *op_cap;
+
+ tty_destroy_winch_pipe ();
+ tty_reset_shell_mode ();
+ tty_noraw_mode ();
+ tty_keypad (FALSE);
+ tty_reset_screen ();
+ tty_exit_ca_mode ();
+ SLang_reset_tty ();
+ slsmg_active = FALSE;
+
+ /* Load the op capability to reset the colors to those that were
+ * active when the program was started up
+ */
+ op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op");
+ if (op_cap != NULL)
+ {
+ fputs (op_cap, stdout);
+ fflush (stdout);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_enter_ca_mode (void)
+{
+ /* S-Lang handles alternate screen switching and cursor position saving */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_exit_ca_mode (void)
+{
+ /* S-Lang handles alternate screen switching and cursor position restoring */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_change_screen_size (void)
+{
+ SLtt_get_screen_size ();
+ if (slsmg_active)
+ SLsmg_reinit_smg ();
+
+#ifdef ENABLE_SUBSHELL
+ if (mc_global.tty.use_subshell)
+ tty_resize (mc_global.tty.subshell_pty);
+#endif
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/* Done each time we come back from done mode */
+
+void
+tty_reset_prog_mode (void)
+{
+ tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
+ SLsmg_init_smg ();
+ slsmg_active = TRUE;
+ SLsmg_touch_lines (0, LINES);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/* Called each time we want to shutdown slang screen manager */
+
+void
+tty_reset_shell_mode (void)
+{
+ tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_raw_mode (void)
+{
+ tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_noraw_mode (void)
+{
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_noecho (void)
+{
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+tty_flush_input (void)
+{
+ return 0; /* OK */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_keypad (gboolean set)
+{
+ char *keypad_string;
+
+ keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke"));
+ if (keypad_string != NULL)
+ SLtt_write_string (keypad_string);
+ if (set && reset_hp_softkeys)
+ slang_reset_softkeys ();
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_nodelay (gboolean set)
+{
+ no_slang_delay = set;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+tty_baudrate (void)
+{
+ return SLang_TT_Baud_Rate;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+tty_lowlevel_getch (void)
+{
+ int c;
+
+ if (no_slang_delay && (SLang_input_pending (0) == 0))
+ return -1;
+
+ c = SLang_getkey ();
+ if (c == SLANG_GETKEY_ERROR)
+ {
+ fprintf (stderr,
+ "SLang_getkey returned SLANG_GETKEY_ERROR\n"
+ "Assuming EOF on stdin and exiting\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return c;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+tty_reset_screen (void)
+{
+ SLsmg_reset_smg ();
+ slsmg_active = FALSE;
+ return 0; /* OK */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_touch_screen (void)
+{
+ SLsmg_touch_lines (0, LINES);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_gotoyx (int y, int x)
+{
+ SLsmg_gotorc (y, x);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_getyx (int *py, int *px)
+{
+ *py = SLsmg_get_row ();
+ *px = SLsmg_get_column ();
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_draw_hline (int y, int x, int ch, int len)
+{
+ int x1;
+
+ if (y < 0 || y >= LINES || x >= COLS)
+ return;
+
+ x1 = x;
+
+ if (x < 0)
+ {
+ len += x;
+ if (len <= 0)
+ return;
+ x = 0;
+ }
+
+ if (ch == ACS_HLINE)
+ ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
+ if (ch == 0)
+ ch = ACS_HLINE;
+
+ SLsmg_gotorc (y, x);
+
+ if (ch == ACS_HLINE)
+ SLsmg_draw_hline (len);
+ else
+ while (len-- != 0)
+ tty_print_char (ch);
+
+ SLsmg_gotorc (y, x1);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_draw_vline (int y, int x, int ch, int len)
+{
+ int y1;
+
+ if (x < 0 || x >= COLS || y >= LINES)
+ return;
+
+ y1 = y;
+
+ if (y < 0)
+ {
+ len += y;
+ if (len <= 0)
+ return;
+ y = 0;
+ }
+
+ if (ch == ACS_VLINE)
+ ch = mc_tty_frm[MC_TTY_FRM_VERT];
+ if (ch == 0)
+ ch = ACS_VLINE;
+
+ SLsmg_gotorc (y, x);
+
+ if (ch == ACS_VLINE)
+ SLsmg_draw_vline (len);
+ else
+ {
+ int pos = 0;
+
+ while (len-- != 0)
+ {
+ SLsmg_gotorc (y + pos, x);
+ tty_print_char (ch);
+ pos++;
+ }
+ }
+
+ SLsmg_gotorc (y1, x);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
+{
+ SLsmg_fill_region (y, x, rows, cols, ch);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_colorize_area (int y, int x, int rows, int cols, int color)
+{
+ if (use_colors)
+ SLsmg_set_color_in_region (color, y, x, rows, cols);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_set_alt_charset (gboolean alt_charset)
+{
+ SLsmg_set_char_set ((int) alt_charset);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_display_8bit (gboolean what)
+{
+ SLsmg_Display_Eight_Bit = what ? 128 : 160;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_char (int c)
+{
+ SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_alt_char (int c, gboolean single)
+{
+#define DRAW(x, y) (x == y) \
+ ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \
+ : SLsmg_write_char ((unsigned int) y)
+ switch (c)
+ {
+ case ACS_VLINE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
+ break;
+ case ACS_HLINE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
+ break;
+ case ACS_LTEE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
+ break;
+ case ACS_RTEE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
+ break;
+ case ACS_TTEE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]);
+ break;
+ case ACS_BTEE:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]);
+ break;
+ case ACS_ULCORNER:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
+ break;
+ case ACS_LLCORNER:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
+ break;
+ case ACS_URCORNER:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
+ break;
+ case ACS_LRCORNER:
+ DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
+ break;
+ case ACS_PLUS:
+ DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
+ break;
+ default:
+ SLsmg_write_char ((unsigned int) c);
+ }
+#undef DRAW
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_anychar (int c)
+{
+ if (c > 255)
+ {
+ char str[UTF8_CHAR_LEN + 1];
+ int res;
+
+ res = g_unichar_to_utf8 (c, str);
+ if (res == 0)
+ {
+ str[0] = '.';
+ str[1] = '\0';
+ }
+ else
+ {
+ str[res] = '\0';
+ }
+ SLsmg_write_string ((char *) str_term_form (str));
+ }
+ else
+ {
+ if (!is_printable (c))
+ c = '.';
+ SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_string (const char *s)
+{
+ SLsmg_write_string ((char *) str_term_form (s));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_printf (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ SLsmg_vprintf ((char *) fmt, args);
+ va_end (args);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+char *
+tty_tgetstr (const char *cap)
+{
+ return SLtt_tgetstr ((SLFUTURE_CONST char *) cap);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_refresh (void)
+{
+ SLsmg_refresh ();
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_beep (void)
+{
+ SLtt_beep ();
+}
+
+/* --------------------------------------------------------------------------------------------- */