top: enable true line input editing with paste support
This patch changes the TERMIO_PROXY define back to the
former TERMIOS_ONLY thus changing the top default too.
Plus we can now use true line input editing while also
retaining paste capability. That former native termios
support provided only a limited destructive backspace.
Now we exploit the Left/Right arrow keys, Home/End and
Delete. Plus, the Insert key can toggle overtype mode!
[ The stage is now set for a really huge improvement ]
[ to any user input terminated with the <Enter> key. ]
[ So please stay tuned for the next patch to arrive! ]
(everything is perfectly justified plus right margins)
(are completely filled, but of course it must be luck)
Reference(s):
commit fa21a6ca81
Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
parent
d04297843f
commit
477b10c0bd
102
top/top.c
102
top/top.c
@ -62,7 +62,7 @@
|
|||||||
/* The original and new terminal definitions
|
/* The original and new terminal definitions
|
||||||
(only set when not in 'Batch' mode) */
|
(only set when not in 'Batch' mode) */
|
||||||
static struct termios Tty_original, // our inherited terminal definition
|
static struct termios Tty_original, // our inherited terminal definition
|
||||||
#ifndef TERMIO_PROXY
|
#ifdef TERMIOS_ONLY
|
||||||
Tty_tweaked, // for interactive 'line' input
|
Tty_tweaked, // for interactive 'line' input
|
||||||
#endif
|
#endif
|
||||||
Tty_raw; // for unsolicited input
|
Tty_raw; // for unsolicited input
|
||||||
@ -718,14 +718,14 @@ static int show_pmt (const char *str) {
|
|||||||
PUTT("%s%s%.*s %s%s%s"
|
PUTT("%s%s%.*s %s%s%s"
|
||||||
, tg2(0, Msg_row)
|
, tg2(0, Msg_row)
|
||||||
, Curwin->capclr_pmt
|
, Curwin->capclr_pmt
|
||||||
, Screen_cols - 3
|
, Screen_cols - 2
|
||||||
, str
|
, str
|
||||||
, Cap_curs_huge
|
, Cap_curs_huge
|
||||||
, Caps_off
|
, Caps_off
|
||||||
, Cap_clr_eol);
|
, Cap_clr_eol);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
// +2 for the ': ' chars we added or -1 for the cursor...
|
// +1 for the space we added or -1 for the cursor...
|
||||||
return ((rc = (int)strlen(str)+2) < Screen_cols) ? rc : Screen_cols-1;
|
return ((rc = (int)strlen(str)+1) < Screen_cols) ? rc : Screen_cols-1;
|
||||||
} // end: show_pmt
|
} // end: show_pmt
|
||||||
|
|
||||||
|
|
||||||
@ -927,7 +927,7 @@ static inline int ioa (struct timespec *ts) {
|
|||||||
static int ioch (int ech, char *buf, unsigned cnt) {
|
static int ioch (int ech, char *buf, unsigned cnt) {
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
|
||||||
#ifndef TERMIO_PROXY
|
#ifdef TERMIOS_ONLY
|
||||||
if (ech) {
|
if (ech) {
|
||||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_tweaked);
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_tweaked);
|
||||||
rc = read(STDIN_FILENO, buf, cnt);
|
rc = read(STDIN_FILENO, buf, cnt);
|
||||||
@ -954,10 +954,11 @@ static int ioch (int ech, char *buf, unsigned cnt) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Support for single keystroke input AND escaped cursor motion keys
|
* Support for single or multiple keystroke input AND
|
||||||
|
* escaped cursor motion keys.
|
||||||
* note: we support more keys than we currently need, in case
|
* note: we support more keys than we currently need, in case
|
||||||
* we attract new consumers in the future */
|
* we attract new consumers in the future */
|
||||||
static int iokey (int init) {
|
static int iokey (int action) {
|
||||||
static char buf12[CAPBUFSIZ], buf13[CAPBUFSIZ]
|
static char buf12[CAPBUFSIZ], buf13[CAPBUFSIZ]
|
||||||
, buf14[CAPBUFSIZ], buf15[CAPBUFSIZ];
|
, buf14[CAPBUFSIZ], buf15[CAPBUFSIZ];
|
||||||
static struct {
|
static struct {
|
||||||
@ -980,10 +981,16 @@ static int iokey (int init) {
|
|||||||
{ "\033\013", kbd_PGUP }, { "\033\012", kbd_PGDN }, /* ctrl+meta+ k,j */
|
{ "\033\013", kbd_PGUP }, { "\033\012", kbd_PGDN }, /* ctrl+meta+ k,j */
|
||||||
{ "\033\010", kbd_HOME }, { "\033\014", kbd_END } /* ctrl+meta+ h,l */
|
{ "\033\010", kbd_HOME }, { "\033\014", kbd_END } /* ctrl+meta+ h,l */
|
||||||
};
|
};
|
||||||
|
#ifdef TERMIOS_ONLY
|
||||||
char buf[SMLBUFSIZ], *pb;
|
char buf[SMLBUFSIZ], *pb;
|
||||||
|
#else
|
||||||
|
static char buf[SMLBUFSIZ];
|
||||||
|
static int pos, len;
|
||||||
|
char *pb;
|
||||||
|
#endif
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (init) {
|
if (action == 0) {
|
||||||
#define tOk(s) s ? s : ""
|
#define tOk(s) s ? s : ""
|
||||||
tinfo_tab[1].str = tOk(key_up);
|
tinfo_tab[1].str = tOk(key_up);
|
||||||
tinfo_tab[2].str = tOk(key_down);
|
tinfo_tab[2].str = tOk(key_down);
|
||||||
@ -1007,8 +1014,25 @@ static int iokey (int init) {
|
|||||||
#undef tOk
|
#undef tOk
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(buf, '\0', sizeof(buf));
|
if (action == 1) {
|
||||||
if (1 > ioch(0, buf, sizeof(buf)-1)) return 0;
|
memset(buf, '\0', sizeof(buf));
|
||||||
|
if (1 > ioch(0, buf, sizeof(buf)-1)) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef TERMIOS_ONLY
|
||||||
|
if (action == 2) {
|
||||||
|
if (pos < len)
|
||||||
|
return buf[pos++]; // exhaust prior keystrokes
|
||||||
|
pos = len = 0;
|
||||||
|
memset(buf, '\0', sizeof(buf));
|
||||||
|
if (1 > ioch(0, buf, sizeof(buf)-1)) return 0;
|
||||||
|
if (isprint(buf[0])) { // no need for translation
|
||||||
|
len = strlen(buf);
|
||||||
|
pos = 1;
|
||||||
|
return buf[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* some emulators implement 'key repeat' too well and we get duplicate
|
/* some emulators implement 'key repeat' too well and we get duplicate
|
||||||
key sequences -- so we'll focus on the last escaped sequence, while
|
key sequences -- so we'll focus on the last escaped sequence, while
|
||||||
@ -1026,7 +1050,7 @@ static int iokey (int init) {
|
|||||||
} // end: iokey
|
} // end: iokey
|
||||||
|
|
||||||
|
|
||||||
#ifndef TERMIO_PROXY
|
#ifdef TERMIOS_ONLY
|
||||||
/*
|
/*
|
||||||
* Get line oriented interactive input from the user,
|
* Get line oriented interactive input from the user,
|
||||||
* using native tty support */
|
* using native tty support */
|
||||||
@ -1047,13 +1071,9 @@ static char *ioline (const char *prompt) {
|
|||||||
#else
|
#else
|
||||||
/*
|
/*
|
||||||
* Get line oriented interactive input from the user,
|
* Get line oriented interactive input from the user,
|
||||||
* going way beyond native tty support !
|
* going way beyond native tty support by providing:
|
||||||
* Unlike native tty input support, this function provides:
|
|
||||||
* . true line editing, not just destructive backspace
|
* . true line editing, not just destructive backspace
|
||||||
* . an input limit that's sensitive to current screen dimensions
|
* . an input limit sensitive to current screen dimensions */
|
||||||
* . immediate signal response without the need to wait for '\n'
|
|
||||||
* However, the user will lose the ability to paste keystrokes
|
|
||||||
* when this function is chosen over the smaller alternative above! */
|
|
||||||
static char *ioline (const char *prompt) {
|
static char *ioline (const char *prompt) {
|
||||||
// thank goodness memmove allows the two strings to overlap
|
// thank goodness memmove allows the two strings to overlap
|
||||||
#define sqzSTR { memmove(&buf[pos], &buf[pos+1], bufMAX-pos); \
|
#define sqzSTR { memmove(&buf[pos], &buf[pos+1], bufMAX-pos); \
|
||||||
@ -1064,31 +1084,32 @@ static char *ioline (const char *prompt) {
|
|||||||
#define phyCOL (beg+pos+1)
|
#define phyCOL (beg+pos+1)
|
||||||
#define bufMAX ((int)sizeof(buf)-2) // -1 for '\0' string delimeter
|
#define bufMAX ((int)sizeof(buf)-2) // -1 for '\0' string delimeter
|
||||||
static char buf[MEDBUFSIZ+1]; // +1 for '\0' string delimeter
|
static char buf[MEDBUFSIZ+1]; // +1 for '\0' string delimeter
|
||||||
int beg, pos, len;
|
int beg, pos, len, key, ovt;
|
||||||
int key;
|
|
||||||
|
|
||||||
pos = 0;
|
pos = ovt = 0;
|
||||||
beg = show_pmt(prompt);
|
beg = show_pmt(prompt);
|
||||||
memset(buf, '\0', sizeof(buf));
|
memset(buf, '\0', sizeof(buf));
|
||||||
|
putp(Cap_curs_norm);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
fflush(stdout);
|
||||||
len = strlen(buf);
|
len = strlen(buf);
|
||||||
key = iokey(0);
|
key = iokey(2);
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case kbd_ESC:
|
case kbd_ESC:
|
||||||
buf[0] = '\0'; // fall through !
|
buf[0] = '\0'; // fall through !
|
||||||
case kbd_ENTER:
|
case kbd_ENTER:
|
||||||
break;
|
break;
|
||||||
|
case kbd_INS:
|
||||||
|
ovt = !ovt;
|
||||||
|
putp(ovt ? Cap_curs_huge : Cap_curs_norm);
|
||||||
|
break;
|
||||||
case kbd_DEL:
|
case kbd_DEL:
|
||||||
case kbd_DOWN:
|
|
||||||
sqzSTR
|
sqzSTR
|
||||||
break;
|
break;
|
||||||
case kbd_BKSP :
|
case kbd_BKSP :
|
||||||
if (0 < pos) { --pos; sqzSTR }
|
if (0 < pos) { --pos; sqzSTR }
|
||||||
break;
|
break;
|
||||||
case kbd_INS:
|
|
||||||
case kbd_UP:
|
|
||||||
expSTR
|
|
||||||
break;
|
|
||||||
case kbd_LEFT:
|
case kbd_LEFT:
|
||||||
if (0 < pos) --pos;
|
if (0 < pos) --pos;
|
||||||
break;
|
break;
|
||||||
@ -1102,13 +1123,14 @@ static char *ioline (const char *prompt) {
|
|||||||
pos = len;
|
pos = len;
|
||||||
break;
|
break;
|
||||||
default: // what we REALLY wanted (maybe)
|
default: // what we REALLY wanted (maybe)
|
||||||
if (isprint(key) && logCOL < bufMAX && phyCOL < Screen_cols)
|
if (isprint(key) && logCOL < bufMAX && phyCOL < Screen_cols) {
|
||||||
|
if (!ovt) expSTR
|
||||||
buf[pos++] = key;
|
buf[pos++] = key;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
putp(fmtmk("%s%s%s", tg2(beg, Msg_row), Cap_clr_eol, buf));
|
putp(fmtmk("%s%s%s", tg2(beg, Msg_row), Cap_clr_eol, buf));
|
||||||
putp(tg2(beg+pos, Msg_row));
|
putp(tg2(beg+pos, Msg_row));
|
||||||
fflush(stdout);
|
|
||||||
} while (key != kbd_ENTER && key != kbd_ESC);
|
} while (key != kbd_ENTER && key != kbd_ESC);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
@ -1954,7 +1976,7 @@ signify_that:
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
if (Frames_resize) goto signify_that;
|
if (Frames_resize) goto signify_that;
|
||||||
key = iokey(0);
|
key = iokey(1);
|
||||||
if (key < 1) goto signify_that;
|
if (key < 1) goto signify_that;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -2813,7 +2835,7 @@ signify_that:
|
|||||||
tcflush(STDIN_FILENO, TCIFLUSH);
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
||||||
|
|
||||||
if (Frames_resize) goto signify_that;
|
if (Frames_resize) goto signify_that;
|
||||||
key = iokey(0);
|
key = iokey(1);
|
||||||
if (key < 1) goto signify_that;
|
if (key < 1) goto signify_that;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -2862,7 +2884,7 @@ signify_that:
|
|||||||
case '=':
|
case '=':
|
||||||
snprintf(buf, sizeof(buf), "%s: %s", Insp_sel->type, Insp_sel->fmts);
|
snprintf(buf, sizeof(buf), "%s: %s", Insp_sel->type, Insp_sel->fmts);
|
||||||
INSP_MKSL(1, buf); // show an extended SL
|
INSP_MKSL(1, buf); // show an extended SL
|
||||||
if (iokey(0) < 1)
|
if (iokey(1) < 1)
|
||||||
goto signify_that;
|
goto signify_that;
|
||||||
break;
|
break;
|
||||||
default: // keep gcc happy
|
default: // keep gcc happy
|
||||||
@ -2918,7 +2940,7 @@ signify_that:
|
|||||||
INSP_MKSL(0, " ");
|
INSP_MKSL(0, " ");
|
||||||
|
|
||||||
if (Frames_resize) goto signify_that;
|
if (Frames_resize) goto signify_that;
|
||||||
if (key == INT_MAX) key = iokey(0);
|
if (key == INT_MAX) key = iokey(1);
|
||||||
if (key < 1) goto signify_that;
|
if (key < 1) goto signify_that;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -3494,7 +3516,7 @@ static void whack_terminal (void) {
|
|||||||
tmptty.c_iflag &= ~IGNBRK;
|
tmptty.c_iflag &= ~IGNBRK;
|
||||||
if (key_backspace && 1 == strlen(key_backspace))
|
if (key_backspace && 1 == strlen(key_backspace))
|
||||||
tmptty.c_cc[VERASE] = *key_backspace;
|
tmptty.c_cc[VERASE] = *key_backspace;
|
||||||
#ifndef TERMIO_PROXY
|
#ifdef TERMIOS_ONLY
|
||||||
if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmptty))
|
if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmptty))
|
||||||
error_exit(fmtmk(N_fmt(FAIL_tty_set_fmt), strerror(errno)));
|
error_exit(fmtmk(N_fmt(FAIL_tty_set_fmt), strerror(errno)));
|
||||||
tcgetattr(STDIN_FILENO, &Tty_tweaked);
|
tcgetattr(STDIN_FILENO, &Tty_tweaked);
|
||||||
@ -3514,7 +3536,7 @@ static void whack_terminal (void) {
|
|||||||
// this has the effect of disabling any troublesome scrollback buffer...
|
// this has the effect of disabling any troublesome scrollback buffer...
|
||||||
if (enter_ca_mode) putp(enter_ca_mode);
|
if (enter_ca_mode) putp(enter_ca_mode);
|
||||||
// and don't forget to ask iokey to initialize his tinfo_tab
|
// and don't forget to ask iokey to initialize his tinfo_tab
|
||||||
iokey(1);
|
iokey(0);
|
||||||
} // end: whack_terminal
|
} // end: whack_terminal
|
||||||
|
|
||||||
/*###### Windows/Field Groups support #################################*/
|
/*###### Windows/Field Groups support #################################*/
|
||||||
@ -3539,7 +3561,7 @@ static WIN_t *win_select (int ch) {
|
|||||||
so we must try to get our own darn ch by begging the user... */
|
so we must try to get our own darn ch by begging the user... */
|
||||||
if (!ch) {
|
if (!ch) {
|
||||||
show_pmt(N_txt(CHOOSE_group_txt));
|
show_pmt(N_txt(CHOOSE_group_txt));
|
||||||
if (1 > (ch = iokey(0))) return w;
|
if (1 > (ch = iokey(1))) return w;
|
||||||
}
|
}
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'a': // we don't carry 'a' / 'w' in our
|
case 'a': // we don't carry 'a' / 'w' in our
|
||||||
@ -3628,7 +3650,7 @@ signify_that:
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
if (Frames_resize) goto signify_that;
|
if (Frames_resize) goto signify_that;
|
||||||
key = iokey(0);
|
key = iokey(1);
|
||||||
if (key < 1) goto signify_that;
|
if (key < 1) goto signify_that;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -3795,7 +3817,7 @@ static void file_writerc (void) {
|
|||||||
|
|
||||||
if (Rc_questions) {
|
if (Rc_questions) {
|
||||||
show_pmt(N_txt(XTRA_warncfg_txt));
|
show_pmt(N_txt(XTRA_warncfg_txt));
|
||||||
if ('y' != tolower(iokey(0)))
|
if ('y' != tolower(iokey(1)))
|
||||||
return;
|
return;
|
||||||
Rc_questions = 0;
|
Rc_questions = 0;
|
||||||
}
|
}
|
||||||
@ -3909,7 +3931,7 @@ signify_that:
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
if (Frames_resize) goto signify_that;
|
if (Frames_resize) goto signify_that;
|
||||||
key = iokey(0);
|
key = iokey(1);
|
||||||
if (key < 1) goto signify_that;
|
if (key < 1) goto signify_that;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -3924,7 +3946,7 @@ signify_that:
|
|||||||
, Winstk[2].rc.winname, Winstk[3].rc.winname));
|
, Winstk[2].rc.winname, Winstk[3].rc.winname));
|
||||||
putp(Cap_clr_eos);
|
putp(Cap_clr_eos);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (Frames_resize || (key = iokey(0)) < 1) {
|
if (Frames_resize || (key = iokey(1)) < 1) {
|
||||||
adj_geometry();
|
adj_geometry();
|
||||||
putp(Cap_clr_scr);
|
putp(Cap_clr_scr);
|
||||||
} else w = win_select(key);
|
} else w = win_select(key);
|
||||||
@ -5130,7 +5152,7 @@ int main (int dont_care_argc, char **argv) {
|
|||||||
pselect(0, NULL, NULL, NULL, &ts, NULL);
|
pselect(0, NULL, NULL, NULL, &ts, NULL);
|
||||||
else {
|
else {
|
||||||
if (ioa(&ts))
|
if (ioa(&ts))
|
||||||
do_key(iokey(0));
|
do_key(iokey(1));
|
||||||
}
|
}
|
||||||
/* note: that above ioa routine exists to consolidate all logic
|
/* note: that above ioa routine exists to consolidate all logic
|
||||||
which is susceptible to signal interrupt and must then
|
which is susceptible to signal interrupt and must then
|
||||||
|
28
top/top.h
28
top/top.h
@ -48,7 +48,7 @@
|
|||||||
//#define RMAN_IGNORED /* don't consider auto right margin glitch */
|
//#define RMAN_IGNORED /* don't consider auto right margin glitch */
|
||||||
//#define SCROLLVAR_NO /* disable intra-column horizontal scroll */
|
//#define SCROLLVAR_NO /* disable intra-column horizontal scroll */
|
||||||
//#define STRINGCASENO /* case insenstive compare/locate versions */
|
//#define STRINGCASENO /* case insenstive compare/locate versions */
|
||||||
//#define TERMIO_PROXY /* true line editing, beyond native input */
|
//#define TERMIOS_ONLY /* just limp along with native input only */
|
||||||
//#define TREE_NORESET /* sort keys do NOT force forest view OFF */
|
//#define TREE_NORESET /* sort keys do NOT force forest view OFF */
|
||||||
//#define USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */
|
//#define USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */
|
||||||
//#define VALIDATE_NLS /* validate the integrity of all nls tbls */
|
//#define VALIDATE_NLS /* validate the integrity of all nls tbls */
|
||||||
@ -140,18 +140,18 @@ char *strcasestr(const char *haystack, const char *needle);
|
|||||||
// support for keyboard stuff (cursor motion keystrokes, mostly)
|
// support for keyboard stuff (cursor motion keystrokes, mostly)
|
||||||
#define kbd_ESC '\033'
|
#define kbd_ESC '\033'
|
||||||
#define kbd_SPACE ' '
|
#define kbd_SPACE ' '
|
||||||
#define kbd_UP 0x01
|
#define kbd_ENTER 128
|
||||||
#define kbd_DOWN 0x02
|
#define kbd_UP 129
|
||||||
#define kbd_RIGHT 0x03
|
#define kbd_DOWN 130
|
||||||
#define kbd_LEFT 0x04
|
#define kbd_LEFT 131
|
||||||
#define kbd_PGUP 0x05
|
#define kbd_RIGHT 132
|
||||||
#define kbd_PGDN 0x06
|
#define kbd_PGUP 133
|
||||||
#define kbd_END 0x07
|
#define kbd_PGDN 134
|
||||||
#define kbd_HOME 0x08
|
#define kbd_HOME 135
|
||||||
#define kbd_BKSP 0x09
|
#define kbd_END 136
|
||||||
#define kbd_ENTER 0x0a // this is also the real ^J
|
#define kbd_BKSP 137
|
||||||
#define kbd_INS 0x0b
|
#define kbd_INS 138
|
||||||
#define kbd_DEL 0x0c
|
#define kbd_DEL 139
|
||||||
|
|
||||||
/* Special value in Pseudo_row to force an additional procs refresh
|
/* Special value in Pseudo_row to force an additional procs refresh
|
||||||
-- used at startup and for task/thread mode transitions */
|
-- used at startup and for task/thread mode transitions */
|
||||||
@ -635,7 +635,7 @@ typedef struct WIN_t {
|
|||||||
//atic char *alloc_s (const char *str);
|
//atic char *alloc_s (const char *str);
|
||||||
//atic inline int ioa (struct timespec *ts);
|
//atic inline int ioa (struct timespec *ts);
|
||||||
//atic int ioch (int ech, char *buf, unsigned cnt);
|
//atic int ioch (int ech, char *buf, unsigned cnt);
|
||||||
//atic int iokey (int init);
|
//atic int iokey (int action);
|
||||||
//atic char *ioline (const char *prompt);
|
//atic char *ioline (const char *prompt);
|
||||||
//atic int readfile (FILE *fp, char **baddr, size_t *bsize, size_t *bread);
|
//atic int readfile (FILE *fp, char **baddr, size_t *bsize, size_t *bread);
|
||||||
/*------ Small Utility routines ----------------------------------------*/
|
/*------ Small Utility routines ----------------------------------------*/
|
||||||
|
Loading…
Reference in New Issue
Block a user