aboutsummaryrefslogtreecommitdiffstats
path: root/main/mg
diff options
context:
space:
mode:
authorCág <ca6c@bitmessage.ch>2017-06-07 19:35:44 +0100
committerLeonardo Arena <rnalrd@alpinelinux.org>2017-06-08 07:07:36 +0000
commit531857338496faa0c0b095a4198d17640c5b1699 (patch)
tree1e014f3b66524536421c1fcff17d448de4ea762b /main/mg
parent880887608d37f5ea2b5428cbfe721d8054b83660 (diff)
downloadaports-531857338496faa0c0b095a4198d17640c5b1699.tar.bz2
aports-531857338496faa0c0b095a4198d17640c5b1699.tar.xz
main/mg: upgrade to 20170401 and add unicode support
Diffstat (limited to 'main/mg')
-rw-r--r--main/mg/APKBUILD20
-rw-r--r--main/mg/missing-defines.patch34
-rw-r--r--main/mg/musl-fixes.patch75
-rw-r--r--main/mg/unicode.patch1347
4 files changed, 1390 insertions, 86 deletions
diff --git a/main/mg/APKBUILD b/main/mg/APKBUILD
index 4d3f36eadc..3e618c3552 100644
--- a/main/mg/APKBUILD
+++ b/main/mg/APKBUILD
@@ -1,8 +1,8 @@
# Contributor: Lee Hinman <hinman@gmail.com>
-# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
+# Maintainer: Cág <ca6c@bitmessage.ch>
pkgname=mg
-pkgver=20140414
-pkgrel=3
+pkgver=20170401
+pkgrel=0
pkgdesc="mg is Micro GNU/emacs, this is a portable version of the mg maintained by the OpenBSD team."
url="http://homepage.boetes.org/software/mg"
arch="x86 x86_64"
@@ -11,8 +11,9 @@ depends=
makedepends="ncurses-dev libbsd-dev bsd-compat-headers"
install=
subpackages="$pkgname-doc"
-source="http://dev.alpinelinux.org/archive/$pkgname-$pkgver.tar.gz
- musl-fixes.patch
+source="http://homepage.boetes.org/software/mg/$pkgname-$pkgver.tar.gz
+ missing-defines.patch
+ unicode.patch
"
_builddir="$srcdir"/$pkgname-$pkgver
@@ -38,9 +39,6 @@ package() {
install -Dm644 tutorial "$pkgdir"/usr/share/doc/$pkgname/tutorial
}
-md5sums="ad4a99a5e26d25db75af7936c6657fbe mg-20140414.tar.gz
-b09b7700ded811df8f6cf35f9a8df1ff musl-fixes.patch"
-sha256sums="89ce25ee13cedc14c3c5c7b76d7a9e0ea38a060ad61e90ac43258d8dadf0d065 mg-20140414.tar.gz
-f05ae4f0cd9475932ebe51e32ce05217e7fb59a5a0614efd2b33af4cc888323c musl-fixes.patch"
-sha512sums="b080b24c6015225ee1670eb6f062a21f40d34b02c699687d92842983506497c93e15cceb79d05b6cbbebfa8466f7261cb7dde8bcc4888489236cf64104215161 mg-20140414.tar.gz
-47ced85e221951c02654533a015be67018c045ad336a9997b70dcc869f10546318aeae8d1fbd43248d485e4f9c1c950deb93789b7aa8def493ba036af1beaaa1 musl-fixes.patch"
+sha512sums="729b2d9ba7eb236cc8bcbcf3ac7604494980cbf2fd21dc5a3856011bc830eee75be29a246b0bf3dfa6c7a03fcf69a902ae50d05ea371ac4d0225a4da6089d7ce mg-20170401.tar.gz
+f1967dd33a025e11ff63ba797c2fb64ac0679cd6c09a332241a28e0976b96db467894e1e3573c5bc88b7feaecdb430695aa4f8fa2712b227d0947b3d8b05c97c missing-defines.patch
+586a622b6838bb5b4f2e958352cec6bd4ca9c7db93bcc0dfeb6d8e588fb1191bc5f01afd17a385c10ddbd6ceb631d2d9caccd4e080294d1fceb5db676ab6a4fc unicode.patch"
diff --git a/main/mg/missing-defines.patch b/main/mg/missing-defines.patch
new file mode 100644
index 0000000000..58f5e109fa
--- /dev/null
+++ b/main/mg/missing-defines.patch
@@ -0,0 +1,34 @@
+diff --git a/fileio.c b/fileio.c
+index 4bb1f46..c937eb0 100644
+--- a/fileio.c
++++ b/fileio.c
+@@ -27,6 +27,14 @@
+ #include "kbd.h"
+ #include "pathnames.h"
+
++#ifndef MAXNAMLEN
++#define MAXNAMLEN 255
++#endif
++
++#ifndef DEFFILEMODE
++#define DEFFILEMODE 0666
++#endif
++
+ static char *bkuplocation(const char *);
+ static int bkupleavetmp(const char *);
+
+diff --git a/re_search.c b/re_search.c
+index 287030a..da44f00 100644
+--- a/re_search.c
++++ b/re_search.c
+@@ -34,6 +34,10 @@
+ #define RE_NMATCH 10 /* max number of matches */
+ #define REPLEN 256 /* max length of replacement string */
+
++#ifndef REG_STARTEND
++#define REG_STARTEND 4
++#endif
++
+ char re_pat[NPAT]; /* regex pattern */
+ int re_srch_lastdir = SRCH_NOPR; /* last search flags */
+ int casefoldsearch = TRUE; /* does search ignore case? */
diff --git a/main/mg/musl-fixes.patch b/main/mg/musl-fixes.patch
deleted file mode 100644
index 31f3fbcc6b..0000000000
--- a/main/mg/musl-fixes.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-diff --git a/GNUmakefile b/GNUmakefile
-index a89c697..ece5944 100644
---- a/GNUmakefile
-+++ b/GNUmakefile
-@@ -31,7 +31,7 @@ CURSES_LIBS= -lcurses
- CC= gcc
- CFLAGS?= -O2 -pipe
- CFLAGS+= -g -Wall
--CPPFLAGS= -DFKEYS -DREGEX -DXKEYS
-+CPPFLAGS= -DFKEYS -DXKEYS
- CPPFLAGS+= $(BSD_CPPFLAGS) -D__dead=__dead2
- LIBS= $(CURSES_LIBS) $(BSD_LIBS)
-
-diff --git a/Makefile b/Makefile
-index 023ed17..0c92790 100644
---- a/Makefile
-+++ b/Makefile
-@@ -13,7 +13,7 @@ DPADD+= ${LIBCURSES} ${LIBUTIL}
- # XKEYS -- use termcap function key definitions.
- # note: XKEYS and bsmap mode do _not_ get along.
- #
--CFLAGS+=-Wall -DFKEYS -DREGEX -DXKEYS
-+CFLAGS+=-Wall -DFKEYS -DXKEYS
-
- SRCS= autoexec.c basic.c bell.c buffer.c cinfo.c dir.c display.c \
- echo.c extend.c file.c fileio.c funmap.c help.c kbd.c keymap.c \
-diff --git a/main.c b/main.c
-index 9204d63..dfd4a6a 100644
---- a/main.c
-+++ b/main.c
-@@ -14,6 +14,10 @@
- #include <err.h>
- #include <locale.h>
-
-+#ifndef __dead2
-+#define __dead2
-+#endif
-+
- int thisflag; /* flags, this command */
- int lastflag; /* flags, last command */
- int curgoal; /* goal column */
-diff --git a/sysdef.h b/sysdef.h
-index 8d3d3a2..ea82d73 100644
---- a/sysdef.h
-+++ b/sysdef.h
-@@ -8,12 +8,7 @@
- #include <sys/param.h>
- #include <sys/queue.h>
-
--/* necesarry to get asprintf & friends with glibc XXX doesn't work for some
-- * mysterious reason! */
--#ifdef __GLIBC__
--# define _GNU_SOURCE
--# define __USE_GNU
--#endif
-+#define _GNU_SOURCE
-
- #if defined(__APPLE__) || defined(__FreeBSD__)
- # define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
-@@ -25,10 +20,15 @@
- #include <string.h>
- #include <errno.h>
- #include <signal.h>
-+#include <fcntl.h>
-
- #define KBLOCK 8192 /* Kill grow. */
- #define GOOD 0 /* Good exit status. */
-
-+#ifndef DEFFILEMODE /* 4.4BSD extension. */
-+#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
-+#endif
-+
- typedef int RSIZE; /* Type for file/region sizes */
- typedef short KCHAR; /* Type for internal keystrokes */
-
diff --git a/main/mg/unicode.patch b/main/mg/unicode.patch
new file mode 100644
index 0000000000..39d8831f68
--- /dev/null
+++ b/main/mg/unicode.patch
@@ -0,0 +1,1347 @@
+diff --git a/README.md b/README.md
+index 994d6be..2f5d2c1 100644
+--- a/README.md
++++ b/README.md
+@@ -1,49 +1,50 @@
+-# PORTING MG AND USING LIBBSD
++# mg with wide display support.
+
+-I've maintained and ported mg for quite some time now and at first it
+-was easy recently it got harder and harder since it was a moving
+-target. Especially the inclusion of some system specific libraries since
+-about 2 years ago made it too much of an effort for my humble coding
+-skills.
+-
+-So recently Jasper Lievisse Adriaanse asked me to try it again and I
+-restarted working on the project and ran into exactly the same problems
+-again. While googling for solutions I ran into libbsd:
+-
+- http://libbsd.freedesktop.org/wiki/
+-
+-It's a porting library for OpenBSD code! And after installing that it
+-was a piece of pie to get mg ported again.
+-
+-## PORTING TO ALL OTHER PLATFORMS
+-
+-Okay, that was debian. Now I have to get the rest of all the previously
+-suported platforms working again. All help is welcome and as always:
+-Please provide patches that do not break stuff for other platforms.
+-
+-## BUILDING MG
+-
+-So, basic instructions for building mg:
+-
+- - Get libbsd installed.
+- - Run the following commands:
++I just received this amazing patch from S. Giles. For those impatient
++people: check it out (tihihi) and add this line to your `.mg`
+
+ ```
+-make
+-sudo make install
++set-default-mode "wide"
+ ```
+
+-## USING CVS
+-
+-This code is the cvs checkout from the OpenBSD project so if you install
+-cvs you can see what I changed to port mg. Like this:
+-
+-```
+-cvs diff -uw
+-```
+-
+-## ABOUT fgetln()
+-
+-Incase you are wondering about that deprecation warning, here is a nice explanation about why it is hard to fix:
+-
+- http://niallohiggins.com/2009/10/03/read-a-file-line-by-line-in-c-secure-fgets-idiom/
++## Introduction by S. Giles
++
++Hi,
++
++I've got a patch that allows mg to display wide characters, if you're
++interested.
++
++It can be turned on by show-wide-mode (better name welcome), and is
++fairly limited in regard to what types of wide characters are
++displayed. Everything goes through mbrtowc(3), so you get exactly one
++supported encoding: whatever LC_* says. Everything else is displayed
++as octal escape sequences (as normal current behavior). Motion is
++still on a byte level, so multibyte characters are slow to travel
++through, and you can insert bytes in the middle of them (which works
++fine). A limited version of insert-char is also included, which works
++through wchar_t, so that on any system with __STDC_ISO_10646__ set,
++inserting unicode codepoints by number is possible.
++
++It also fixes some odd bugs related to wide character display and
++extended lines. For example: in a file with enough wide characters
++(such as ABC) to make a line extend far (say, 200 characters on an
++80-wide display), moving to the right one character at a time will (in
++20160118) corrupt the display, then eventually segfault, because
++vtpute doesn't perform the same octal expansion as vtputc and the
++columns get out of sync. This patch makes display.c aware of the
++possibility that the bytes and glyphs of the buffers aren't 1:1, so
++protects against that.
++
++That said, wide character support complicates a lot of already
++complicated logic (for example, vtputs) and relies on wchar_t for
++almost everything, adding some unescapable overhead.
++
++If you want to take this patch, please do so. If you think it's too
++ugly or not useful, that's also fine. Let me know if you want me to
++rewrite parts of it (or if you see any bugs) or if there are style
++conventions I didn't follow. It applies cleanly with patch -i, please
++forgive the git-isms.
++
++(And, of course, many thanks for your work in maintaining the port.)
++
++S. Gilles
+diff --git a/basic.c b/basic.c
+index 85d9f70..123e115 100644
+--- a/basic.c
++++ b/basic.c
+@@ -18,6 +18,7 @@
+ #include <signal.h>
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <wchar.h>
+
+ #include "def.h"
+
+@@ -269,12 +270,25 @@ setgoal(void)
+ int
+ getgoal(struct line *dlp)
+ {
+- int c, i, col = 0;
+- char tmp[5];
++ return getbyteofcol(dlp, 0, curgoal);
++}
+
++/*
++ * Return the byte offset within lp that is targetcol columns beyond
++ * startbyte
++ */
++size_t
++getbyteofcol(const struct line *lp, const size_t startbyte,
++ const size_t targetcol)
++{
++ int c;
++ size_t i, col = 0;
++ char tmp[5];
++ size_t advance_by = 1;
+
+- for (i = 0; i < llength(dlp); i++) {
+- c = lgetc(dlp, i);
++ for (i = startbyte; i < llength(lp); i += advance_by) {
++ advance_by = 1;
++ c = lgetc(lp, i);
+ if (c == '\t'
+ #ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+@@ -284,18 +298,86 @@ getgoal(struct line *dlp)
+ col++;
+ } else if (ISCTRL(c) != FALSE) {
+ col += 2;
+- } else if (isprint(c))
++ } else if (isprint(c)) {
+ col++;
+- else {
++ } else if (!(curbp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumed = mbrtowc(&wc, &lp->l_text[i],
++ llength(lp) - i, &mbs);
++ int width = -1;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ }
++ if (width >= 0) {
++ col += width;
++ advance_by = consumed;
++ } else {
++ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
++ }
++ } else {
+ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
+ }
+- if (col > curgoal)
++ if (col > targetcol)
+ break;
+ }
+ return (i);
+ }
+
+ /*
++ * Return the column at which specified offset byte would appear, if
++ * this were part of a longer string printed by vtputs, starting at
++ * intial_col
++ */
++size_t
++getcolofbyte(const struct line *lp, const size_t startbyte,
++ const size_t initial_col, const size_t targetoffset)
++{
++ int c;
++ size_t i, col = initial_col;
++ char tmp[5];
++ size_t advance_by = 1;
++
++ for (i = startbyte; i < llength(lp); i += advance_by) {
++ if (i >= targetoffset)
++ break;
++ advance_by = 1;
++ c = lgetc(lp, i);
++ if (c == '\t'
++#ifdef NOTAB
++ && !(curbp->b_flag & BFNOTAB)
++#endif
++ ) {
++ col |= 0x07;
++ col++;
++ } else if (ISCTRL(c) != FALSE) {
++ col += 2;
++ } else if (isprint(c)) {
++ col++;
++ } else if (!(curbp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumed = mbrtowc(&wc, &lp->l_text[i],
++ llength(lp) - i, &mbs);
++ int width = -1;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ }
++ if (width >= 0) {
++ col += width;
++ advance_by = consumed;
++ } else {
++ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
++ }
++ } else {
++ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
++ }
++ }
++ return (col);
++}
++
++
++/*
+ * Scroll forward by a specified number
+ * of lines, or by a full page if no argument.
+ * The "2" is the window overlap (this is the default
+diff --git a/cmode.c b/cmode.c
+index a238285..9d4cac3 100644
+--- a/cmode.c
++++ b/cmode.c
+@@ -14,6 +14,7 @@
+ #include <ctype.h>
+ #include <signal.h>
+ #include <stdio.h>
++#include <wchar.h>
+
+ #include "def.h"
+ #include "funmap.h"
+@@ -419,10 +420,25 @@ findcolpos(const struct buffer *bp, const struct line *lp, int lo)
+ ) {
+ col |= 0x07;
+ col++;
+- } else if (ISCTRL(c) != FALSE)
++ } else if (ISCTRL(c) != FALSE) {
+ col += 2;
+- else if (isprint(c)) {
++ } else if (isprint(c)) {
+ col++;
++ } else if (!(bp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumed = mbrtowc(&wc, &lp->l_text[i],
++ llength(lp) - i, &mbs);
++ int width = -1;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ }
++ if (width >= 0) {
++ col += width;
++ i += (consumed - 1);
++ } else {
++ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
++ }
+ } else {
+ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
+ }
+diff --git a/def.h b/def.h
+index 54b74ce..2fb289d 100644
+--- a/def.h
++++ b/def.h
+@@ -270,7 +270,7 @@ struct buffer {
+ int b_marko; /* ditto for the "mark" */
+ short b_nmodes; /* number of non-fundamental modes */
+ char b_nwnd; /* Count of windows on buffer */
+- char b_flag; /* Flags */
++ short b_flag; /* Flags */
+ char b_fname[NFILEN]; /* File name */
+ char b_cwd[NFILEN]; /* working directory */
+ struct fileinfo b_fi; /* File attributes */
+@@ -297,6 +297,7 @@ struct buffer {
+ #define BFDIRTY 0x20 /* Buffer was modified elsewhere */
+ #define BFIGNDIRTY 0x40 /* Ignore modifications */
+ #define BFDIREDDEL 0x80 /* Dired has a deleted 'D' file */
++#define BFSHOWRAW 0x100 /* Show unprintable as octal */
+ /*
+ * This structure holds information about recent actions for the Undo command.
+ */
+@@ -497,6 +498,7 @@ int digit_argument(int, int);
+ int negative_argument(int, int);
+ int selfinsert(int, int);
+ int quote(int, int);
++int insert_char(int, int);
+
+ /* main.c */
+ int ctrlg(int, int);
+@@ -519,6 +521,8 @@ int forwline(int, int);
+ int backline(int, int);
+ void setgoal(void);
+ int getgoal(struct line *);
++size_t getbyteofcol(const struct line *, size_t, size_t);
++size_t getcolofbyte(const struct line *, size_t, size_t, size_t);
+ int forwpage(int, int);
+ int backpage(int, int);
+ int forw1page(int, int);
+@@ -665,6 +669,7 @@ int notabmode(int, int);
+ #endif /* NOTAB */
+ int overwrite_mode(int, int);
+ int set_default_mode(int,int);
++int show_raw_mode(int, int);
+
+ #ifdef REGEX
+ /* re_search.c X */
+diff --git a/display.c b/display.c
+index 7af723c..104f873 100644
+--- a/display.c
++++ b/display.c
+@@ -7,7 +7,7 @@
+ * redisplay system knows almost nothing about the editing
+ * process; the editing functions do, however, set some
+ * hints to eliminate a lot of the grinding. There is more
+- * that can be done; the "vtputc" interface is a real
++ * that can be done; the "vtputs" interface is a real
+ * pig.
+ */
+
+@@ -18,6 +18,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <term.h>
++#include <wchar.h>
+
+ #include "def.h"
+ #include "kbd.h"
+@@ -52,11 +53,10 @@ struct score {
+ };
+
+ void vtmove(int, int);
+-void vtputc(int);
+ void vtpute(int);
+-int vtputs(const char *);
++int vtputs(const char *, size_t, size_t);
+ void vteeol(void);
+-void updext(int, int);
++int updext(int, int);
+ void modeline(struct mgwin *, int);
+ void setscores(int, int);
+ void traceback(int, int, int, int);
+@@ -216,8 +216,8 @@ vtresize(int force, int newrow, int newcol)
+ }
+ if (rowchanged || colchanged || first_run) {
+ for (i = 0; i < 2 * (newrow - 1); i++)
+- TRYREALLOC(video[i].v_text, newcol);
+- TRYREALLOC(blanks.v_text, newcol);
++ TRYREALLOC(video[i].v_text, newcol * MB_CUR_MAX);
++ TRYREALLOC(blanks.v_text, newcol * MB_CUR_MAX);
+ }
+
+ nrow = newrow;
+@@ -260,7 +260,7 @@ vtinit(void)
+ */
+
+ blanks.v_color = CTEXT;
+- for (i = 0; i < ncol; ++i)
++ for (i = 0; i < ncol * MB_CUR_MAX; ++i)
+ blanks.v_text[i] = ' ';
+ }
+
+@@ -287,7 +287,7 @@ vttidy(void)
+ * Move the virtual cursor to an origin
+ * 0 spot on the virtual display screen. I could
+ * store the column as a character pointer to the spot
+- * on the line, which would make "vtputc" a little bit
++ * on the line, which would make "vtputs" a little bit
+ * more efficient. No checking for errors.
+ */
+ void
+@@ -298,82 +298,6 @@ vtmove(int row, int col)
+ }
+
+ /*
+- * Write a character to the virtual display,
+- * dealing with long lines and the display of unprintable
+- * things like control characters. Also expand tabs every 8
+- * columns. This code only puts printing characters into
+- * the virtual display image. Special care must be taken when
+- * expanding tabs. On a screen whose width is not a multiple
+- * of 8, it is possible for the virtual cursor to hit the
+- * right margin before the next tab stop is reached. This
+- * makes the tab code loop if you are not careful.
+- * Three guesses how we found this.
+- */
+-void
+-vtputc(int c)
+-{
+- struct video *vp;
+-
+- c &= 0xff;
+-
+- vp = vscreen[vtrow];
+- if (vtcol >= ncol)
+- vp->v_text[ncol - 1] = '$';
+- else if (c == '\t'
+-#ifdef NOTAB
+- && !(curbp->b_flag & BFNOTAB)
+-#endif
+- ) {
+- do {
+- vtputc(' ');
+- } while (vtcol < ncol && (vtcol & 0x07) != 0);
+- } else if (ISCTRL(c)) {
+- vtputc('^');
+- vtputc(CCHR(c));
+- } else if (isprint(c))
+- vp->v_text[vtcol++] = c;
+- else {
+- char bf[5];
+-
+- snprintf(bf, sizeof(bf), "\\%o", c);
+- vtputs(bf);
+- }
+-}
+-
+-/*
+- * Put a character to the virtual screen in an extended line. If we are not
+- * yet on left edge, don't print it yet. Check for overflow on the right
+- * margin.
+- */
+-void
+-vtpute(int c)
+-{
+- struct video *vp;
+-
+- c &= 0xff;
+-
+- vp = vscreen[vtrow];
+- if (vtcol >= ncol)
+- vp->v_text[ncol - 1] = '$';
+- else if (c == '\t'
+-#ifdef NOTAB
+- && !(curbp->b_flag & BFNOTAB)
+-#endif
+- ) {
+- do {
+- vtpute(' ');
+- } while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol);
+- } else if (ISCTRL(c) != FALSE) {
+- vtpute('^');
+- vtpute(CCHR(c));
+- } else {
+- if (vtcol >= 0)
+- vp->v_text[vtcol] = c;
+- ++vtcol;
+- }
+-}
+-
+-/*
+ * Erase from the end of the software cursor to the end of the line on which
+ * the software cursor is located. The display routines will decide if a
+ * hardware erase to end of line command should be used to display this.
+@@ -384,7 +308,7 @@ vteeol(void)
+ struct video *vp;
+
+ vp = vscreen[vtrow];
+- while (vtcol < ncol)
++ while (vtcol < ncol * MB_CUR_MAX)
+ vp->v_text[vtcol++] = ' ';
+ }
+
+@@ -404,7 +328,7 @@ update(int modelinecolor)
+ struct mgwin *wp;
+ struct video *vp1;
+ struct video *vp2;
+- int c, i, j;
++ int c, i;
+ int hflag;
+ int currow, curcol;
+ int offs, size;
+@@ -479,8 +403,9 @@ update(int modelinecolor)
+ vscreen[i]->v_color = CTEXT;
+ vscreen[i]->v_flag |= (VFCHG | VFHBAD);
+ vtmove(i, 0);
+- for (j = 0; j < llength(lp); ++j)
+- vtputc(lgetc(lp, j));
++ if (llength(lp)) {
++ vtputs(lp->l_text, llength(lp), 0);
++ }
+ vteeol();
+ } else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) {
+ hflag = TRUE;
+@@ -489,8 +414,10 @@ update(int modelinecolor)
+ vscreen[i]->v_flag |= (VFCHG | VFHBAD);
+ vtmove(i, 0);
+ if (lp != wp->w_bufp->b_headp) {
+- for (j = 0; j < llength(lp); ++j)
+- vtputc(lgetc(lp, j));
++ if (llength(lp)) {
++ vtputs(lp->l_text, llength(lp),
++ 0);
++ }
+ lp = lforw(lp);
+ }
+ vteeol();
+@@ -508,32 +435,53 @@ update(int modelinecolor)
+ ++currow;
+ lp = lforw(lp);
+ }
++
+ curcol = 0;
+ i = 0;
+ while (i < curwp->w_doto) {
+- c = lgetc(lp, i++);
++ char tmp[5];
++ c = lgetc(lp, i);
+ if (c == '\t'
+ #ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+ #endif
+ ) {
+- curcol |= 0x07;
+ curcol++;
++ while ((curcol - lbound) & 0x07) {
++ curcol++;
++ }
+ } else if (ISCTRL(c) != FALSE)
+ curcol += 2;
+- else if (isprint(c))
++ else if (isprint(c)) {
+ curcol++;
+- else {
+- char bf[5];
+-
+- snprintf(bf, sizeof(bf), "\\%o", c);
+- curcol += strlen(bf);
++ } else if (!(curbp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumed = mbrtowc(&wc, &lp->l_text[i],
++ llength(lp) - i, &mbs);
++ int width = -1;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ } else {
++ memset(&mbs, 0, sizeof mbs);
++ }
++ if (width >= 0) {
++ curcol += width;
++ i += (consumed - 1);
++ } else {
++ snprintf(tmp, sizeof(tmp), "\\%o", c);
++ curcol += strlen(tmp);
++ }
++ } else {
++ snprintf(tmp, sizeof(tmp), "\\%o", c);
++ curcol += strlen(tmp);
+ }
++ i++;
+ }
+ if (curcol >= ncol - 1) { /* extended line. */
+ /* flag we are extended and changed */
+ vscreen[currow]->v_flag |= VFEXT | VFCHG;
+- updext(currow, curcol); /* and output extended line */
++ curcol = updext(currow, curcol);
+ } else
+ lbound = 0; /* not extended line */
+
+@@ -552,8 +500,10 @@ update(int modelinecolor)
+ if ((wp != curwp) || (lp != wp->w_dotp) ||
+ (curcol < ncol - 1)) {
+ vtmove(i, 0);
+- for (j = 0; j < llength(lp); ++j)
+- vtputc(lgetc(lp, j));
++ if (llength(lp)) {
++ vtputs(lp->l_text, llength(lp),
++ 0);
++ }
+ vteeol();
+ /* this line no longer is extended */
+ vscreen[i]->v_flag &= ~VFEXT;
+@@ -655,39 +605,44 @@ ucopy(struct video *vvp, struct video *pvp)
+ pvp->v_hash = vvp->v_hash;
+ pvp->v_cost = vvp->v_cost;
+ pvp->v_color = vvp->v_color;
+- bcopy(vvp->v_text, pvp->v_text, ncol);
++ bcopy(vvp->v_text, pvp->v_text, ncol * MB_CUR_MAX);
+ }
+
+ /*
+- * updext: update the extended line which the cursor is currently on at a
+- * column greater than the terminal width. The line will be scrolled right or
+- * left to let the user see where the cursor is.
++ * updext: update the extended line which the cursor is currently on
++ * at a column greater than the terminal width. The line will be
++ * scrolled right or left to let the user see where the cursor
++ * is. curcol may need to be adjusted, depending on how wide
++ * characters and lbound interact, that adjusted position is returned.
+ */
+-void
++int
+ updext(int currow, int curcol)
+ {
+- struct line *lp; /* pointer to current line */
+- int j; /* index into line */
++ struct line *lp = curwp->w_dotp; /* pointer to current line */
++ size_t startbyte;
++ int bettercol = curcol;
++ size_t fullextent;
+
+ if (ncol < 2)
+- return;
++ return curcol;
+
+ /*
+- * calculate what column the left bound should be
+- * (force cursor into middle half of screen)
++ * calculate what column the left bound should be (force
++ * cursor into middle half of screen). Ensuring that it is at
++ * a tabstop allows update() to calculate curcol without
++ * wondering how tabstops are calculated before the first '$'.
+ */
+ lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
+-
+- /*
+- * scan through the line outputing characters to the virtual screen
+- * once we reach the left edge
+- */
+- vtmove(currow, -lbound); /* start scanning offscreen */
+- lp = curwp->w_dotp; /* line to output */
+- for (j = 0; j < llength(lp); ++j) /* until the end-of-line */
+- vtpute(lgetc(lp, j));
+- vteeol(); /* truncate the virtual line */
+- vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */
++ lbound = (lbound | 0x07) + 1;
++ vscreen[currow]->v_text[0] = '$';
++ vtmove(currow, 1);
++ startbyte = getbyteofcol(lp, 0, lbound + 1);
++ fullextent = getbyteofcol(lp, startbyte, ncol + 1);
++ vtputs(lp->l_text + startbyte, fullextent - startbyte, 1);
++ vteeol();
++
++ bettercol = lbound + getcolofbyte(lp, startbyte, 1, curwp->w_doto);
++ return (bettercol);
+ }
+
+ /*
+@@ -702,12 +657,35 @@ updext(int currow, int curcol)
+ void
+ uline(int row, struct video *vvp, struct video *pvp)
+ {
+- char *cp1;
+- char *cp2;
+- char *cp3;
+- char *cp4;
+- char *cp5;
++ char *cp1; /* Pointer to the start of dirty region */
++ char *cp2; /* pvp's counterpart for cp1 */
++ char *cp3; /* Pointer to end of dirty region */
++ char *cp4; /* pvp's counterpart for cp3 */
++ char *cp5; /* After this, within dirty region, all is ' ' */
++ char *mbcounter;
+ int nbflag;
++ int startcol; /* onscreen column matching cp1 */
++ char *lastbyte; /* byte which handles last onscreen column */
++ int seencols = 0;
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++
++ lastbyte = vvp->v_text;
++ while (seencols < ncol && *lastbyte) {
++ size_t consumed = mbrtowc(&wc, lastbyte,
++ (vvp->v_text + ncol * MB_CUR_MAX - lastbyte), &mbs);
++ if (consumed < (size_t) -2) {
++ lastbyte += consumed;
++ seencols += wcwidth(wc);
++ } else {
++ lastbyte++;
++ seencols++;
++ memset(&mbs, 0, sizeof mbs);
++ }
++ }
++ if (lastbyte - vvp->v_text < ncol) {
++ lastbyte = &vvp->v_text[ncol];
++ }
+
+ if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */
+ ttmove(row, 0); /* full redraw. */
+@@ -723,11 +701,12 @@ uline(int row, struct video *vvp, struct video *pvp)
+ * putting the invisible glitch character on the next line.
+ * (Hazeltine executive 80 model 30)
+ */
+- cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ?
+- (magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)];
++ cp2 = lastbyte -
++ (magic_cookie_glitch >= 0 ? (magic_cookie_glitch != 0 ?
++ magic_cookie_glitch : 1) : 0);
+ #else
+ cp1 = &vvp->v_text[0];
+- cp2 = &vvp->v_text[ncol];
++ cp2 = lastbyte;
+ #endif
+ while (cp1 != cp2) {
+ ttputc(*cp1++);
+@@ -738,21 +717,31 @@ uline(int row, struct video *vvp, struct video *pvp)
+ }
+ cp1 = &vvp->v_text[0]; /* Compute left match. */
+ cp2 = &pvp->v_text[0];
+- while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
++ while (cp1 != lastbyte && cp1[0] == cp2[0]) {
+ ++cp1;
+ ++cp2;
+ }
+- if (cp1 == &vvp->v_text[ncol]) /* All equal. */
++ if (cp1 == lastbyte) /* All equal. */
+ return;
++ while (cp1 != vvp->v_text && !isprint(*cp1) &&
++ mbrtowc(&wc, cp1, (lastbyte - cp1), &mbs) >= (size_t) -2) {
++ --cp1;
++ --cp2;
++ }
+ nbflag = FALSE;
+- cp3 = &vvp->v_text[ncol]; /* Compute right match. */
+- cp4 = &pvp->v_text[ncol];
++ cp3 = lastbyte; /* Compute right match. */
++ cp4 = &pvp->v_text[lastbyte - vvp->v_text];
+ while (cp3[-1] == cp4[-1]) {
+ --cp3;
+ --cp4;
+ if (cp3[0] != ' ') /* Note non-blanks in */
+ nbflag = TRUE; /* the right match. */
+ }
++ while (cp3 != lastbyte && !isprint(*cp3) &&
++ mbrtowc(&wc, cp3, (lastbyte - cp3), &mbs) >= (size_t) -2) {
++ ++cp3;
++ ++cp4;
++ }
+ cp5 = cp3; /* Is erase good? */
+ if (nbflag == FALSE && vvp->v_color == CTEXT) {
+ while (cp5 != cp1 && cp5[-1] == ' ')
+@@ -762,13 +751,27 @@ uline(int row, struct video *vvp, struct video *pvp)
+ cp5 = cp3;
+ }
+ /* Alcyon hack */
+- ttmove(row, (int) (cp1 - &vvp->v_text[0]));
++ startcol = 0;
++ mbcounter = vvp->v_text;
++ while ((cp1 - mbcounter) > 0) {
++ size_t consumed = mbrtowc(&wc, mbcounter, (cp1 - mbcounter),
++ &mbs);
++ if (consumed < (size_t) -2) {
++ mbcounter += consumed;
++ startcol += wcwidth(wc);
++ } else {
++ mbcounter++;
++ startcol++;
++ memset(&mbs, 0, sizeof mbs);
++ }
++ }
++ ttmove(row, startcol);
+ #ifdef STANDOUT_GLITCH
+ if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) {
+ if (cp1 < &vvp->v_text[magic_cookie_glitch])
+ cp1 = &vvp->v_text[magic_cookie_glitch];
+- if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch])
+- cp5 = &vvp->v_text[ncol - magic_cookie_glitch];
++ if (cp5 > lastbyte - magic_cookie_glitch)
++ cp5 = lastbyte - magic_cookie_glitch;
+ } else if (magic_cookie_glitch < 0)
+ #endif
+ ttcolor(vvp->v_color);
+@@ -801,46 +804,39 @@ modeline(struct mgwin *wp, int modelinecolor)
+ vscreen[n]->v_flag |= (VFCHG | VFHBAD); /* Recompute, display. */
+ vtmove(n, 0); /* Seek to right line. */
+ bp = wp->w_bufp;
+- vtputc('-');
+- vtputc('-');
++ n = vtputs("--", 0, 0);
+ if ((bp->b_flag & BFREADONLY) != 0) {
+- vtputc('%');
++ n += vtputs("%", 0, n);
+ if ((bp->b_flag & BFCHG) != 0)
+- vtputc('*');
++ n += vtputs("*", 0, n);
+ else
+- vtputc('%');
++ n += vtputs("%", 0, n);
+ } else if ((bp->b_flag & BFCHG) != 0) { /* "*" if changed. */
+- vtputc('*');
+- vtputc('*');
++ n += vtputs("**", 0, n);
+ } else {
+- vtputc('-');
+- vtputc('-');
++ n += vtputs("--", 0, n);
+ }
+- vtputc('-');
++ n += vtputs("-", 0, n);
+ n = 5;
+- n += vtputs("Mg: ");
++ n += vtputs("Mg: ", 0, n);
+ if (bp->b_bname[0] != '\0')
+- n += vtputs(&(bp->b_bname[0]));
++ n += vtputs(&(bp->b_bname[0]), 0, n);
+ while (n < 42) { /* Pad out with blanks. */
+- vtputc(' ');
+- ++n;
++ n += vtputs(" ", 0, n);
+ }
+- vtputc('(');
+- ++n;
++ n += vtputs("(", 0, n);
+ for (md = 0; ; ) {
+- n += vtputs(bp->b_modes[md]->p_name);
++ n += vtputs(bp->b_modes[md]->p_name, 0, n);
+ if (++md > bp->b_nmodes)
+ break;
+- vtputc('-');
+- ++n;
++ n += vtputs("-", 0, n);
+ }
+ /* XXX These should eventually move to a real mode */
+ if (macrodef == TRUE)
+- n += vtputs("-def");
++ n += vtputs("-def", 0, n);
+ if (globalwd == TRUE)
+- n += vtputs("-gwd");
+- vtputc(')');
+- ++n;
++ n += vtputs("-gwd", 0, n);
++ n += vtputs(")", 0, n);
+
+ if (linenos && colnos)
+ len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline,
+@@ -850,27 +846,132 @@ modeline(struct mgwin *wp, int modelinecolor)
+ else if (colnos)
+ len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp));
+ if ((linenos || colnos) && len < sizeof(sl) && len != -1)
+- n += vtputs(sl);
++ n += vtputs(sl, 0, n);
+
+ while (n < ncol) { /* Pad out. */
+- vtputc('-');
+- ++n;
++ n += vtputs("-", 0, n);
+ }
+ }
+
+ /*
+- * Output a string to the mode line, report how long it was.
++ * Output a string to the mode line, report how long it was,
++ * dealing with long lines and the display of unprintable
++ * things like control characters. Also expand tabs every 8
++ * columns. This code only puts printing characters into
++ * the virtual display image. Special care must be taken when
++ * expanding tabs. On a screen whose width is not a multiple
++ * of 8, it is possible for the virtual cursor to hit the
++ * right margin before the next tab stop is reached. This
++ * makes the tab code loop if you are not careful.
++ * Three guesses how we found this.
+ */
+ int
+-vtputs(const char *s)
++vtputs(const char *s, const size_t max_bytes, const size_t initial_col)
+ {
+- int n = 0;
++ const unsigned char *us = (const unsigned char *) s;
++ struct video *vp = vscreen[vtrow];
++ size_t bytes_handled = 0;
++ size_t last_full_byte_start = vtcol;
++ size_t space_printed = 0;
++
++ if (!s) {
++ return (0);
++ }
++
++ while (*us && (!max_bytes || bytes_handled < max_bytes)) {
++ if (space_printed + initial_col >= ncol) {
++ break;
++ } else if (*us == '\t'
++#ifdef NOTAB
++ && !(curbp->b_flag & BFNOTAB)
++#endif
++ ) {
++ last_full_byte_start = vtcol;
++ do {
++ if (vtcol >= 0) {
++ last_full_byte_start = vtcol;
++ vp->v_text[vtcol] = ' ';
++ }
++ vtcol++;
++ space_printed++;
++ } while (space_printed + initial_col < ncol &&
++ ((space_printed + initial_col) & 0x07));
++ us++;
++ bytes_handled++;
++ } else if (ISCTRL(*us)) {
++ last_full_byte_start = vtcol;
++ if (vtcol >= 0) {
++ vp->v_text[vtcol] = '^';
++ }
++ vtcol++;
++ if (vtcol >= 0) {
++ vp->v_text[vtcol] = CCHR(*us);
++ }
++ vtcol++;
++ bytes_handled++;
++ space_printed += 2;
++ us++;
++ } else if (isprint(*us)) {
++ last_full_byte_start = vtcol;
++ if (vtcol >= 0) {
++ vp->v_text[vtcol] = *us++;
++ }
++ vtcol++;
++ bytes_handled++;
++ space_printed++;
++ } else if (!(curbp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumable = max_bytes ?
++ (max_bytes - bytes_handled) : -1;
++ size_t consumed = mbrtowc(&wc, (const char *)us,
++ consumable, &mbs);
++ int width = -1;
++ last_full_byte_start = vtcol;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ }
++ if (width >= 0) {
++ bytes_handled += consumed;
++ space_printed += width;
++ do {
++ if (vtcol >= 0) {
++ vp->v_text[vtcol] = *us++;
++ }
++ vtcol++;
++ } while (--consumed);
++ } else {
++ char bf[5];
++ snprintf(bf, sizeof(bf), "\\%o", *us);
++ bytes_handled++;
++ space_printed += vtputs(bf, 0,
++ space_printed + initial_col);
++ us++;
++ }
++ } else {
++ char bf[5];
++ last_full_byte_start = vtcol;
++ snprintf(bf, sizeof(bf), "\\%o", *us);
++ bytes_handled++;
++ space_printed += vtputs(bf, 0,
++ space_printed + initial_col);
++ us++;
++ }
++ }
+
+- while (*s != '\0') {
+- vtputc(*s++);
+- ++n;
++ if ((space_printed + initial_col > ncol) ||
++ (space_printed + initial_col == ncol &&
++ (*us && (!max_bytes || bytes_handled < max_bytes)))) {
++ vp->v_text[last_full_byte_start] = '$';
++ while (++last_full_byte_start <= vtcol) {
++ vp->v_text[last_full_byte_start] = ' ';
++ }
++ bytes_handled++;
++ space_printed++;
++ us++;
+ }
+- return (n);
++
++ return (space_printed);
+ }
+
+ /*
+@@ -888,11 +989,11 @@ hash(struct video *vp)
+ char *s;
+
+ if ((vp->v_flag & VFHBAD) != 0) { /* Hash bad. */
+- s = &vp->v_text[ncol - 1];
+- for (i = ncol; i != 0; --i, --s)
++ s = &vp->v_text[ncol * MB_CUR_MAX - 1];
++ for (i = ncol * MB_CUR_MAX; i != 0; --i, --s)
+ if (*s != ' ')
+ break;
+- n = ncol - i; /* Erase cheaper? */
++ n = ncol * MB_CUR_MAX - i; /* Erase cheaper? */
+ if (n > tceeol)
+ n = tceeol;
+ vp->v_cost = i + n; /* Bytes + blanks. */
+diff --git a/echo.c b/echo.c
+index 6966c00..22b8333 100644
+--- a/echo.c
++++ b/echo.c
+@@ -844,9 +844,11 @@ ewprintf(const char *fmt, ...)
+ * %k prints the name of the current key (and takes no arguments).
+ * %d prints a decimal integer
+ * %o prints an octal integer
++ * %x prints a hexadecimal integer
+ * %p prints a pointer
+ * %s prints a string
+ * %ld prints a long word
++ * %lx prints a hexadecimal long word
+ * Anything else is echoed verbatim
+ */
+ static void
+@@ -885,6 +887,10 @@ eformat(const char *fp, va_list ap)
+ eputi(va_arg(ap, int), 8);
+ break;
+
++ case 'x':
++ eputi(va_arg(ap, int), 16);
++ break;
++
+ case 'p':
+ snprintf(tmp, sizeof(tmp), "%p",
+ va_arg(ap, void *));
+@@ -902,6 +908,9 @@ eformat(const char *fp, va_list ap)
+ case 'd':
+ eputl(va_arg(ap, long), 10);
+ break;
++ case 'x':
++ eputl(va_arg(ap, long), 16);
++ break;
+ default:
+ eputc(c);
+ break;
+@@ -939,6 +948,7 @@ static void
+ eputl(long l, int r)
+ {
+ long q;
++ int c;
+
+ if (l < 0) {
+ eputc('-');
+@@ -946,7 +956,10 @@ eputl(long l, int r)
+ }
+ if ((q = l / r) != 0)
+ eputl(q, r);
+- eputc((int)(l % r) + '0');
++ c = (int)(l % r) + '0';
++ if (c > '9')
++ c += 'a' - '9' - 1;
++ eputc(c);
+ }
+
+ /*
+diff --git a/funmap.c b/funmap.c
+index bd555d6..7d88b75 100644
+--- a/funmap.c
++++ b/funmap.c
+@@ -114,6 +114,7 @@ static struct funmap functnames[] = {
+ {bufferinsert, "insert-buffer",},
+ {fileinsert, "insert-file",},
+ {fillword, "insert-with-wrap",},
++ {insert_char, "insert-char",},
+ {backisearch, "isearch-backward",},
+ {forwisearch, "isearch-forward",},
+ {joinline, "join-line",},
+@@ -191,6 +192,7 @@ static struct funmap functnames[] = {
+ {shellcommand, "shell-command",},
+ {piperegion, "shell-command-on-region",},
+ {shrinkwind, "shrink-window",},
++ {show_raw_mode, "show-raw-mode",},
+ #ifdef NOTAB
+ {space_to_tabstop, "space-to-tabstop",},
+ #endif /* NOTAB */
+diff --git a/kbd.c b/kbd.c
+index 1d7a1a2..f613b10 100644
+--- a/kbd.c
++++ b/kbd.c
+@@ -9,6 +9,8 @@
+ #include <sys/queue.h>
+ #include <signal.h>
+ #include <stdio.h>
++#include <stdlib.h>
++#include <wchar.h>
+
+ #include "def.h"
+ #include "kbd.h"
+@@ -406,6 +408,43 @@ quote(int f, int n)
+ }
+
+ /*
++ * Prompt for a codepoint in whatever the native system's encoding is,
++ * insert it into the file
++ */
++int
++insert_char(int f, int n)
++{
++ char *bufp;
++ char inpbuf[32];
++ wchar_t wc;
++ char mb[MB_CUR_MAX + 1];
++ mbstate_t mbs = { 0 };
++ size_t mbslen;
++ size_t i;
++
++ if ((bufp = eread("Insert character (hex): ", inpbuf, sizeof inpbuf,
++ EFNEW)) == NULL) {
++ return (ABORT);
++ } else if (bufp[0] == '\0') {
++ return (FALSE);
++ }
++
++ wc = (wchar_t) strtoll(bufp, NULL, 16);
++ mbslen = wcrtomb(mb, wc, &mbs);
++ if (mbslen == (size_t) -1) {
++ return (FALSE);
++ }
++
++ for (i = 0; i < mbslen; ++i) {
++ if (linsert(1, mb[i]) == FALSE) {
++ return (FALSE);
++ }
++ }
++
++ return (TRUE);
++}
++
++/*
+ * Wraper function to count invocation repeats.
+ * We ignore any function whose sole purpose is to get us
+ * to the intended function.
+diff --git a/keymap.c b/keymap.c
+index ef71f84..ab23182 100644
+--- a/keymap.c
++++ b/keymap.c
+@@ -120,6 +120,21 @@ static struct KEYMAPE (2) cX4map = {
+ }
+ };
+
++static PF cX8J[] = {
++ insert_char /* ^M */
++};
++
++static struct KEYMAPE (1) cX8map = {
++ 1,
++ 1,
++ rescan,
++ {
++ {
++ CCHR('M'), CCHR('M'), cX8J, NULL
++ }
++ }
++};
++
+ static PF cXcB[] = {
+ listbuffers, /* ^B */
+ quit, /* ^C */
+@@ -158,6 +173,10 @@ static PF cX0[] = {
+ NULL /* 4 */
+ };
+
++static PF cX8[] = {
++ NULL /* 4 */
++};
++
+ static PF cXeq[] = {
+ showcpos /* = */
+ };
+@@ -189,9 +208,9 @@ static PF cXcar[] = {
+ undo /* u */
+ };
+
+-struct KEYMAPE (6) cXmap = {
+- 6,
+- 6,
++struct KEYMAPE (7) cXmap = {
++ 7,
++ 7,
+ rescan,
+ {
+ {
+@@ -207,6 +226,9 @@ struct KEYMAPE (6) cXmap = {
+ '0', '4', cX0, (KEYMAP *) & cX4map
+ },
+ {
++ '8', '8', cX8, (KEYMAP *) & cX8map
++ },
++ {
+ '=', '=', cXeq, NULL
+ },
+ {
+@@ -491,6 +513,18 @@ static struct KEYMAPE (1) overwmap = {
+ }
+ };
+
++static struct KEYMAPE (1) rawmap = {
++ 0,
++ 1, /* 1 to avoid 0 sized array */
++ rescan,
++ {
++ /* unused dummy entry for VMS C */
++ {
++ (KCHAR)0, (KCHAR)0, NULL, NULL
++ }
++ }
++};
++
+
+ /*
+ * The basic (root) keyboard map
+@@ -513,6 +547,7 @@ static struct maps_s map_table[] = {
+ {(KEYMAP *) &notabmap, "notab",},
+ #endif /* NOTAB */
+ {(KEYMAP *) &overwmap, "overwrite",},
++ {(KEYMAP *) &rawmap, "raw",},
+ {(KEYMAP *) &metamap, "esc prefix",},
+ {(KEYMAP *) &cXmap, "c-x prefix",},
+ {(KEYMAP *) &cX4map, "c-x 4 prefix",},
+diff --git a/modes.c b/modes.c
+index 027a5cd..2013bcb 100644
+--- a/modes.c
++++ b/modes.c
+@@ -111,6 +111,23 @@ overwrite_mode(int f, int n)
+ }
+
+ int
++show_raw_mode(int f, int n)
++{
++ if (changemode(f, n, "raw") == FALSE)
++ return (FALSE);
++ if (f & FFARG) {
++ if (n <= 0)
++ curbp->b_flag &= ~BFSHOWRAW;
++ else
++ curbp->b_flag |= BFSHOWRAW;
++ } else
++ curbp->b_flag ^= BFSHOWRAW;
++
++ sgarbf = TRUE;
++ return (TRUE);
++}
++
++int
+ set_default_mode(int f, int n)
+ {
+ int i;
+@@ -170,5 +187,11 @@ set_default_mode(int f, int n)
+ defb_flag |= BFNOTAB;
+ }
+ #endif /* NOTAB */
++ if (strcmp(modebuf, "raw") == 0) {
++ if (n <= 0)
++ defb_flag &= ~BFSHOWRAW;
++ else
++ defb_flag |= BFSHOWRAW;
++ }
+ return (TRUE);
+ }
+diff --git a/util.c b/util.c
+index 9357d4d..dbfc058 100644
+--- a/util.c
++++ b/util.c
+@@ -13,6 +13,8 @@
+ #include <ctype.h>
+ #include <signal.h>
+ #include <stdio.h>
++#include <stdlib.h>
++#include <wchar.h>
+
+ #include "def.h"
+
+@@ -33,6 +35,9 @@ showcpos(int f, int n)
+ int nline, row;
+ int cline, cbyte; /* Current line/char/byte */
+ int ratio;
++ char ismb = 0;
++ wchar_t wc = 0;
++ char mbc[MB_CUR_MAX + 1];
+
+ /* collect the data */
+ clp = bfirstlp(curbp);
+@@ -69,8 +74,43 @@ showcpos(int f, int n)
+ clp = lforw(clp);
+ }
+ ratio = nchar ? (100L * cchar) / nchar : 100;
+- ewprintf("Char: %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d",
+- cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp));
++
++ if (!(curbp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ size_t consumed = 0;
++ size_t offset = 0;
++ while (cbyte != '\n' && offset <= curwp->w_doto) {
++ int c = lgetc(clp, curwp->w_doto - offset);
++ if (isprint(c) || (ISCTRL(c) != FALSE)) {
++ break;
++ }
++ consumed = mbrtowc(&wc,
++ &clp->l_text[curwp->w_doto - offset],
++ llength(clp) - curwp->w_doto + offset,
++ &mbs);
++ if (consumed < (size_t) -2) {
++ ismb = (offset < consumed);
++ snprintf(mbc, consumed + 1, "%s",
++ &clp->l_text[curwp->w_doto - offset]);
++ mbc[consumed + 1] = '\0';
++ break;
++ } else {
++ memset(&mbs, 0, sizeof mbs);
++ }
++ offset++;
++ }
++ }
++
++ if (ismb) {
++ ewprintf("Char: %s (codepoint 0x%lx) Byte: %c (0%o) "
++ "point=%ld(%d%%) line=%d row=%d col=%d", mbc,
++ (long) wc, cbyte, cbyte, cchar, ratio, cline, row,
++ getcolpos(curwp));
++ } else {
++ ewprintf("Char: %c (0%o) point=%ld(%d%%) line=%d row=%d"
++ "col=%d", cbyte, cbyte, cchar, ratio, cline, row,
++ getcolpos(curwp));
++ }
+ return (TRUE);
+ }
+
+@@ -96,6 +136,22 @@ getcolpos(struct mgwin *wp)
+ col += 2;
+ else if (isprint(c)) {
+ col++;
++ } else if (!(wp->w_bufp->b_flag & BFSHOWRAW)) {
++ mbstate_t mbs = { 0 };
++ wchar_t wc = 0;
++ size_t consumed = mbrtowc(&wc, &wp->w_dotp->l_text[i],
++ llength(wp->w_dotp) - i,
++ &mbs);
++ int width = -1;
++ if (consumed < (size_t) -2) {
++ width = wcwidth(wc);
++ }
++ if (width >= 0) {
++ col += width;
++ i += (consumed - 1);
++ } else {
++ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
++ }
+ } else {
+ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
+ }