summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Warner <james.warner@comcast.net>2022-06-15 00:00:00 -0500
committerCraig Small <csmall@dropbear.xyz>2022-06-19 20:58:51 +1000
commitd7cd21ebbab656a5c377895aa5edf445eaf8fde7 (patch)
tree6cfe942cd47a9487fce40f1dfd0cea2f26a4cf4a
parent57389eab336b6a54fa26e7065528508bdbbee306 (diff)
downloadprocps-ng-d7cd21ebbab656a5c377895aa5edf445eaf8fde7.tar.gz
top: enable processing utf8 multi-byte character input <=== port of newlib c20fef1d
______________________________ original newlib message This program was well equipped to properly handle utf8 multi-byte characters - except for one important area! If users typed any unicode character (shift+ctrl+u) or pasted a utf-8 multi-byte string as a response to some input prompt, those characters would simply be ignored since they would not pass the internal 'isprint' test. Well, now we can handle such data while preserving all line editing provisions such as insertions, deletions, destructive backspace, prior line recall (up/down) and those all important cursor left plus right arrow keys. [ we even support overtype mode for multi-byte stuff ] [ even though our gui emulator will not let us alter ] [ the cursor as confirmation (as we do at a console) ] Signed-off-by: Jim Warner <james.warner@comcast.net>
-rw-r--r--top/top.c110
1 files changed, 74 insertions, 36 deletions
diff --git a/top/top.c b/top/top.c
index daa9019..01430a7 100644
--- a/top/top.c
+++ b/top/top.c
@@ -1239,7 +1239,7 @@ static int iokey (int action) {
#ifdef TERMIOS_ONLY
char buf[SMLBUFSIZ], *pb;
#else
- static char buf[SMLBUFSIZ];
+ static char buf[MEDBUFSIZ];
static int pos, len;
char *pb;
#endif
@@ -1278,7 +1278,7 @@ static int iokey (int action) {
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
+ if (!iscntrl(buf[0])) { // no need for translation
len = strlen(buf);
pos = 1;
return buf[0];
@@ -1322,44 +1322,56 @@ static char *ioline (const char *prompt) {
#else
/*
- * Get line oriented interactive input from the user,
- * going way beyond native tty support by providing:
- * . true line editing, not just destructive backspace
- * . an input limit sensitive to current screen dimensions
- * . ability to recall prior strings for re-input/re-editing */
+ * Get some line oriented interactive input from the ol' user,
+ * going way, way beyond that native tty support by providing:
+ * . true input line editing, not just a destructive backspace
+ * . an input limit sensitive to the current screen dimensions
+ * . an ability to recall prior strings for editing & re-input */
static char *ioline (const char *prompt) {
+ #define setLEN ( len = strlen(buf) - utf8_delta(buf) )
+ #define setPOS(X) ( pos = utf8_embody(buf, X) )
+ #define utfCHR(X) ( (unsigned char *)&buf[X] )
+ #define utfTOT(X) ( UTF8_tab[(unsigned char)buf[X]] )
+ #define utfCOL(X) ( utf8_cols(utfCHR(X), utfTOT(X)) )
+ #define movBKW { setPOS(cur - 1); while (utfTOT(pos) < 0) --pos; }
+ #define chkCUR { if (cur < 0) cur = 0; if (cur > len) cur = len; }
+ // thank goodness ol' memmove will safely allow strings to overlap
+ #define sqzSTR { i = utfTOT(pos); while (i < 0) i = utfTOT(--pos); \
+ memmove(&buf[pos], &buf[pos + i], bufMAX-(pos + i)); \
+ memset(&buf[bufMAX - i], '\0', i); }
+ #define expSTR(X) if (bufNXT < bufMAX && scrNXT < Screen_cols) { \
+ memmove(&buf[pos + X], &buf[pos], bufMAX - pos); }
#define savMAX 50
- // thank goodness memmove allows the two strings to overlap
- #define sqzSTR { memmove(&buf[pos], &buf[pos+1], bufMAX-pos); \
- buf[sizeof(buf)-1] = '\0'; }
- #define expSTR if (len+1 < bufMAX && len+beg+1 < Screen_cols) { \
- memmove(&buf[pos+1], &buf[pos], bufMAX-pos); buf[pos] = ' '; }
- #define logCOL (pos+1)
- #define phyCOL (beg+pos+1)
+ #define bufNXT ( pos + 4 ) // four equals longest utf8 str
+ #define scrNXT ( beg + len + 2 ) // two due to multi-column char
#define bufMAX ((int)sizeof(buf)-2) // -1 for '\0' string delimeter
static char buf[MEDBUFSIZ+1]; // +1 for '\0' string delimeter
static int ovt;
- int beg, pos, len, key, i;
+ int beg, // the physical column where input began, buf[0]
+ cur, // the logical current column/insertion position
+ len, // the logical input length, thus the end column
+ pos, // the physical position in the buffer currently
+ key, i;
struct lin_s {
- struct lin_s *bkw; // ptr to older saved strs
- struct lin_s *fwd; // ptr to newer saved strs
- char *str; // the saved string
+ struct lin_s *bkw; // pointer for older saved strs
+ struct lin_s *fwd; // pointer for newer saved strs
+ char *str; // an actual saved input string
};
static struct lin_s *anchor, *plin;
if (!anchor) {
anchor = alloc_c(sizeof(struct lin_s));
- anchor->str = alloc_s(""); // top-of-stack == empty str
+ anchor->str = alloc_s(""); // the top-of-stack (empty str)
}
plin = anchor;
- pos = 0;
+ cur = len = pos = 0;
beg = show_pmt(prompt);
memset(buf, '\0', sizeof(buf));
+ // this may not work under a gui emulator (but linux console is ok)
putp(ovt ? Cap_curs_huge : Cap_curs_norm);
do {
fflush(stdout);
- len = strlen(buf);
key = iokey(IOKEY_NEXT);
switch (key) {
case 0:
@@ -1369,6 +1381,7 @@ static char *ioline (const char *prompt) {
buf[0] = kbd_ESC;
return buf;
case kbd_ENTER:
+ case kbd_BTAB: case kbd_PGUP: case kbd_PGDN:
continue;
case kbd_INS:
ovt = !ovt;
@@ -1377,45 +1390,63 @@ static char *ioline (const char *prompt) {
case kbd_DEL:
sqzSTR
break;
- case kbd_BKSP :
- if (0 < pos) { --pos; sqzSTR }
+ case kbd_BKSP:
+ if (0 < cur) { movBKW; cur -= utfCOL(pos); setPOS(cur); sqzSTR; }
break;
case kbd_LEFT:
- if (0 < pos) --pos;
+ if (0 < cur) { movBKW; cur -= utfCOL(pos); }
break;
case kbd_RIGHT:
- if (pos < len) ++pos;
+ if (cur < len) cur += utfCOL(pos);
break;
case kbd_HOME:
- pos = 0;
+ cur = pos = 0;
break;
case kbd_END:
- pos = len;
+ cur = len;
+ pos = strlen(buf);
break;
case kbd_UP:
if (plin->bkw) {
plin = plin->bkw;
memset(buf, '\0', sizeof(buf));
- pos = snprintf(buf, sizeof(buf), "%.*s", Screen_cols - beg - 1, plin->str);
+ memccpy(buf, plin->str, '\0', bufMAX);
+ cur = setLEN;
+ pos = strlen(buf);
}
break;
case kbd_DOWN:
- memset(buf, '\0', sizeof(buf));
if (plin->fwd) plin = plin->fwd;
- pos = snprintf(buf, sizeof(buf), "%.*s", Screen_cols - beg - 1, plin->str);
+ memset(buf, '\0', sizeof(buf));
+ memccpy(buf, plin->str, '\0', bufMAX);
+ cur = setLEN;
+ pos = strlen(buf);
break;
default: // what we REALLY wanted (maybe)
- if (isprint(key) && logCOL < bufMAX && phyCOL < Screen_cols) {
- if (!ovt) expSTR
+ if (bufNXT < bufMAX && scrNXT < Screen_cols) {
+ int tot = UTF8_tab[(unsigned char)key],
+ sav = pos;
+ if (tot < 1) tot = 1;
+ if (!ovt) { expSTR(tot); }
+ else { pos = utf8_embody(buf, cur); sqzSTR; expSTR(tot); }
buf[pos++] = key;
+ while (tot > 1) {
+ key = iokey(IOKEY_NEXT);
+ buf[pos++] = key;
+ --tot;
+ }
+ cur += utfCOL(sav);
}
break;
}
+ setLEN;
+ chkCUR;
+ setPOS(cur);
putp(fmtmk("%s%s%s", tg2(beg, Msg_row), Cap_clr_eol, buf));
#ifdef OVERTYPE_SEE
putp(fmtmk("%s%c", tg2(beg - 1, Msg_row), ovt ? '^' : ' '));
#endif
- putp(tg2(beg+pos, Msg_row));
+ putp(tg2(beg + cur, Msg_row));
} while (key != kbd_ENTER);
// weed out duplicates, including empty strings (top-of-stack)...
@@ -1454,11 +1485,18 @@ static char *ioline (const char *prompt) {
anchor->bkw = plin; // by sliding it in as new number 2!
return buf; // protect our copy, return original
- #undef savMAX
+ #undef setLEN
+ #undef setPOS
+ #undef utfCHR
+ #undef utfTOT
+ #undef utfCOL
+ #undef movBKW
+ #undef chkCUR
#undef sqzSTR
#undef expSTR
- #undef logCOL
- #undef phyCOL
+ #undef savMAX
+ #undef bufNXT
+ #undef scrNXT
#undef bufMAX
} // end: ioline
#endif