aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-x.fmsbw/10-freebsd14-msys-sciteco54
-rwxr-xr-x.fmsbw/20-freebsd14-osx-sciteco7
-rwxr-xr-x.fmsbw/50-ubuntu22-appimage9
-rw-r--r--.fmsbw/images/Makefile67
-rw-r--r--.fmsbw/images/msys-activate6
-rw-r--r--ChangeLog73
-rw-r--r--INSTALL20
-rw-r--r--NEWS7
-rw-r--r--README14
-rw-r--r--TODO165
-rw-r--r--configure.ac57
m---------contrib/scinterm0
m---------contrib/scintilla0
-rw-r--r--debian/changelog12
-rw-r--r--doc/cheat-sheet.mm7
-rw-r--r--doc/sciteco.1.in11
-rw-r--r--doc/tutorial.ms.in4
-rw-r--r--fallback.teco_inibin2513 -> 2640 bytes
-rw-r--r--freebsd/Makefile2
-rw-r--r--freebsd/distinfo6
-rw-r--r--freebsd/pkg-plist1
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/colors/solarized.tes53
-rw-r--r--lib/colors/terminal.tes1
-rw-r--r--lib/fnkeys.tesbin4391 -> 5102 bytes
-rw-r--r--lib/opener.tes15
-rw-r--r--lib/tank.tes211
-rw-r--r--m4/ax_with_ncurses.m44
-rw-r--r--src/cmdline.c32
-rw-r--r--src/core-commands.c128
-rw-r--r--src/interface-curses/curses-info-popup.c32
-rw-r--r--src/interface-curses/curses-info-popup.h2
-rw-r--r--src/interface-curses/curses-utils.c16
-rw-r--r--src/interface-curses/curses-utils.h9
-rw-r--r--src/interface-curses/interface.c541
-rw-r--r--src/interface-gtk/gtk-info-popup.c4
-rw-r--r--src/interface-gtk/gtk-label.c22
-rw-r--r--src/interface-gtk/gtk-label.h2
-rw-r--r--src/interface-gtk/interface.c128
-rw-r--r--src/interface.h9
-rw-r--r--src/main.c31
-rw-r--r--src/qreg.c6
-rw-r--r--src/ring.c19
-rw-r--r--src/sciteco.h6
-rw-r--r--src/spawn.c2
-rw-r--r--src/stdio-commands.c37
-rw-r--r--src/string-utils.c10
-rw-r--r--src/view.c12
-rw-r--r--tests/atlocal.in2
-rw-r--r--tests/testsuite.at25
-rwxr-xr-xwww/build.tes25
-rw-r--r--www/screenshots.md4
52 files changed, 1245 insertions, 667 deletions
diff --git a/.fmsbw/10-freebsd14-msys-sciteco b/.fmsbw/10-freebsd14-msys-sciteco
index 037ac66..f8e1f40 100755
--- a/.fmsbw/10-freebsd14-msys-sciteco
+++ b/.fmsbw/10-freebsd14-msys-sciteco
@@ -1,39 +1,5 @@
#!/usr/local/bin/bash
set -ex
-export ASSUME_ALWAYS_YES=yes
-
-# Already in freebsd14-sciteco
-# TODO: Build this with buildah.
-# Start with --network=host
-#pkg update
-#pkg install FreeBSD-clang FreeBSD-clibs-dev \
-# gmake pkgconf autoconf automake libtool \
-# glib gtk3 groff doxygen lowdown valgrind
-#
-#pkg install llvm21 gnugrep gmake coreutils gsed gawk git wget gnupg bash groff zip autoconf automake libtool python3
-#pkg remove FreeBSD-clang
-#git clone https://github.com/HolyBlackCat/quasi-msys2.git /opt/quasi-msys2
-#cd /opt/quasi-msys2
-#ln -s /usr/local/bin/gpgv2 /usr/local/bin/gpgv
-#ln -s /usr/local/bin/bash /bin/bash
-#mkdir -p gnu-overrides
-#ln -s /usr/local/bin/ggrep gnu-overrides/grep
-#ln -s /usr/local/bin/gmake gnu-overrides/make
-#ln -s /usr/local/bin/gsed gnu-overrides/sed
-#ln -s /usr/local/bin/greadlink gnu-overrides/readlink
-#ln -s /usr/local/bin/wine64 gnu-overrides/wine
-#echo MINGW64 >msystem.txt
-#cat >activate << EOF
-#cd /opt/quasi-msys2
-#export PATH=`pwd`/gnu-overrides:$PATH
-#export PKG_CONFIG=pkg-config
-#set +ex
-#. env/all.src
-#set -ex
-#EOF
-#gmake install _autotools _gcc _libc++ _glib2 _pdcurses _gtk3 _librsvg
-#ln -nfs "/opt/quasi-msys2/root/mingw64" /mingw64
-#pkg clean -a
autoreconf -i
mkdir build-freebsd
@@ -75,7 +41,11 @@ cp ico/sciteco-48.png /opt/htdocs/graphics
. /opt/quasi-msys2/activate
cd /opt/build
-export CURSES_CFLAGS=-I/mingw64/include/pdcurses/
+# NOTE: Sometimes the PDCursesMod MSYS upstream package is too old,
+# so we also installed v4.5.4 into the container.
+#export CURSES_CFLAGS=-I/mingw64/include/pdcurses/
+export CURSES_CFLAGS=-I/opt/PDCursesMod
+
# FIXME: glib on MinGW supports static linking but the gspawn
# helper binaries are still linked dynamically, forcing us to ship
# all DLLs anyway. Therefore it makes little sense to link SciTECO
@@ -90,25 +60,31 @@ export LDFLAGS="-flto=thin -stdlib=libc++"
# We cannot run Windows binaries automatically through Wine,
# so we must still force cross-compilation with --host.
+# There is a --with-launcher=wine64, but SciTECO is currently
+# simply broken under Wine.
#autoreconf -i
mkdir build-wingui build-wincon
cd build-wingui
+# See above, we use a manually built PDCursesMod v4.5.4
+#export CURSES_LIBS="-lpdcurses_wingui -lgdi32 -lcomdlg32 -lwinmm"
+export CURSES_LIBS="/opt/PDCursesMod/wingui/pdcurses.a -lgdi32 -lcomdlg32 -lwinmm"
../configure --host=x86_64-w64-mingw32 \
--with-interface=pdcurses-gui --enable-html-docs --program-prefix=g \
--with-scitecodatadir=. \
- --disable-bootstrap \
- CURSES_LIBS="-lpdcurses_wingui -lgdi32 -lcomdlg32 -lwinmm"
+ --disable-bootstrap
make
make install-strip
#make check TESTSUITEFLAGS="--verbose --color=never"
cd ../build-wincon
+# See above, we use a manually built PDCursesMod v4.5.4
+#export CURSES_LIBS="-lpdcurses_wincon -lgdi32 -lwinmm"
+export CURSES_LIBS="/opt/PDCursesMod/wincon/pdcurses.a -lgdi32 -lwinmm"
../configure --host=x86_64-w64-mingw32 \
--with-interface=pdcurses --enable-html-docs \
--with-scitecodatadir=. \
- --disable-bootstrap \
- CURSES_LIBS="-lpdcurses_wincon -lgdi32 -lwinmm"
+ --disable-bootstrap
make
make install-strip
#make check TESTSUITEFLAGS="--verbose --color=never"
diff --git a/.fmsbw/20-freebsd14-osx-sciteco b/.fmsbw/20-freebsd14-osx-sciteco
index bef1cfb..5cbd49f 100755
--- a/.fmsbw/20-freebsd14-osx-sciteco
+++ b/.fmsbw/20-freebsd14-osx-sciteco
@@ -25,12 +25,14 @@ cd ..
#UNATTENDED=1 ./build.sh
#unset CPPFLAGS
#export MACOSX_DEPLOYMENT_TARGET=10.13
-# FIXME: This is not unattended. Perhaps echo https://nue.de.packages.macports.org/macports/packages >target/macports/MIRROR
+# FIXME: This is not unattended.
+# Perhaps echo https://nue.de.packages.macports.org/macports/packages >target/macports/MIRROR
# dylibbundler is available but can't be run naturally.
+# FIXME: How to install variants, like the Quartz variant for Gtk3?
#osxcross-macports install --static glib2-devel gtk3-devel
#
#pkg install cmake
-#git clone https://github.com/auriamg/macdylibbundler.git /opt/macdylibbundler
+#git clone --depth=1 https://github.com/auriamg/macdylibbundler.git /opt/macdylibbundler
#cd /opt/macdylibbundler
#cmake .
#make
@@ -68,6 +70,7 @@ cd ..
# FIXME: Also build -arch arm64 and package with x86_64-apple-darwin25-lipo into universal binary.
# x86_64-apple-darwin25-lipo -lipo -create -output sciteco x86_64/usr/local/bin/sciteco arm64/usr/local/bin/sciteco
# TODO: Build Gtk version as well.
+# Currently, we cannot install the Quartz variant.
mkdir -p /opt/htdocs/downloads/nightly/
cp sciteco-curses_nightly_macos_x86_64.tar.gz /opt/htdocs/downloads/nightly/
diff --git a/.fmsbw/50-ubuntu22-appimage b/.fmsbw/50-ubuntu22-appimage
index ade1100..ca987fa 100755
--- a/.fmsbw/50-ubuntu22-appimage
+++ b/.fmsbw/50-ubuntu22-appimage
@@ -9,15 +9,6 @@ set -ex
# we run this CI job.
# We should be fine, though unless committing at 6:00 in the morning.
-#apt-get update -o APT::Cache-Start=100000000
-#apt-get install -o APT::Cache-Start=100000000 -y fuse libfuse2 imagemagick wget file binutils libglib2.0-bin
-#mkdir -p ~/pkg2appimage
-#cd ~/pkg2appimage
-#wget -O pkg2appimage.AppImage https://github.com/AppImageCommunity/pkg2appimage/releases/download/continuous/pkg2appimage-1eceb30-x86_64.AppImage
-#chmod +x pkg2appimage.AppImage
-# FIXME: We could get automatic mounting to work with fusefs in the host and by exposesing /dev/fuse.
-#./pkg2appimage.AppImage --appimage-extract
-
cd AppImage
~/pkg2appimage/squashfs-root/AppRun curses.yml
mv out/*.AppImage /opt/htdocs/downloads/nightly/sciteco-curses_nightly_x86_64.AppImage
diff --git a/.fmsbw/images/Makefile b/.fmsbw/images/Makefile
new file mode 100644
index 0000000..678a05a
--- /dev/null
+++ b/.fmsbw/images/Makefile
@@ -0,0 +1,67 @@
+# Don't build any image by default
+all:
+
+# Base image for building SciTECO on FreeBSD.
+freebsd14-sciteco:
+ buildah from --name $@-working --network=host quay.io/dougrabson/freebsd14.1-small
+ buildah config --env ASSUME_ALWAYS_YES=yes $@-working
+ buildah run $@-working pkg update
+ buildah run $@-working pkg install FreeBSD-clang FreeBSD-lld FreeBSD-libcompiler_rt-dev FreeBSD-clibs-dev \
+ gmake pkgconf autoconf automake libtool \
+ glib gtk3 groff doxygen lowdown valgrind
+ buildah run $@-working pkg clean -a
+ buildah commit $@-working $@
+
+freebsd14-msys-sciteco:
+ buildah from --name $@-working --network=host freebsd14-sciteco
+ buildah run $@-working pkg install llvm21 gnugrep gmake coreutils gsed gawk \
+ git wget gnupg bash groff zip autoconf automake libtool python3 \
+ wine
+ #buildah run $@-working pkg remove FreeBSD-clang
+ buildah run $@-working pkg clean -a
+ # Cannot check out with --depth=1 as we need a particular commit.
+ buildah run $@-working git clone https://github.com/HolyBlackCat/quasi-msys2.git /opt/quasi-msys2
+ buildah config --workingdir /opt/quasi-msys2 $@-working
+ buildah run $@-working git checkout e41c4d0f7dde15031132348875d1d01c8d0ea857
+ buildah run $@-working ln -s /usr/local/bin/gpgv2 /usr/local/bin/gpgv
+ buildah run $@-working ln -s /usr/local/bin/bash /bin/bash
+ buildah run $@-working mkdir -p gnu-overrides
+ buildah run $@-working ln -s /usr/local/bin/ggrep gnu-overrides/grep
+ buildah run $@-working ln -s /usr/local/bin/gmake gnu-overrides/make
+ buildah run $@-working ln -s /usr/local/bin/gsed gnu-overrides/sed
+ buildah run $@-working ln -s /usr/local/bin/greadlink gnu-overrides/readlink
+ buildah run $@-working ln -s /usr/local/bin/wine64 gnu-overrides/wine
+ buildah run $@-working bash -c 'echo MINGW64 >msystem.txt'
+ buildah copy $@-working msys-activate activate
+ buildah run $@-working gmake install _autotools _gcc _libc++ _glib2 _pdcurses _gtk3 _librsvg
+ buildah run $@-working ln -nfs "/opt/quasi-msys2/root/mingw64" /mingw64
+ # The upstream _pdcurses package is often too outdated, so we also build from sources.
+ # TOOD: Build this with -flto.
+ buildah run $@-working git clone --depth=1 -b v4.5.4 https://github.com/Bill-Gray/PDCursesMod.git /opt/PDCursesMod
+ buildah run $@-working bash -c '. /opt/quasi-msys2/activate && gmake -j2 -C /opt/PDCursesMod/wincon CC=$$CC AR=$$AR WIDE=Y UTF8=Y'
+ buildah run $@-working bash -c '. /opt/quasi-msys2/activate && gmake -j2 -C /opt/PDCursesMod/wingui CC=$$CC AR=$$AR WIDE=Y UTF8=Y'
+ buildah commit $@-working $@
+
+freebsd14-osx-sciteco:
+ false # TODO
+
+APT_GET = apt-get -o APT::Cache-Start=100000000 --yes
+
+ubuntu22-appimage:
+ buildah from --name $@-working --network=host --os=linux ubuntu:22.04
+ buildah run $@-working $(APT_GET) update
+ buildah run $@-working $(APT_GET) install fuse libfuse2 imagemagick wget file binutils libglib2.0-bin
+ buildah run $@-working $(APT_GET) clean
+ buildah run $@-working mkdir -p ~/pkg2appimage
+ buildah config --workingdir '~/pkg2appimage' $@-working
+ buildah run $@-working wget -O pkg2appimage.AppImage \
+ https://github.com/AppImageCommunity/pkg2appimage/releases/download/continuous/pkg2appimage-1eceb30-x86_64.AppImage
+ buildah run $@-working chmod +x pkg2appimage.AppImage
+ # FIXME: We could get automatic mounting to work with fusefs in the host and by exposing /dev/fuse.
+ # FIXME: This does not run without /proc.
+ buildah run $@-working ./pkg2appimage.AppImage --appimage-extract
+ buildah commit $@-working $@
+
+# Remove all temporary containers
+clean:
+ buildah rm --all
diff --git a/.fmsbw/images/msys-activate b/.fmsbw/images/msys-activate
new file mode 100644
index 0000000..655fcae
--- /dev/null
+++ b/.fmsbw/images/msys-activate
@@ -0,0 +1,6 @@
+cd /opt/quasi-msys2
+export PATH=`pwd`/gnu-overrides:$PATH
+export PKG_CONFIG=pkg-config
+set +ex
+. env/all.src
+set -ex
diff --git a/ChangeLog b/ChangeLog
index fa3e6c3..ca9153e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,77 @@ using a prebuilt binary) are included.
Entries marked with "(!)" might break macro portability
compared to the preceding release.
+Version 2.5.2 (2026-04-20)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* sped up `EQq<filename>$`
+* Curses/UNIX: It should be possible to interrupt more potentially blocking
+ operations with CTRL+C (SIGINT).
+* `^A` is no longer executed in parse-only mode - fixes bogus warnings.
+ Has been broken since v2.5.0.
+* (!) `^A` now supports a message severity level, so you can log
+ warnings and errors as well.
+* Added TANK MODE.
+ Launch with EI^EQ[$SCITECOPATH]/tank.tes$
+* support Groff v1.24.0 and later
+* fnkeys.tes: When preserving the horizontal column,
+ take the character representations and tab draw modes into account.
+* SIGTERM/SIGHUP always quits.
+ On GTK recovery files are always dumped first.
+* decreased default recovery dumping interval to 120s
+* (!) `-$$` and `-^C` always return -1 now instead of passing down the prefix sign
+* fixed scrolling at the end of a multi-line command-line
+* fixed auto completion of two-character Q-register names
+* `ER` updates `^S`/`^Y` now as any other text insertion
+* GTK: fixed --detach and support stdout/stderr redirections.
+ Has been broken since v2.5.0.
+* GTK: Fixed interaction between `^T` and main input handling.
+ Key presses are no longer missed and inserted into the command line later.
+* Curses: handle window resizes when blocking in `^T` and don't return function keys.
+ Also, filter out bogus key press events on PDCursesMod/WinGUI.
+* Curses: fixed monochrome support.
+ Has been broken since v2.5.0.
+* Curses: fixed 16-color schemes and default colors (`sciteco --no-profile`)
+ on 8-color terminals (like the Linux/FreeBSD VTs)
+ Has been broken since v2.5.0.
+* Curses: default colors are now closer to terminal.tes
+* Curses: fixed colors on emulators with less than 256 color pairs
+ (e.g. TERM=rxvt)
+* Curses: support up to 32767 color pairs
+* Curses: require wide-char support even on PDCurses
+* fixed auto-completion of Unicode (non-ANSI) file names
+* disallow replacing non-Unicode command lines via `{` and `}`
+* Curses: fixed flickering of the hardware cursor on the command line.
+ On the downside, this can result in the cursor being visible when
+ drawing the buffer as well.
+ Has been broken since v2.5.1.
+* fallback.teco_ini: added commented-out line to disable the hardware cursor
+* fallback.teco_ini: fixed exclusion of the unnamed buffer from custom
+ margin and indention settings.
+ Has been broken since v2.5.0.
+* opener.tes: opener.check-recovery can be used in ~/.teco_ini to check
+ for and warn about the presence of `#recovery#` files.
+
+Version 2.5.1 (2026-01-10)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Haiku is officially supported again from now on
+* minor documentation improvement for `ED`
+* Curses: The command-line uses the hardware cursor by default now.
+ To re-enable the self-drawn cursor, do something like
+ `0,2048ED 2#16@ES/SETCARETSTYLE//$ 2048,0ED`
+* PDCurses/XCurses (X11): fixed crashes on startup and mouse support
+
+Regressions introduced by v2.5.0 and now fixed:
+
+* GTK: Fixed bogus "(Unnamed)" strings in empty message lines
+* PDCurses: Fixed reporting of mouse modifiers (CTRL, ALT, SHIFT)
+* PDCurses: Fixed colors
+ (affects the Windows sciteco.exe and gsciteco.exe).
+* PDCurses: Fixed hanging after pressing mouse keys
+ (affects the Windows sciteco.exe and gsciteco.exe).
+* PDCurses/WinGUI: Fixed hanging input (affects gsciteco.exe).
+
Version 2.5.0 (2026-01-01)
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -115,7 +186,7 @@ Version 2.5.0 (2026-01-01)
* implemented ^E<code> string building constructs for embedding bytes and codepoints
in a strtoul()-like manner
* support <:]q> (pop Q-Register) for getting a success/failure boolean
- * support <==> and <===> for printing octal and hexadecimal numbers
+ * (!) support <==> and <===> for printing octal and hexadecimal numbers
* support :=/:==/:=== commands: print number without trailing linefeed
* Implemented the <^A> command for printing arbitrary strings.
You can use :^A to force raw ANSI output.
diff --git a/INSTALL b/INSTALL
index 5450e6e..3816fde 100644
--- a/INSTALL
+++ b/INSTALL
@@ -3,7 +3,7 @@ Installation Instructions
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
-Copyright (C) 2013-2025 Robin Haberkorn
+Copyright (C) 2013-2026 Robin Haberkorn
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
@@ -21,13 +21,14 @@ SciTECO Build and Runtime Dependencies
* Glib 2 as a cross-platform runtime library (v2.44 or later):
https://developer.gnome.org/glib/
* When choosing the Curses interface, you need one of:
- * NCurses (http://www.gnu.org/software/ncurses/).
- If you plan to use the ncurses MinGW port,
- I recommend ncurses 6.0 or later.
+ * ncurses (http://www.gnu.org/software/ncurses/).
+ Should be built with wide-character support (--enable-widec).
+ If you plan to use the ncurses MinGW port, I recommend ncurses 6.0 or later.
* NetBSD Curses (https://github.com/sabotage-linux/netbsd-curses).
- This is the default on NetBSD.
- * PDCursesMod v4.5.1 or later (https://github.com/Bill-Gray/PDCursesMod.git).
+ This is the default on NetBSD and should always support wide characters.
+ * PDCursesMod v4.5.4 or later (https://github.com/Bill-Gray/PDCursesMod.git).
This is the recommended flavor of PDCurses to use.
+ It must be built with WIDE=Y and UTF8=Y.
* PDCurses/EMCurses (https://github.com/rhaberkorn/emcurses).
* PDCurses/XCurses (http://pdcurses.sourceforge.net/).
Note that XCurses v3.4 appears to be broken, you may have to
@@ -47,7 +48,7 @@ installed by the user manually:
* Scintilla (v5.3.0 or later):
http://www.scintilla.org/
* When choosing the Curses interface:
- * Scinterm (v5.2 or later):
+ * Scinterm (v5.5 or later):
http://foicica.com/scinterm/
* Lexilla (v5.0.0 or later, optional):
https://www.scintilla.org/Lexilla.html
@@ -75,6 +76,11 @@ On NetBSD:
$ sudo pkgin install git gmake pkg-config autoconf automake libtool-base \
glib2 gtk3+ doxygen
+On Haiku:
+
+ $ pkgman install git make pkgconfig autoconf automake libtool \
+ glib2_devel ncurses6_devel gtk3_devel groff doxygen
+
Building from Source Tar Ball or Repository
===========================================
diff --git a/NEWS b/NEWS
index 4382d33..f5baba8 100644
--- a/NEWS
+++ b/NEWS
@@ -2,8 +2,5 @@ News
====
<span class="nf nf-md-new_box"></span>
-SciTECO [v2.5.0](https://sciteco.fmsbw.de/downloads/v2.5.0/) has been released.
-This release brings many new features, but most importantly
-makes the language much more usable as a non-interactive scripting language.
-Also, the command-line is syntax highlighted now and recovery files are written
-to help you recover from crashes and other unexpected terminations.
+SciTECO [v2.5.2](https://sciteco.fmsbw.de/downloads/v2.5.2/) has been released.
+This is mostly a fixup to v2.5.1 and v2.5.0.
diff --git a/README b/README
index d110285..4ed071d 100644
--- a/README
+++ b/README
@@ -23,11 +23,11 @@ The Curses frontend is verified to work with [ncurses](https://www.gnu.org/softw
[PDCurses/XCurses](https://github.com/wmcbrine/PDCurses),
[PDCursesMod](https://github.com/Bill-Gray/PDCursesMod) and
[EMCurses](https://github.com/rhaberkorn/emcurses).
-All X/Open-compatible libraries should be supported.
+All X/Open-compatible libraries with enhanced and wide-character support should be supported.
SVr4 curses without enhanced definitions is **not** supported.
Linux, FreeBSD, NetBSD, [Mac OS X](https://sciteco.fmsbw.de/knowledge/Mac%20OS%20Support),
-Windows (MinGW 32/64) ~~and [Haiku](https://www.haiku-os.org/) (gcc4)~~ are tested and supported.
+Windows (MinGW 32/64) and [Haiku](https://www.haiku-os.org/) (gcc4) are tested and supported.
SciTECO compiles with both GCC and Clang.
SciTECO should compile just fine on other UNIX-compatible platforms.
However UNIX-compatibility is not strictly required:
@@ -121,7 +121,9 @@ There are prebuilt binary packages and source bundles for your convenience:
* [Main download archive](https://sciteco.fmsbw.de/downloads)
* [Download Archive at Sourceforge](https://sourceforge.net/projects/sciteco/files/)
* [FreeBSD port](https://www.freshports.org/editors/sciteco/)
- [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/sciteco-curses.svg?header=PACKAGE)](https://repology.org/project/sciteco-curses/versions)
+ ![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/sciteco-curses.svg?header=PACKAGE)
+* [Haiku port](https://depot.haiku-os.org/#!/pkg/sciteco_curses)
+ ![Haiku port](https://repology.org/badge/version-for-repo/haikuports_master/sciteco.svg?header=PACKAGE)
* OBS repositories and binary downloads for RPM-based (Fedora, openSUSE, etc.) and
Debian-based (Debian, Raspbian, Ubuntu) distributions:
[![build result](https://build.opensuse.org/projects/home:rhaberkorn:sciteco:STABLE/packages/sciteco/badge.svg?type=percent)](https://build.opensuse.org/package/show/home:rhaberkorn:sciteco:STABLE/sciteco)
@@ -129,12 +131,12 @@ There are prebuilt binary packages and source bundles for your convenience:
* [Gtk packages](https://software.opensuse.org/download.html?project=home:rhaberkorn:sciteco:STABLE&package=sciteco-gtk)
* [Curses packages](https://software.opensuse.org/download.html?project=home:rhaberkorn:sciteco:STABLE&package=sciteco-curses)
* [Arch User Repository](https://aur.archlinux.org/packages/sciteco-git)
- [![AUR package](https://repology.org/badge/version-for-repo/aur/sciteco.svg?header=PACKAGE)](https://repology.org/project/sciteco/versions)
+ ![AUR package](https://repology.org/badge/version-for-repo/aur/sciteco.svg?header=PACKAGE)
* [Alpine Linux package](https://pkgs.alpinelinux.org/package/edge/community/x86_64/sciteco)
- [![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/sciteco.svg?header=PACKAGE)](https://repology.org/project/sciteco/versions)
+ ![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/sciteco.svg?header=PACKAGE)
* [Chocolatey package](https://community.chocolatey.org/packages/SciTECO)
for Windows users
- [![Chocolatey package](https://repology.org/badge/version-for-repo/chocolatey/sciteco.svg?header=PACKAGE)](https://repology.org/project/sciteco/versions)
+ ![Chocolatey package](https://repology.org/badge/version-for-repo/chocolatey/sciteco.svg?header=PACKAGE)
* Yocto/OpenEmbedded users should try the
[`sciteco` package from this layer](https://git.fmsbw.de/meta-rhaberkorn/).
* Users of OpenWrt may try to install the
diff --git a/TODO b/TODO
index b2f5821..678121d 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,7 @@
Tasks:
* Have a look at TECO-86.
* VEDIT and PMATE for MS-DOS
- * Scintilla: upstream 2 patches
+ * Make sure that fmsbw.de is indexed by Google
Known Bugs:
* OBS GTK builds sometimes fail: "cannot open display"
@@ -9,48 +9,36 @@ Known Bugs:
* In multiline command lines, the asterisk can scroll out of
view. Perhaps it has to be drawn independently of Scinterm.
* FreeBSD's `crontab -e` is not compatible with SciTECO's savepoint mechanism.
- * ncurses: GNOME Terminal and Xterm produces BUTTON3_PRESSED (without BUTTON3_RELEASED)
- events when scrolling horizontally.
- This is fixed upstream in ncurses and there is a workaround for
- older ncurses versions which limits the effects of this bug.
- * ncurses: st and Xterm produce BUTTON2_RELEASED, followed by BUTTON2_PRESSED
- when clicking the middle button.
- We also *sometimes* get it in the correct order.
- This bug has been fixed upstream and there is a workaround for
- older ncurses versions. You may loose the distinction between
- pressed and release events, though.
+ * Gnome Terminal: You can see hardware cursor flickering when deleting
+ lines repeatedly (if it's enabled of course). This is also visible in Aerc.
+ Scroll regions are already used, but only when scrolling.
+ Disabling the hardware cursor around doupdate() causes flickering
+ of the cursor on some terminals.
* Upgrade to Scintilla 5.5.7 requires charconv header which bumps
the minimum GCC version to 8.1 (officially 9).
This breaks OpenSUSE 15.5-15.6 builds.
* Build problems on Fedora 41: See mail from Blake McBride.
- * @^Um{-$$} Mm= should probably return -1.
* {@I/$$23=/} doesn't insert anything after $$.
- This would be necessary for an interactive screen editing script,
- that leaves you in <I> always.
This would require some refactoring.
+ * GTK on Haiku: CTRL and AltGr-combinations do not work.
+ The modifiers are not delivered properly and it appears to affect all Gtk apps
+ on Haiku.
* Gtk: The control characters in tutorial.woman are still styled with
the variable-width font since its rendered in STYLE_CONTROLCHAR (36),
which is reset in woman.tes.
Perhaps it should always be in lexer.font.
- * The current horizontal position (set by 4EJ via SCI_GETCOLUMN) is
- often wrong, i.e. pressing the up-cursor key can get you into the
- wrong column.
- Character representations obviously take always one column.
- This would have to be fixed in Scintilla's SCI_GETCOLUMN.
* Excessively long lines slow down SciTECO too much, especially
when enabling SCI_SETWRAPMODE(SC_WRAP_CHAR).
In some cases, the internal redrawing blocks SciTECO forever.
* @ES/SCI_CLEARALLREPRESENTATIONS// does nothing.
Might be a Scintilla bug.
- * PDCurses/Wincon does not report button released events.
- We try to work around this with click detection,
- but it still behaves a bit oddly.
- See https://github.com/Bill-Gray/PDCursesMod/issues/330
- Waiting for PDCurses in MSYS to be updated.
- * PDCurses/WinGUI: There is still some flickering, but it got better
- since key macros update the command line only once.
- Could already be fixed upstream, see:
- https://github.com/Bill-Gray/PDCursesMod/issues/322
+ * Some operations can be hard to interrupt because they result
+ in costly calculations within Scintilla.
+ For instance interrupting ECcat /dev/zero$ can hang in
+ the Scintilla Undo operation.
+ This particular hang could be avoided by getting rid of
+ Scintilla undo actions in teco_view_load_from_channel() and spawn.c.
+ We can probably only get rid of undo actions completely and globally.
* Win32: Interrupting <EC> will sometimes hang.
Affects both PDCurses/WinGUI and Gtk.
This no longer happens with ECbash -c 'while true; do true; done'$.
@@ -59,28 +47,23 @@ Known Bugs:
However the UNIX path translation appears to be a Cygwin feature.
If SciTECO would do that, it might break other things (e.g. you might
want to refer directory C:\mingw64 instead).
- * Win32/Wincon: cat ... | sciteco -i
- "Redirection is not supported."
- PR: https://github.com/Bill-Gray/PDCursesMod/pull/344
- Waiting for an MSYS package releae.
- * PDCurses/Win32: Both Wincon and WinGUI crash when you press *any*
- non-ANSI character. This is fixed since PDCurses v4.5.2:
- https://github.com/Bill-Gray/PDCursesMod/issues/335
- We're waiting for an MSYS package upgrade.
- It could also be worked around by using wget_wch() instead of wgetch().
* PDCurses/Win32: Crashes sometimes without any error message.
* NetBSD Curses: scrolling apparently uses hardware idl capabilities
- resulting in graphical glitches on slow terminals.
+ without scroll regions resulting in graphical glitches on slow terminals.
idlok(FALSE) is apparently ignored.
- * NetBSD: Very slow, even the redrawing.
+ * NetBSD Curses: Very slow, even the redrawing.
This does not happen with ncurses on NetBSD.
+ Do they have immedok(TRUE) enabled by default?
* dlmalloc's malloc_trim() does not seem to free any resident memory
after hitting the OOM limit, eg. after <%a>.
Apparently an effect of HAVE_MORECORE (sbrk()) - some allocation is
always left at the end.
* S<LF>^ES^N<$ does not find the first line that does not begin with "<".
This is because \s+ backtracks and can match shorter sequences.
- Perhaps ^ES should always be translated to \s++ (possessive quantifier)?
+ TECO-11 patterns do not backtrack apparently.
+ Perhaps ^ES should always be translated to \s++ (possessive quantifier).
+ On the other hand, we would have to extend the pattern syntax to allow
+ backtracking.
* Colors are still wrong in Linux console even if TERM=linux-16color
when using Solarized. Affects e.g. the message line which uses the
reverse of STYLE_DEFAULT.
@@ -105,6 +88,7 @@ Known Bugs:
re2 should be an optional dependency, so we can still build against the
glib APIs.
Optionally, I could build a PCRE-compatible wrapper for Rust's regex crate.
+ It would also be possible to port hxrex to UTF-8 and add it as a submodule.
* It is still possible to crash SciTECO using recursive functions,
since they map to the C program's call stack.
It is perhaps best to use another stack of
@@ -215,6 +199,27 @@ Known Bugs:
Also, the command-line redrawing is still broken in GNOME Terminal.
* When config.status is run as a consequence of touching configure.ac,
config.h will be wrong, forcing us to rerun ./configure.
+ * Does not scroll at the end of a multiline command-line
+ (the first character on a scrolled line will not be visible).
+ On Curses the hardware cursor will still stay at the end of the window of course.
+ Obviously a Scintilla bug.
+ Break on EditView::DisplayFromPosition
+ Perhaps the problem is in Editor::MaxScrollPos().
+ As a workaround, we set SCI_SETENDATLASTLINE(0).
+ * OBS builds do not provide Ubuntu i586 since subversion is obviously no longer
+ packaged, breaking obs-service-tar-scm.
+ Is there any third-party build?
+ * GTK: too much flickering in tank mode when shooting.
+ This is because my font doesn't contain the correct glyph.
+ Try: I ^E<10041>$
+ Perhaps we should use more widespread glyphs.
+ https://groups.google.com/g/scintilla-interest/c/ZgLik8tNyjM
+ * There are still potentially blocking syscalls that could result in
+ SciTECO hanging. Curses/UNIX can rely on SIGINT, but this won't
+ help on any other platform.
+ All blocking operations must be within an event loop and call into
+ teco_interface_is_interrupted() to potentially drive the UI and
+ detect CTRL+C presses.
Features:
* Should we support *.sgml files with the HTML lexer?
@@ -235,6 +240,7 @@ Features:
Would probably require some kind of server...
* opener.tes should try to center the opened line
(SCI_SETFIRSTVISIBLELINE).
+ However, this would require a new ED hook, so we can query SCI_LINESONSCREEN.
* Rubout of SCI_GOTOPOS could also restore the vertical
scrolling position (SCI_SETFIRSTVISIBLELINE).
So e.g. rubbing out ZJ restores the exact view.
@@ -312,21 +318,11 @@ Features:
line. May be useful e.g. for solarized's F5 key (i.e. function
key macros that need to terminate the command line as they
cannot be rubbed out properly).
- * Key macros could support special escape sequences
- that allow us to modify the parser state reliably.
- E.g. one construct could expand to the current string argument's
- termination character (which may not be Escape).
- In combination with a special key macro state
- effective only in the start state of the string building
- state machine, perhaps only in insertion commands, this
- could be used to make the cursor movement keys work in
- insertion commands by automatically terminating the command.
- Even more simple, the function key flag could be effective
- only when the termination character is $.
- Instead of reserving a character, we could also reserverve
- a bit in the function key maks.
- It's however impossible to reliably return to the start state
- from arbitrary parser states.
+ * Key macro flag to automatically terminate insertion commands
+ (or everywhere where it's safe).
+ Should be used to make the cursor movement keys (fnkeys.tes)
+ automatically terminate string arguments.
+ Will probably require a new state callback.
* Support more function keys.
We can define more function keys via define_key(3NCURSES).
Unfortunately they are not really standardized - st and urxvt for instance
@@ -403,7 +399,6 @@ Features:
* ^A currently doesn't flush the output.
Perhaps flushing should be controlled by the language.
Perhaps via the ET flags.
- * Mini game where you can drive a tank around your source code.
* `@]q` to pop keeping the numeric part of q intact.
Perhaps `@@]q` to overwrite __only__ the numeric part,
but keeping the string intact.
@@ -494,7 +489,12 @@ Features:
command-line termination.
Also, we'd need a command to fetch the modification timestamp
as well.
- * Recovery files should probably be hidden during auto completions.
+ * Curses: Dump recovery files on SIGTERM and SIGHUP as well.
+ Useful when we are unexpectedly but gracefully terminated,
+ Could only be done on Curses by setting a small timeout and
+ returning from getch() frequently to check whether the signal
+ ocurred.
+ On Wincon we could also react to window closes.
* Error handling in SciTECO macros: Allow throwing errors with
e.g. [n]^F<description>^F where n is an error code, defaulting
to 0 and description is the error string - there could be code-specific
@@ -607,6 +607,7 @@ Features:
* It should be possible to disable auto-completions of one-character
register names, so that we can map the idention macro to M<TAB>.
* Add a configure-switch for LTO (--enable-lto).
+ All of the packages will have to be adapted as well.
* There should be a string building construct for escaping
search patterns.
Since string building is performed immediately before
@@ -614,9 +615,12 @@ Features:
search for a Q-Register verbatim.
* Tweak the Makefile lexer for Automake support.
In the simplest case, just add the *.am file extension.
- * Add an fnkeys.tes alternative where moving cursor keys
+ * Add key macros where moving cursor keys
leaves you in the insert (I) command.
That will behave very similar to classical editors.
+ See n00b.tes.
+ This still requires an ED hook for new command lines and
+ FQ to remove Q-registers.
* Lexing via SciTECO macros?
They would have to be in their own parser instance since Scintilla
could ask us to restyle at any time and within string arguments,
@@ -713,6 +717,9 @@ Features:
^U[^L0]SciTECO ^XI <^XT> ^XF$
As a first step, customizing the window title might be the most useful,
as often, you don't need the "SciTECO - <Buffer>" prefix.
+ * Would also allow the configuration of "powerline" symbols at least
+ for the Curses variant.
+ https://github.com/ryanoasis/powerline-extra-symbols
* Until we have customizeable layouts, it may make sense to hardcode
the current line+column in to the message line.
* With Unicode icon support, we might want to replace a few more
@@ -762,14 +769,6 @@ Features:
To format a hex byte, you would write 16^R 2,Qa\ ^D.
The same extension might not be desirable for =/==/===, unless
they are colon-modified?.
- * Curses: allow freely using RGB colors instead of only the constants
- corresponding with the 16 default curses colors.
- Scinterm v5.5 supports that. This means we also need a hash map for
- mapping RGB color values to curses color numbers, analoguous to what
- Scinterm does.
- The problem of Scinterm reusing the same color namespace both for
- arbitrary RGB values and for predefined special constants however
- hasn't been solved yet.
* Scinterm: INDIC_PLAIN and INDIC_STRIKE could theoretically be supported.
PDCurses has A_STRIKETHROUGH. This would require Scintilla API
changes, though, as it currently calls SurfaceImpl::FillRectangle(),
@@ -801,6 +800,40 @@ Features:
* There could be a key macro script for my Russian-phonetic
keyboard layout. This would probably require some new
key macro states as well.
+ * Allow the hardware cursor (CARETSTYLE_CURSES) on the main view as well.
+ We'd have to handle an overlapping info popup, though.
+ Should we deactivate the hardware cursor via curs_set(0) if it overlaps
+ the popup?
+ * ncurses: Support default colors (use_default_colors()).
+ Instead of drawing a black background and white foreground
+ we could use pair number -1.
+ This means you don't suddenly become white-on-black on terminals
+ that use black-on-white by default.
+ On the other hand, not all color schemes might look good,
+ so this would have to be a setting (ED flag).
+ Also, Scinterm would have to support it as well.
+ This also appears to be responsible for the greyish background
+ on Haiku and GNOME Terminal: They don't have a true black as color 0
+ even if the default terminal background is a true black.
+ * Ctags support
+ Perhaps can be Video TECO compatible.
+ https://github.com/universal-ctags/ctags
+ * Non-spacing code points would currently be handled as separate
+ SciTECO characters.
+ On the one hand, this means you can remove accents from characters.
+ On the other hand, it may be inconvinient in texts with many accents.
+ What do Asian scripts like Hangul do?
+ * :^Q should perhaps return a status code, so you can check for
+ invalid line ranges.
+ The current :^Q (dot to line) would have to be renamed to
+ something else, like ^L.
+ Or alternatively @^Q could simply ignore out of range
+ arguments.
+ * Perhaps add math.tes with primitives from my Project Euler
+ solutions to the standard library.
+ * :^W should perhaps inhibit the caret scrolling.
+ When using ^W purely as a wait command, this could be undesirable.
+ Also, it is undesirable for some animations (e.g. shooting in tank.tes).
Optimizations:
* Use SC_DOCUMENTOPTION_STYLES_NONE in batch mode.
diff --git a/configure.ac b/configure.ac
index 5ac7aad..f64636b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
-AC_INIT([SciTECO], [2.5.0],
+AC_INIT([SciTECO], [2.5.2],
[hackers@fmsbw.de],
[sciteco],
[https://sciteco.fmsbw.de/])
@@ -215,7 +215,7 @@ case $host in
*-*-linux* | *-*-*bsd* | *-*-darwin* | *-*-cygwin* | *-*-haiku*)
# NOTE: Keep this on a single line for compatibility
# with ancient versions of Autoconf.
- AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork setsid], , [
+ AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork execv setsid sigaction], , [
AC_MSG_ERROR([Missing libc function])
])
AC_SEARCH_LIBS(dladdr, [dl], , [
@@ -289,7 +289,8 @@ case $INTERFACE in
case $INTERFACE in
ncurses)
# This gives precendence to the widechar version of ncurses,
- # which is necessary for Unicode support even when not using widechar APIs.
+ # which is necessary for Unicode support and when using more than
+ # 256 color pairs.
# However we also accept libncurses.so if it also contains the
# enhanced definitions.
# NOTE: This also defines CURSES_CFLAGS and CURSES_LIBS arguments,
@@ -303,6 +304,9 @@ case $INTERFACE in
CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
LIBS="$LIBS $CURSES_LIBS"
+ # Scinterm cares about the correct flags itself.
+ AC_DEFINE(NCURSES_WIDECHAR, 1, [Provide wide-character functions])
+
AC_CHECK_FUNCS([tigetstr])
;;
@@ -312,10 +316,6 @@ case $INTERFACE in
# installed, we want this to be an explicit setting.
AC_DEFINE(NETBSD_CURSES, 1, [Building against netbsd-curses])
- CFLAGS="$CFLAGS $CURSES_CFLAGS"
- CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
- LIBS="$LIBS $CURSES_LIBS"
-
if [[ "x$CURSES_LIBS" = "x" ]]; then
# libncurses.pc is only shipped by Void Linux' fork,
# not in NetBSD itself.
@@ -325,15 +325,20 @@ case $INTERFACE in
else
AC_MSG_CHECKING([checking for netbsd-curses (CURSES_LIBS)])
AC_MSG_RESULT([$CURSES_LIBS])
- LIBS="$LIBS $CURSES_LIBS"
fi
+ CFLAGS="$CFLAGS $CURSES_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
+ LIBS="$LIBS $CURSES_LIBS"
+
AC_CHECK_FUNCS([tigetstr])
;;
xcurses)
AC_CHECK_PROG(XCURSES_CONFIG, xcurses-config, xcurses-config)
+ # NOTE: `xcurses-config --cflags` is expected to set
+ # -DPDC_WIDE and -DPDC_FORCE_UTF8 (if these features are enabled).
if [[ "x$CURSES_CFLAGS" = "x" -a "x$XCURSES_CONFIG" != "x" ]]; then
CURSES_CFLAGS=`$XCURSES_CONFIG --cflags`
fi
@@ -351,15 +356,18 @@ case $INTERFACE in
LIBS="$LIBS $CURSES_LIBS"
# It is crucial to define XCURSES before including curses.h.
+ # This is not important for Scinterm.
AC_DEFINE(XCURSES, 1, [Enable PDCurses/XCurses extensions])
+ AC_CHECK_FUNC([has_mouse], [
+ # not important to pass to Scinterm
+ AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API])
+ ])
+
AC_DEFINE(PDCURSES_GUI, 1, [PDCurses with GUI window])
;;
pdcurses*)
- CFLAGS="$CFLAGS $CURSES_CFLAGS"
- CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
-
if [[ "x$CURSES_LIBS" = "x" ]]; then
AC_CHECK_LIB(pdcurses, PDC_get_version, , [
AC_MSG_ERROR([libpdcurses missing!])
@@ -367,21 +375,26 @@ case $INTERFACE in
else
AC_MSG_CHECKING([checking for PDCurses (CURSES_LIBS)])
AC_MSG_RESULT([$CURSES_LIBS])
- LIBS="$LIBS $CURSES_LIBS"
fi
# It is crucial to define PDC_WIDE before including curses.h.
# FIXME: MinGW has a pdcurses.h that already defines all necessary macros,
# but it's not in upstream PDCurses/PDCursesMod.
- AC_CHECK_FUNC([add_wch], [
- AC_DEFINE(PDC_WIDE, 1, [PDCurses built with wide-character support])
- # FIXME: It would be better to check for PDC_FORCE_UTF8.
- # Theoretically, we could check for endwin_u[32|64]_4302,
- # but I'm not sure this will work reliably in the future.
- AC_DEFINE(PDC_FORCE_UTF8, 1, [PDCursesMod forces use of UTF8])
+ # FIXME: It would be better to check for PDC_FORCE_UTF8.
+ # Theoretically, we could check for endwin_u[32|64]_4302,
+ # but I'm not sure this will work reliably in the future.
+ CURSES_CFLAGS="$CURSES_CFLAGS -DPDC_WIDE -DPDC_FORCE_UTF8"
+
+ CFLAGS="$CFLAGS $CURSES_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
+ LIBS="$LIBS $CURSES_LIBS"
+
+ AC_CHECK_FUNC([add_wch], [], [
+ AC_MSG_ERROR([libpdcurses does not include wide-character support!])
])
AC_CHECK_FUNC([has_mouse], [
+ # not important to pass to Scinterm
AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API])
])
@@ -452,7 +465,13 @@ AC_ARG_ENABLE(malloc-replacement,
[Replace the libc malloc() [default=check]]),
[malloc_replacement=$enableval], [malloc_replacement=check])
if [[ $malloc_replacement = check ]]; then
- # We currently do not support dlmalloc on Windows and Mac OS.
+ # malloc() replacement via dlmalloc should work practically everywhere
+ # but does not extend to shared libraries on Windows and Mac OS.
+ # That's why it is disabled by default on Windows and Mac OS where other cheap ways
+ # of introspection are available (see memory.c).
+ # On the remaining platforms you can try to combine --enable-malloc-replacement
+ # with --enable-static-executables to link in as many libraries statically
+ # as possible.
case $host in
*-*-darwin* | *-mingw*) malloc_replacement=no;;
*) malloc_replacement=yes;;
diff --git a/contrib/scinterm b/contrib/scinterm
-Subproject 53eb713af10abf46f250746a4acc336b16b02f3
+Subproject 274656ad8fd7478ff7ed7fc4e04eedb7863b2f9
diff --git a/contrib/scintilla b/contrib/scintilla
-Subproject b64652b857d3a5922c72ccc801ac77aa9cff27c
+Subproject 748f9cbc4f2a9d7a4216feaa37aee8e834123f0
diff --git a/debian/changelog b/debian/changelog
index 0923f61..ab0d425 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+sciteco (2.5.2) unstable; urgency=low
+
+ * new upstream version v2.5.2
+
+ -- Robin Haberkorn <rhaberkorn@fmsbw.de> Sun, 19 Apr 2026 20:21:14 +0000
+
+sciteco (2.5.1) unstable; urgency=low
+
+ * new upstream version v2.5.1
+
+ -- Robin Haberkorn <rhaberkorn@fmsbw.de> Sat, 10 Jan 2026 15:16:39 +0000
+
sciteco (2.5.0-0) unstable; urgency=low
* new upstream version v2.5.0
diff --git a/doc/cheat-sheet.mm b/doc/cheat-sheet.mm
index ba7247c..9a63f4e 100644
--- a/doc/cheat-sheet.mm
+++ b/doc/cheat-sheet.mm
@@ -825,7 +825,12 @@ Binary negate \fIn\fP \(em negate TECO boolean.
. TD
. CI "" n =
. TD
-Show value of \fIn\fP in message line.
+Show value of \fIn\fP in message line (decimal).
+. TRX
+. TD
+. CI "" n ===
+. TD
+Show value of \fIn\fP in message line (hexadecimal).
. TRX
. TD colspan=2
Q-Registers consist of 2 cells: strings and integers.
diff --git a/doc/sciteco.1.in b/doc/sciteco.1.in
index e47ca93..83dba44 100644
--- a/doc/sciteco.1.in
+++ b/doc/sciteco.1.in
@@ -425,13 +425,12 @@ Note that this signal can usually also be generated when pressing
.TP
.SCITECO_TOPIC "SIGTERM"
.B SIGTERM
+.TQ
+.SCITECO_TOPIC "SIGHUP"
+.B SIGHUP
Try to gracefully shut down \*(ST.
-In batch mode this only interrupts the currently running macro
-similar to \fBSIGINT\fP causing \*(ST to exit.
-If technically possible, user interfaces will additionally
-process \fBSIGTERM\fP in interactive mode as if the \fICLOSE\fP
-function key has been pressed, which will result in unconditional
-program termination or user-programmed behaviour.
+If technically possible, this will immediately dump recovery
+files unless recovery file dumping is disabled (\(lq0,6EJ\(rq).
.
.
.SH FILES
diff --git a/doc/tutorial.ms.in b/doc/tutorial.ms.in
index 92da0af..4016605 100644
--- a/doc/tutorial.ms.in
+++ b/doc/tutorial.ms.in
@@ -36,6 +36,10 @@
. ns
..
.
+.\" Work around problems with early device extension commands.
+.\" See groff(7) and https://savannah.gnu.org/bugs/?67992
+\c
+.
.\" The entire document is monospaced as users are supposed to
.\" navigate through it and perhaps even modify it inplace.
.SCITECO_TT
diff --git a/fallback.teco_ini b/fallback.teco_ini
index c0b3a49..bd92c29 100644
--- a/fallback.teco_ini
+++ b/fallback.teco_ini
Binary files differ
diff --git a/freebsd/Makefile b/freebsd/Makefile
index 1dcd194..8550f5a 100644
--- a/freebsd/Makefile
+++ b/freebsd/Makefile
@@ -1,5 +1,5 @@
PORTNAME= sciteco
-DISTVERSION= 2.5.0
+DISTVERSION= 2.5.2
CATEGORIES= editors textproc devel
MASTER_SITES= https://sciteco.fmsbw.de/downloads/v${DISTVERSION}/ \
SOURCEFORGE/${PORTNAME}/v${DISTVERSION}/
diff --git a/freebsd/distinfo b/freebsd/distinfo
index 49a7b48..9396b55 100644
--- a/freebsd/distinfo
+++ b/freebsd/distinfo
@@ -1,3 +1,3 @@
-TIMESTAMP = 1745085748
-SHA256 (sciteco-2.4.0.tar.gz) = 5b053644d8365eb0fddd9b268af9d6c6c44786dfa51dd4cbb962abac66670402
-SIZE (sciteco-2.4.0.tar.gz) = 4077220
+TIMESTAMP = 1776638070
+SHA256 (sciteco-2.5.2.tar.gz) = 94149fa3371e3adff723ae933502b5983804c4e06a681c6d00b0af19c439a978
+SIZE (sciteco-2.5.2.tar.gz) = 4113973
diff --git a/freebsd/pkg-plist b/freebsd/pkg-plist
index ab74145..dd3e7cb 100644
--- a/freebsd/pkg-plist
+++ b/freebsd/pkg-plist
@@ -13,6 +13,7 @@ share/man/man7/%%PROGRAM_PREFIX%%sciteco.7.gz
%%DATADIR%%/lib/getopt.tes
%%DATADIR%%/lib/lexer.tes
%%DATADIR%%/lib/repl.tes
+%%DATADIR%%/lib/tank.tes
%%DATADIR%%/lib/tecat.tes
%%LEXILLA%%%%DATADIR%%/lib/lexers/abaqus.tes
%%LEXILLA%%%%DATADIR%%/lib/lexers/ada.tes
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 0da616a..04e83ad 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,5 @@
dist_scitecolib_DATA = color.tes lexer.tes session.tes opener.tes \
- fnkeys.tes string.tes getopt.tes
+ fnkeys.tes string.tes getopt.tes tank.tes
# Standalone scripts.
# These are not installed via _SCRIPTS as it would add the --program-prefix.
diff --git a/lib/colors/solarized.tes b/lib/colors/solarized.tes
index 11567d9..91b2718 100644
--- a/lib/colors/solarized.tes
+++ b/lib/colors/solarized.tes
@@ -17,41 +17,24 @@
* `solarized.toggle` will be mapped to the F5 function key.
* F5 will also automatically terminate the command line.
*!
-0EJ-1"=
- 000,043,054:M[color.rgb],08,3EJ Q[color.lblack] U[color.base03]
- 007,054,066:M[color.rgb],00,3EJ Q[color.black] U[color.base02]
- 088,110,117:M[color.rgb],10,3EJ Q[color.lgreen] U[color.base01]
- 101,123,131:M[color.rgb],11,3EJ Q[color.lyellow] U[color.base00]
- 131,148,150:M[color.rgb],12,3EJ Q[color.lblue] U[color.base0]
- 147,161,161:M[color.rgb],14,3EJ Q[color.lcyan] U[color.base1]
- 238,232,213:M[color.rgb],07,3EJ Q[color.white] U[color.base2]
- 253,246,227:M[color.rgb],15,3EJ Q[color.lwhite] U[color.base3]
- 181,137,000:M[color.rgb],03,3EJ !* yellow *!
- 203,075,022:M[color.rgb],09,3EJ Q[color.lred] U[color.orange]
- 220,050,047:M[color.rgb],01,3EJ !* red *!
- 211,054,130:M[color.rgb],05,3EJ !* magenta *!
- 108,113,196:M[color.rgb],13,3EJ Q[color.lmagenta]U[color.violet]
- 038,139,210:M[color.rgb],04,3EJ !* blue *!
- 042,161,152:M[color.rgb],06,3EJ !* cyan *!
- 133,153,000:M[color.rgb],02,3EJ !* green *!
-|
- 000,043,054:M[color.rgb]U[color.base03]
- 007,054,066:M[color.rgb]U[color.base02]
- 088,110,117:M[color.rgb]U[color.base01]
- 101,123,131:M[color.rgb]U[color.base00]
- 131,148,150:M[color.rgb]U[color.base0]
- 147,161,161:M[color.rgb]U[color.base1]
- 238,232,213:M[color.rgb]U[color.base2]
- 253,246,227:M[color.rgb]U[color.base3]
- 181,137,000:M[color.rgb]U[color.yellow]
- 203,075,022:M[color.rgb]U[color.orange]
- 220,050,047:M[color.rgb]U[color.red]
- 211,054,130:M[color.rgb]U[color.magenta]
- 108,113,196:M[color.rgb]U[color.violet]
- 038,139,210:M[color.rgb]U[color.blue]
- 042,161,152:M[color.rgb]U[color.cyan]
- 133,153,000:M[color.rgb]U[color.green]
-'
+0,3EJ
+
+000,043,054:M[color.rgb]U[color.base03]
+007,054,066:M[color.rgb]U[color.base02]
+088,110,117:M[color.rgb]U[color.base01]
+101,123,131:M[color.rgb]U[color.base00]
+131,148,150:M[color.rgb]U[color.base0]
+147,161,161:M[color.rgb]U[color.base1]
+238,232,213:M[color.rgb]U[color.base2]
+253,246,227:M[color.rgb]U[color.base3]
+181,137,000:M[color.rgb]U[color.yellow]
+203,075,022:M[color.rgb]U[color.orange]
+220,050,047:M[color.rgb]U[color.red]
+211,054,130:M[color.rgb]U[color.magenta]
+108,113,196:M[color.rgb]U[color.violet]
+038,139,210:M[color.rgb]U[color.blue]
+042,161,152:M[color.rgb]U[color.cyan]
+133,153,000:M[color.rgb]U[color.green]
:Q[solarized.light]"< 0U[solarized.light] '
@[solarized.light]{
diff --git a/lib/colors/terminal.tes b/lib/colors/terminal.tes
index ea048aa..39c5bac 100644
--- a/lib/colors/terminal.tes
+++ b/lib/colors/terminal.tes
@@ -9,6 +9,7 @@ Q[color.white]U[color.selback]
!* Also used for popups *!
[color.calltip] 0,Q[color.lwhite],Q[color.black] 
+!* Due to the bold attribute this should be grey even on 8-color terminals *!
[color.comment] 1,Q[color.black],Q[color.lblack] 
[color.number] 0,Q[color.black],Q[color.cyan] 
[color.keyword] 1,Q[color.black],Q[color.lwhite] 
diff --git a/lib/fnkeys.tes b/lib/fnkeys.tes
index 937caf9..f58a26a 100644
--- a/lib/fnkeys.tes
+++ b/lib/fnkeys.tes
Binary files differ
diff --git a/lib/opener.tes b/lib/opener.tes
index c0183c8..8bcd896 100644
--- a/lib/opener.tes
+++ b/lib/opener.tes
@@ -36,3 +36,18 @@
EBN[\.i] Q.c-1,Q.l-1ESFINDCOLUMN:J
>
]* }
+
+!*$
+ * M[opener.check-recovery] -- Warn if there if a recovery file (#filename#) is detected
+ *
+ * This points to a prior crash or unexpected termination.
+ *!
+@[opener.check-recovery]{
+ :Q*"=  '
+ [*
+ EQ.f G* I# R
+ <-A"I 1; ' :R;>
+ I#
+ 1:EN*Q.f"S 2Detected recovery file "Q.f" '
+ ]*
+}
diff --git a/lib/tank.tes b/lib/tank.tes
new file mode 100644
index 0000000..0894009
--- /dev/null
+++ b/lib/tank.tes
@@ -0,0 +1,211 @@
+!*
+ * TANK MODE
+ *
+ * Drive an ASCII tank around your buffer.
+ * Invoke via EIQ[$SCITECOPATH]/tank.tes
+ *!
+[tank.sprite.0] !* background *!
+
+!* FIXME: Perhaps encode the gun's top in the integer cell *!
+[tank.sprite.1]
+█▓█▓
+╠════○
+▓█▓█
+[tank.sprite.2]
+ █▓█▓
+○════╣
+ ▓█▓█
+[tank.sprite.3]
+ ┌┐
+▓││█
+█││▓
+▓┴┴█
+[tank.sprite.4]
+█┬┬▓
+▓││█
+█││▓
+ └┘
+
+!*
+ * f,tM[tank.rand] - LCG pseudo random number generator for values between f and t
+ * This is based on the version in the BSD libc.
+ *!
+::U[tank.rand]
+@[tank.rand]{
+ U.tU.f
+ (1103515245*Q[tank.rand]+12345) & (2^*31-1)U[tank.rand]
+ (Q[tank.rand]^/(Q.t-Q.f+1)+Q.f)
+}
+
+!* x,yM[tank.goto] -> success|failure *!
+@[tank.goto]{
+ U.yU.x
+ Q.x"< 0 ' Q.y"< 0 '
+ J Q.y:L"F 0 '
+ Q.x<0A"< I  F> ' 0A-10"= I  F> ' C>
+ -1
+}
+
+!* x,y,spriteM[tank.blit] *!
+@[tank.blit]{
+ U.sU.yU.x Q.xU.#x0
+ Q.s"N
+ 10[tank.sprite.0] !* background *!
+ %[tank.blit]
+ '
+ <
+ %.iQ[tank.sprite.\.s]U.c Q.c:;
+ Q[tank.blit]&1"=
+ !* toggle the chain character to simulate movements *!
+ Q.c-█"= ▓U.c | Q.c-▓"= █U.c ' '
+ '
+
+ Q.c-10"=
+ %.y Q.#x0U.x
+ Q.s"N 10:[tank.sprite.0] '
+ |
+ Q.x,Q.yM[tank.goto];
+ Q.s"N !* not background sprite *!
+ Q.c- "=
+ !* space is "transparent" - copy background unmodified *!
+ 0A"< I  | 0A-10"= I  | C ' '
+ -A:[tank.sprite.0]
+ %.x F<
+ '
+ !* "destroy" fields we have touched *!
+  U.d
+ 0A"F 0A-10"N 0A- "N 33,126M[tank.rand]U.d ' ' '
+ Q.d:[tank.sprite.0]
+ '
+ 0A"F 0A-10"N D ' ' Q.cI
+ %.x
+ '
+ >
+}
+
+@[tank.explode]{
+ !!.[explosion]*oO%. 
+ .[explosion]✹✸✶✧· 
+ :Q.[explosion]<
+ 0A"F 0A-10"N D ' '
+ %.i-1Q.[explosion]IR
+ >
+}
+
+0U[tank.score]
+
+@[tank.shoot]{
+ U.#dr U.yU.x
+
+ !* FIXME: Could also be encoded into a map *!
+ Q.#dr-1Oright,left,up,down
+ !right!
+ 6-1%.x %.y
+ 1U.#dx 0U.#dy
+ Oend
+ !left!
+ %.y
+ -U.#dx 0U.#dy
+ Oend
+ !up!
+ 2-1%.x
+ 0U.#dx -U.#dy
+ Oend
+ !down!
+ 2-1%.x 3%.y
+ 0U.#dx 1U.#dy
+ !* fall through *!
+ !end!
+ <
+ (Q.#dx%.x),(Q.#dy%.y)M[tank.goto]"F  '
+ 0A"<  ' Q.#dy"= 0A-10"=  ' '
+ 0A- "N 0A-10"N 1; ' '
+ 0A"F 0A-10"N D ' ' I●
+  -DI 
+ >
+ M[tank.explode]
+
+ %[tank.score] 1SCORE: \[tank.score]
+}
+
+@[tank.selfdestruct]{
+ U.yU.x Q.x+2U.#cx Q.y+2U.#cy
+ .[explosion]✹✹✸✸✶✶✧✧· 
+ 10<
+ Q.#cx-10U.x 20<
+ Q.x-1"< %.x F> '
+ Q.#cy-10U.y 20<
+ !* d = sqrt((x - cx)^2 + ((y - cy) * aspect)^2) *!
+ (Q.x-Q.#cx)^*2 + ((Q.y-Q.#cy)*2)^*2 U.#d2
+
+ Q.r^*2 - Q.#d2"<
+ Q.#d2 - (Q.r+2)^*2"<
+ !* on shock wave *!
+ Q.x,Q.yM[tank.goto]"F %.y F> '
+ -D Q.rQ.[explosion]I
+ '
+ |
+ !* within shock wave *!
+ Q.x,Q.yM[tank.goto]"F %.y F> '
+ -D I 
+ '
+ %.y>
+ %.x>
+ 1
+ %.r>
+ Q.#cx,Q.#cyM[tank.goto]
+}
+
+ESGETCOLUMNU.x :-1U.y
+Q.xU.#xo Q.yU.#yo
+
+!*
+ * Normalize identations to spaces -- assumed by M[goto]
+ * FIXME: Will not work well with character representations.
+ *!
+ESGETTABDRAWMODE-2"N
+ J <S^I;
+ ESGETCOLUMN-(-1ESGETCOLUMN)U.w
+ -D Q.w<I >
+ >
+'
+
+1CONTROL: W A S D. SHOOT: SPACE. QUIT: ESCAPE. PRESS ANY KEY TO START. 
+
+ESGETCARETSTYLEU.[caretstyle]
+0ESSETCARETSTYLE
+
+1U.[sprite]
+<
+ Q.#xo,Q.#yo,0M[tank.blit] !* redraw background *!
+ Q.xU.#xo Q.yU.#yo
+
+ Q.x,Q.y,Q.[sprite]M[tank.blit]
+ 0 U.c
+ :Okey.U.c
+ F>
+ !key.w!
+ Q.y"> -%.y ' 3U.[sprite]
+ F>
+ !key.a!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ Q.x"> -%.x ' 2U.[sprite]
+ F>
+ !key.s!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ %.y 4U.[sprite]
+ F>
+ !key.d!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ %.x 1U.[sprite]
+ F>
+ !key. !
+ !* FIXME: this also scrolls due to  *!
+ Q.x,Q.y,Q.[sprite]M[tank.shoot]
+ F>
+ !key.!
+ Q.x,Q.yM[tank.selfdestruct]
+ 1;
+>
+
+Q.[caretstyle]ESSETCARETSTYLE
diff --git a/m4/ax_with_ncurses.m4 b/m4/ax_with_ncurses.m4
index 4dc2f33..cdcfd04 100644
--- a/m4/ax_with_ncurses.m4
+++ b/m4/ax_with_ncurses.m4
@@ -5,8 +5,8 @@
# DESCRIPTION
#
# This macro checks for an ncurses library with enhanced definitions
-# providing a curses.h either in the default search path or as
-# established by pkg-config.
+# (including wide-char support) providing a curses.h either in the default
+# search path or as established by pkg-config.
#
# It is based on the AX_WITH_CURSES macro but does not attempt
# to find any non-standard header, which would require #ifdefing
diff --git a/src/cmdline.c b/src/cmdline.c
index fa69d91..b944b5e 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -99,6 +99,14 @@ teco_cmdline_init(void)
teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0);
/*
+ * FIXME: This works around a problem where the caret is not
+ * scrolled in multi-line mode after it wraps at the end of the
+ * line. You cannot scroll the command-line with the mouse, so the
+ * user won't see any difference.
+ */
+ teco_cmdline_ssm(SCI_SETENDATLASTLINE, FALSE, 0);
+
+ /*
* FIXME: Something resets the margin text, so we have to set it last.
*/
teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*");
@@ -162,12 +170,32 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
* Result of command line replacement (}):
* Exchange command lines
*/
+ g_clear_error(&tmp_error);
+
teco_qreg_t *cmdline_reg = teco_qreg_table_find(&teco_qreg_table_globals, "\e", 1);
g_auto(teco_string_t) new_cmdline = {NULL, 0};
if (!cmdline_reg->vtable->get_string(cmdline_reg, &new_cmdline.data, &new_cmdline.len,
- NULL, error))
+ NULL, &tmp_error)) {
+ teco_error_add_frame_toplevel();
+ teco_error_display_short(tmp_error);
+ g_propagate_error(error, g_steal_pointer(&tmp_error));
+ return FALSE;
+ }
+
+ /*
+ * SciTECO code must always be UTF-8, but you can smuggle arbitrary bytes
+ * into the "\e" register.
+ * This would be cumbersome to test for in teco_state_start_cmdline_pop().
+ */
+ if (!teco_string_validate_utf8(new_cmdline)) {
+ g_set_error_literal(&tmp_error, TECO_ERROR, TECO_ERROR_CODEPOINT,
+ "Invalid UTF-8 byte sequence in command-line replacement");
+ teco_error_add_frame_toplevel();
+ teco_error_display_short(tmp_error);
+ g_propagate_error(error, g_steal_pointer(&tmp_error));
return FALSE;
+ }
/*
* Search for first differing character in old and
@@ -229,7 +257,7 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
}
}
- /* error is handled in teco_cmdline_keypress_c() */
+ /* error is handled in teco_cmdline_keypress() */
g_propagate_error(error, g_steal_pointer(&tmp_error));
return FALSE;
}
diff --git a/src/core-commands.c b/src/core-commands.c
index f81bdf3..d2c8b9d 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -1739,8 +1739,10 @@ TECO_DEFINE_STATE(teco_state_ascii,
/*$ ^[^[ ^[$ $$ ^C terminate return
* [a1,a2,...]$$ -- Terminate command line or return from macro
+ * -$$
* [a1,a2,...]^[$
* [a1,a2,...]^C
+ * -^C
*
* Returns from the current macro invocation.
* This will pass control to the calling macro immediately
@@ -1754,6 +1756,8 @@ TECO_DEFINE_STATE(teco_state_ascii,
* All braces opened in the current macro invocation will
* be closed and their values discarded.
* Only the direct arguments to \fB$$\fP will be kept.
+ * If the prefix sign is set (\(lq-\fB$$\fP\(rq) the command
+ * will always leave -1 on the stack.
*
* Returning from the top-level macro in batch mode
* will exit the program or start up interactive mode depending
@@ -1806,6 +1810,9 @@ teco_return(teco_machine_main_t *ctx, GError **error)
ctx->parent.current = &teco_state_start;
if (!teco_expressions_eval(FALSE, error))
return NULL;
+ if (teco_num_sign < 0)
+ /* only if you wrote -$$ */
+ teco_expressions_push(1);
teco_error_return_set(error, teco_expressions_args());
return NULL;
}
@@ -1873,6 +1880,18 @@ TECO_DEFINE_STATE_START(teco_state_escape,
.end_of_macro_cb = teco_state_escape_end_of_macro
);
+static gboolean
+teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error)
+{
+ if (G_UNLIKELY(ctx == &teco_cmdline.machine)) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "<^C> is not allowed to terminate command-lines");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/*
* Just like ^[, ^C actually implements a lookahead,
* so a ^C itself does nothing.
@@ -1892,20 +1911,20 @@ teco_state_ctlc_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
static gboolean
-teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error)
+teco_state_ctlc_end_of_macro(teco_machine_main_t *ctx, GError **error)
{
- if (G_UNLIKELY(ctx == &teco_cmdline.machine)) {
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
- "<^C> is not allowed to terminate command-lines");
- return FALSE;
- }
-
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return TRUE;
+ if (teco_num_sign < 0)
+ /* only if you wrote -^C */
+ teco_expressions_push(1);
return TRUE;
}
TECO_DEFINE_STATE_START(teco_state_ctlc,
.initial_cb = (teco_state_initial_cb_t)teco_state_ctlc_initial,
- .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input
+ .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input,
+ .end_of_macro_cb = (teco_state_end_of_macro_cb_t)teco_state_ctlc_end_of_macro
);
static teco_state_t *
@@ -1983,7 +2002,7 @@ TECO_DEFINE_STATE_COMMAND(teco_state_ctlc_control,
* Without any argument ED returns the current flags.
*
* Currently, the following flags are used by \*(ST:
- * .IP 2: 5
+ * .IP 2: 6
* Reflects whether program termination has been requested
* by successfully performing the \fBEX\fP command.
* This flag can also be used to cancel the effect of any
@@ -2117,46 +2136,35 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* first.
* Memory limiting is enabled by default.
* .IP 3:
- * This \fBwrite-only\fP property allows redefining the
- * first 16 entries of the terminal color palette \(em a
- * feature required by some
- * color schemes when using the Curses user interface.
- * When setting this property, you are making a request
- * to define the terminal \fIcolor\fP as the Scintilla-compatible
- * RGB color value given in the \fIrgb\fP parameter.
- * \fIcolor\fP must be a value between 0 and 15
- * corresponding to black, red, green, yellow, blue, magenta,
- * cyan, white, bright black, bright red, etc. in that order.
- * The \fIrgb\fP value has the format 0xBBGGRR, i.e. the red
- * component is the least-significant byte and all other bytes
- * are ignored.
- * Note that on curses, RGB color values sent to Scintilla
- * are actually mapped to these 16 colors by the Scinterm port
- * and may represent colors with no resemblance to the \(lqRGB\(rq
- * value used (depending on the current palette) \(em they should
- * instead be viewed as placeholders for 16 standard terminal
- * color codes.
- * Please refer to the Scinterm manual for details on the allowed
- * \(lqRGB\(rq values and how they map to terminal colors.
- * This command provides a crude way to request exact RGB colors
- * for the first 16 terminal colors.
- * The color definition may be queued or be completely ignored
- * on other user interfaces and no feedback is given
- * if it fails. In fact feedback cannot be given reliably anyway.
- * Note that on 8 color terminals, only the first 8 colors
- * can be redefined (if you are lucky).
- * Note that due to restrictions of most terminal emulators
- * and some curses implementations, this command simply will not
- * restore the original palette entry or request
- * when rubbed out and should generally only be used in
- * \fIbatch-mode\fP \(em typically when loading a color scheme.
- * For the same reasons \(em even though \*(ST tries hard to
- * restore the original palette on exit \(em palette changes may
- * persist after \*(ST terminates on most terminal emulators on Unix.
- * The only emulator which will restore their default palette
- * on exit the author is aware of is \fBxterm\fP(1) and
- * the Linux console driver.
- * You have been warned. Good luck.
+ * This \fBwrite-only\fP property allows disabling use of
+ * the 16 color palette of default colors when using
+ * the Curses user interface.
+ * By default, when working with RGB values in Scintilla
+ * messages (and \*(ST color schemes for that matter)
+ * the 16 \(lqdefault\(rq colors are represented by
+ * special RGB placeholders (e.g. 0x000080 for \fICOLOR_RED\fP)
+ * as documented in the Scinterm manual.
+ * Any other RGB values will be automatically allocated
+ * in the terminal emulator if supported, giving the illusion
+ * of true color support, even though the actual number of
+ * concurrent colors on screen may be limited by the terminal
+ * emulator (see \fImax_colors\fP and \fImax_pairs\fP
+ * .B terminfo (1)
+ * capabilities) and other factors.
+ * \*(ST tries to avoid the first 16 color ids so as not to
+ * interfer with other terminal-based applications.
+ * The default palette however may be overwritten when requesting
+ * new colors in terminals restricted to 16 colors.
+ * In this case the original palette might not be restored properly on
+ * program termination and affect other terminal applications.
+ * The 16 predefined colors do not map to exact RGB values,
+ * though, but point into a palette at the terminal emulator.
+ * \*(ST therefore allows you to disable the 16 RGB placeholders
+ * by calling \(lq0,3EJ\(rq so that all colors are freshly initialized
+ * and will represent their exact RGB values.
+ * It has no effect when using the GTK interface.
+ * You are recommended to add \(lq0,3EJ\(rq to all color
+ * schemes that rely on exact RGB values.
* .IP 4:
* The column after the last horizontal movement.
* This is only used by \fBfnkeys.tes\fP and is similar to the Scintilla-internal
@@ -2169,12 +2177,14 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* .IP 6:
* .SCITECO_TOPIC recovery
* Interval in seconds for the creation of recovery files
- * or 0 if those dumps are disabled (the default is 300 seconds).
+ * or 0 if those dumps are disabled (the default is 120 seconds).
* When enabled all dirty buffers are dumped to files with hash
* signs around the original basename (\fB#\fIfilename\fB#\fR).
* They are removed automatically when no longer required,
* but may be left around when the \*(ST crashes or terminates
* unexpectedly.
+ * During graceful shutdowns (\fBSIGTERM\fP etc.) recovery
+ * files may be dumped immediately even before the interval expires.
* After changing the interval, the new value may become
* active only after the previous interval expires.
* Recovery files are not dumped in batch mode.
@@ -2229,7 +2239,7 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
EJ_USER_INTERFACE = 0,
EJ_BUFFERS,
EJ_MEMORY_LIMIT,
- EJ_INIT_COLOR,
+ EJ_PALETTE,
EJ_CARETX,
EJ_CMDLINE_HEIGHT,
EJ_RECOVERY_INTERVAL
@@ -2253,19 +2263,13 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
return;
break;
- case EJ_INIT_COLOR:
- if (value < 0 || value >= 16) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid color code %" TECO_INT_FORMAT " "
- "specified for <EJ>", value);
- return;
- }
- if (!teco_expressions_args()) {
- teco_error_argexpected_set(error, "EJ");
+ case EJ_PALETTE:
+ if (teco_is_success(value)) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Default color palette can only be disabled");
return;
}
- teco_interface_init_color((guint)value,
- (guint32)teco_expressions_pop_num(0));
+ teco_interface_disable_palette();
break;
case EJ_CARETX:
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
index edb6e15..83d4665 100644
--- a/src/interface-curses/curses-info-popup.c
+++ b/src/interface-curses/curses-info-popup.c
@@ -73,7 +73,7 @@ teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_
}
static void
-teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
+teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair)
{
int pad_lines; /**! pad height */
gint pad_cols; /**! entry columns */
@@ -102,11 +102,11 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
ctx->pad = newpad(pad_lines, COLS - 2);
/*
- * NOTE: attr could contain A_REVERSE on monochrome terminals,
+ * NOTE: attr could contain WA_REVERSE on monochrome terminals,
* so we use foreground attributes instead of background attributes.
- * This way, we can cancel out the A_REVERSE if necessary.
+ * This way, we can cancel out the WA_REVERSE if necessary.
*/
- wattrset(ctx->pad, attr);
+ wattr_set(ctx->pad, attr, pair, NULL);
teco_curses_clrtobot(ctx->pad);
/*
@@ -161,7 +161,7 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
}
void
-teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
+teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair)
{
if (!ctx->length)
/* nothing to display */
@@ -171,7 +171,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
delwin(ctx->window);
if (!ctx->pad)
- teco_curses_info_popup_init_pad(ctx, attr);
+ teco_curses_info_popup_init_pad(ctx, attr, pair);
gint pad_lines = getmaxy(ctx->pad);
/*
@@ -183,15 +183,17 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
/* window covers message, scintilla and info windows */
ctx->window = newwin(popup_lines, 0, LINES - teco_cmdline.height - popup_lines, 0);
- wattrset(ctx->window, attr);
+ wattr_set(ctx->window, attr, pair, NULL);
- wborder(ctx->window,
- ACS_VLINE,
- ACS_VLINE, /* may be overwritten with scrollbar */
- ACS_HLINE,
- ' ', /* no bottom line */
- ACS_ULCORNER, ACS_URCORNER,
- ACS_VLINE, ACS_VLINE);
+ /*
+ * NOTE: wborder() is broken for large pair numbers, at least on ncurses.
+ */
+ waddch(ctx->window, ACS_ULCORNER);
+ whline(ctx->window, ACS_HLINE, COLS - 2);
+ mvwaddch(ctx->window, 0, COLS - 1, ACS_URCORNER);
+ mvwvline(ctx->window, 1, 0, ACS_VLINE, getmaxy(ctx->window)-1);
+ /* may be overwritten with scrollbar */
+ mvwvline(ctx->window, 1, COLS - 1, ACS_VLINE, getmaxy(ctx->window)-1);
copywin(ctx->pad, ctx->window,
ctx->pad_first_line, 0,
@@ -214,7 +216,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
* Instead, simply draw reverse blanks.
*/
wmove(ctx->window, bar_y, COLS-1);
- wattrset(ctx->window, attr ^ A_REVERSE);
+ wattr_set(ctx->window, attr ^ WA_REVERSE, pair, NULL);
wvline(ctx->window, ' ', bar_height);
}
diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h
index fd923e9..898ba70 100644
--- a/src/interface-curses/curses-info-popup.h
+++ b/src/interface-curses/curses-info-popup.h
@@ -49,7 +49,7 @@ teco_curses_info_popup_init(teco_curses_info_popup_t *ctx)
void teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_t type,
const gchar *name, gsize name_len, gboolean highlight);
-void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr);
+void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair);
const teco_string_t *teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x);
void teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx);
void teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta);
diff --git a/src/interface-curses/curses-utils.c b/src/interface-curses/curses-utils.c
index 875c332..3b25d56 100644
--- a/src/interface-curses/curses-utils.c
+++ b/src/interface-curses/curses-utils.c
@@ -53,9 +53,9 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
/*
* The entire background might be in reverse, especially
* on monochrome terminals.
- * In those cases, we have to __remove__ the A_REVERSE flag.
+ * In those cases, we have to __remove__ the WA_REVERSE flag.
*/
- attr_t attrs = A_NORMAL;
+ attr_t attrs = WA_NORMAL;
short pair = 0;
wattr_get(win, &attrs, &pair, NULL);
@@ -81,28 +81,28 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
chars_added++;
if (chars_added > max_width)
goto truncate;
- wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ wattr_set(win, attrs ^ WA_REVERSE, pair, NULL);
waddch(win, '$');
break;
case '\r':
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ wattr_set(win, attrs ^ WA_REVERSE, pair, NULL);
waddstr(win, "CR");
break;
case '\n':
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ wattr_set(win, attrs ^ WA_REVERSE, pair, NULL);
waddstr(win, "LF");
break;
case '\t':
chars_added += 3;
if (chars_added > max_width)
goto truncate;
- wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ wattr_set(win, attrs ^ WA_REVERSE, pair, NULL);
waddstr(win, "TAB");
break;
default:
@@ -110,7 +110,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ wattr_set(win, attrs ^ WA_REVERSE, pair, NULL);
waddch(win, '^');
waddch(win, TECO_CTL_ECHO(*str));
} else {
@@ -126,7 +126,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
waddnstr(win, str, clen);
}
}
- /* restore original state of A_REVERSE */
+ /* restore original state of WA_REVERSE */
wattr_set(win, attrs, pair, NULL);
str += clen;
diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h
index 97fc1cc..c6d9d8d 100644
--- a/src/interface-curses/curses-utils.h
+++ b/src/interface-curses/curses-utils.h
@@ -27,15 +27,12 @@ guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_
guint teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width);
-/**
- * Add Unicode character to window.
- * This is just like wadd_wch(), but does not require wide-char APIs.
- */
+/** Add Unicode character to window. */
static inline void
teco_curses_add_wc(WINDOW *win, gunichar chr)
{
- gchar buf[6];
- waddnstr(win, buf, g_unichar_to_utf8(chr, buf));
+ wchar_t wc = chr;
+ waddnwstr(win, &wc, 1);
}
/**
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 0764db3..21e728f 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -125,7 +125,8 @@ teco_console_ctrl_handler(DWORD type)
{
switch (type) {
case CTRL_C_EVENT:
- teco_interrupted = TRUE;
+ case CTRL_BREAK_EVENT:
+ teco_interrupted = TECO_INTERRUPTED;
return TRUE;
}
@@ -137,6 +138,17 @@ teco_console_ctrl_handler(DWORD type)
static gint teco_xterm_version(void) G_GNUC_UNUSED;
static gint teco_interface_blocking_getch(void);
+static inline gboolean teco_interface_check_key(gint key);
+
+#define MAX_COLORS (MIN(COLORS, G_MAXSHORT)/2)
+/**
+ * Maximum effective number of color pairs.
+ * Scinterm uses shorts for color pairs, so G_MAXSHORT is the maximum.
+ * Some Curses implementations (NetBSD, PDCurses) may support less,
+ * but set COLOR_PAIRS accordingly.
+ * Half of the range is reserved to Scinterm.
+ */
+#define MAX_PAIRS (MIN(COLOR_PAIRS, G_MAXSHORT)/2)
/**
* Get bright variant of one of the 8 standard
@@ -154,6 +166,12 @@ static gint teco_interface_blocking_getch(void);
* The 8 bright colors (if terminal supports at
* least 16 colors), else they are identical to
* the non-bright colors (default curses colors).
+ *
+ * As a consequence a light black (grey) foreground
+ * on black background might be invisible on 8 color
+ * terminals.
+ * If you add in A_BOLD, there is a good chance that
+ * the text will still become grey.
*/
#define COLOR_LBLACK COLOR_LIGHT(COLOR_BLACK)
#define COLOR_LRED COLOR_LIGHT(COLOR_RED)
@@ -166,30 +184,22 @@ static gint teco_interface_blocking_getch(void);
static struct {
/**
+ * Mapping of RGB codes (encoded into a pointer)
+ * to a curses color number.
+ */
+ GHashTable *color_table;
+ /**
* Mapping of foreground and background curses color tuples
* (encoded into a pointer) to a color pair number.
*/
GHashTable *pair_table;
-
/**
- * Mapping of the first 16 curses color codes (that may or may not
- * correspond with the standard terminal color codes) to
- * Scintilla-compatible RGB values (red is LSB) to initialize after
- * Curses startup.
- * Negative values mean no color redefinition (keep the original
- * palette entry).
+ * Whether to map the 16 standard palettized colors
+ * in color_table.
+ * Should be disabled when using arbitrary RGB values,
+ * so every color is initialized to exact RGB values.
*/
- gint32 color_table[16];
-
- /**
- * Mapping of the first 16 curses color codes to their
- * original values for restoring them on shutdown.
- * Unfortunately, this may not be supported on all
- * curses ports, so this array may be unused.
- */
- struct {
- gshort r, g, b;
- } orig_color_table[16];
+ gboolean use_palette;
int stdin_orig, stdout_orig, stderr_orig;
SCREEN *screen;
@@ -232,56 +242,50 @@ static struct {
*
* Scinterm no longer initializes all color pairs for all combinations of
* the builtin foreground and background colors.
- * Since curses guarantees only 256 color pairs, we cannot do that either.
- * Instead we allocate color pairs beginnig at 128 on demand
- * (similar to what Scinterm does).
- *
- * @note Scinterm now also has scintilla_set_color_offsets(),
- * so we could use the lower 127 color pairs as well.
+ * Since curses does not guarantee any number of color pairs, we cannot do that either.
+ * Instead we allocate color pairs in the first half of
+ * color pair space on demand, while the second half is reserved to Scinterm.
*
+ * @param attr attributes to modify (for supporting monochrome terminals)
* @param fg curses foreground color
* @param bg curses background color
* @return curses color pair number
*/
static gshort
-teco_color_pair(gshort fg, gshort bg)
+teco_color_pair(attr_t *attr, gshort fg, gshort bg)
{
- static gshort last_pair = 127;
+ static guint next_pair = 1;
+
+ /*
+ * Basic support for monochrome terminals:
+ * Every background, that is not black is assumed to be a
+ * dark-on-bright area, rendered in reverse.
+ * This will at least work with the terminal.tes and contrast.tes
+ * color schemes.
+ */
+ if (!has_colors()) {
+ if (bg != COLOR_BLACK)
+ *attr |= WA_REVERSE;
+ return 0;
+ }
G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
- gpointer key = GUINT_TO_POINTER(((guint)fg << 16) | bg);
+ gpointer key = GUINT_TO_POINTER(((guint)fg << 8*sizeof(bg)) | bg);
gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
if (G_LIKELY(value != NULL))
return GPOINTER_TO_UINT(value);
- init_pair(++last_pair, fg, bg);
- g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(last_pair));
- return last_pair;
+ if (G_UNLIKELY(next_pair >= MAX_PAIRS))
+ return 0;
+ init_pair(next_pair, fg, bg);
+ g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(next_pair));
+ return next_pair++;
}
-/**
- * Curses attribute for the color combination
- * according to the color pairs initialized by
- * Scinterm.
- * This is equivalent to Scinterm's internal term_color_attr().
- *
- * @param fg foreground color
- * @param bg background color
- * @return curses attribute
- */
-static inline attr_t
-teco_color_attr(gshort fg, gshort bg)
+static inline gint
+teco_wattr_set(WINDOW *win, attr_t attr, gshort fg, gshort bg)
{
- if (has_colors())
- return COLOR_PAIR(teco_color_pair(fg, bg));
-
- /*
- * Basic support for monochrome terminals:
- * Every background, that is not black is assumed to be a
- * dark-on-bright area, rendered in reverse.
- * This will at least work with the terminal.tes
- * color scheme.
- */
- return bg != COLOR_BLACK ? A_REVERSE : 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ return wattr_set(win, attr, pair, NULL);
}
/**
@@ -298,38 +302,60 @@ teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
*b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
}
+static inline void
+teco_interface_insert_color(guint32 rgb, gshort color)
+{
+ /*
+ * Color ids are stored one-offset, so that COLOR_BLACK
+ * can be discerned from a missing entry.
+ */
+ g_hash_table_insert(teco_interface.color_table,
+ GUINT_TO_POINTER(rgb), GUINT_TO_POINTER(color)+1);
+}
+
/**
* Convert a Scintilla-compatible RGB color value
* (0xBBGGRR) to a Curses color code (e.g. COLOR_BLACK).
- * This does not work with arbitrary RGB values but
- * only the 16 RGB color values defined by Scinterm
- * corresponding to the 16 terminal colors.
- * It is equivalent to Scinterm's internal `term_color`
- * function.
+ *
+ * This will automatically initialize new curses colors
+ * when necessary.
+ * Unless the palette is disabled with
+ * teco_interface_disable_palette(), this will return
+ * the 16 "standard" colors for a predefined list of
+ * RGB values (just like defined by Scinterm).
*/
static gshort
teco_rgb2curses(guint32 rgb)
{
- switch (rgb) {
- case 0x000000: return COLOR_BLACK;
- case 0x000080: return COLOR_RED;
- case 0x008000: return COLOR_GREEN;
- case 0x008080: return COLOR_YELLOW;
- case 0x800000: return COLOR_BLUE;
- case 0x800080: return COLOR_MAGENTA;
- case 0x808000: return COLOR_CYAN;
- case 0xC0C0C0: return COLOR_WHITE;
- case 0x404040: return COLOR_LBLACK;
- case 0x0000FF: return COLOR_LRED;
- case 0x00FF00: return COLOR_LGREEN;
- case 0x00FFFF: return COLOR_LYELLOW;
- case 0xFF0000: return COLOR_LBLUE;
- case 0xFF00FF: return COLOR_LMAGENTA;
- case 0xFFFF00: return COLOR_LCYAN;
- case 0xFFFFFF: return COLOR_LWHITE;
- }
+ static guint colors = 0;
+
+ if (!has_colors())
+ return COLOR_WHITE;
- return COLOR_WHITE;
+ gpointer value = g_hash_table_lookup(teco_interface.color_table,
+ GUINT_TO_POINTER(rgb));
+ if (G_LIKELY(value != NULL))
+ return GPOINTER_TO_UINT(value)-1;
+
+ /*
+ * Even when initializing new colors, we will __try__ not to
+ * touch the 16 palettized colors.
+ * First of all, we might still need them and secondly,
+ * this ensures we don't have to reset them on exit
+ * (which is not always possible anyway).
+ * There can still be 16 color terminals with
+ * can_change_color() == TRUE, though.
+ */
+ guint next_color = colors + (COLORS > 16 ? 16 : 0);
+
+ if (G_UNLIKELY(next_color >= MAX_COLORS) || !can_change_color())
+ return COLOR_WHITE;
+ gshort r, g, b;
+ teco_rgb2curses_triple(rgb, &r, &g, &b);
+ init_color(next_color, r, g, b);
+ teco_interface_insert_color(rgb, next_color);
+ colors++;
+ return next_color;
}
static gint
@@ -395,6 +421,13 @@ teco_view_noutrefresh(teco_view_t *ctx)
scintilla_noutrefresh(ctx);
}
+static inline void
+teco_view_update_cursor(teco_view_t *ctx)
+{
+ if (teco_view_ssm(ctx, SCI_GETCARETSTYLE, 0, 0) & CARETSTYLE_CURSES)
+ scintilla_update_cursor(ctx);
+}
+
static inline WINDOW *
teco_view_get_window(teco_view_t *ctx)
{
@@ -413,9 +446,6 @@ teco_view_free(teco_view_t *ctx)
scintilla_delete(ctx);
}
-static void teco_interface_init_color_safe(guint color, guint32 rgb);
-static void teco_interface_restore_colors(void);
-
static void teco_interface_init_screen(void);
static gboolean teco_interface_init_interactive(GError **error);
static void teco_interface_restore_batch(void);
@@ -428,12 +458,9 @@ static void teco_interface_set_window_title(const gchar *title);
static void teco_interface_draw_info(void);
void
-teco_interface_init(void)
+teco_interface_init(gint argc, gchar **argv)
{
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++)
- teco_interface.color_table[i] = -1;
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++)
- teco_interface.orig_color_table[i].r = -1;
+ teco_interface.use_palette = TRUE;
teco_interface.stdin_orig = teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
@@ -442,9 +469,13 @@ teco_interface_init(void)
teco_cmdline_init();
/*
* The default INDIC_STRIKE wouldn't be visible.
- * Instead we use INDIC_SQUIGGLE, which is rendered as A_UNDERLINE.
+ * Instead we use INDIC_SQUIGGLE, which is rendered as WA_UNDERLINE.
*/
teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_SQUIGGLE);
+ /*
+ * Enable hardware cursor by default.
+ */
+ teco_cmdline_ssm(SCI_SETCARETSTYLE, CARETSTYLE_CURSES, 0);
/*
* On all platforms except Curses/XTerm, it's
@@ -471,133 +502,11 @@ teco_interface_get_options(void)
return NULL;
}
-static void
-teco_interface_init_color_safe(guint color, guint32 rgb)
-{
-#if defined(__PDCURSES__) && !defined(PDCURSES_GUI)
- if (teco_interface.orig_color_table[color].r < 0) {
- color_content((short)color,
- &teco_interface.orig_color_table[color].r,
- &teco_interface.orig_color_table[color].g,
- &teco_interface.orig_color_table[color].b);
- }
-#endif
-
- gshort r, g, b;
- teco_rgb2curses_triple(rgb, &r, &g, &b);
- init_color((short)color, r, g, b);
-}
-
-#if defined(__PDCURSES__) && !defined(PDCURSES_GUI)
-
-/*
- * On PDCurses/WinCon, color_content() will actually return
- * the real console color palette - or at least the default
- * palette when the console started.
- */
-static void
-teco_interface_restore_colors(void)
-{
- if (!can_change_color())
- return;
-
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++) {
- if (teco_interface.orig_color_table[i].r < 0)
- continue;
-
- init_color((short)i,
- teco_interface.orig_color_table[i].r,
- teco_interface.orig_color_table[i].g,
- teco_interface.orig_color_table[i].b);
- }
-}
-
-#elif defined(CURSES_TTY)
-
-/*
- * FIXME: On UNIX/ncurses teco_interface_init_color_safe() __may__
- * change the terminal's palette permanently and there does not
- * appear to be any portable way of restoring the original one.
- * Curses has color_content(), but there is actually no terminal
- * that allows querying the current palette and so color_content()
- * will return bogus "default" values and only for the first 8 colors.
- * It would do more damage to restore the palette returned by
- * color_content() than it helps.
- * xterm has the escape sequence "\e]104\a" which restores
- * the palette from Xdefaults but not all terminal emulators
- * claiming to be "xterm" via $TERM support this escape sequence.
- * lxterminal for instance will print gibberish instead.
- * So we try to look whether $XTERM_VERSION is set.
- * There are hardly any other terminal emulators that support palette
- * resets.
- * The only emulator I'm aware of which can be identified reliably
- * by $TERM supporting a palette reset is the Linux console
- * (see console_codes(4)). The escape sequence "\e]R" is already
- * part of its terminfo description (orig_colors capability)
- * which is apparently sent by endwin(), so the palette is
- * already properly restored on endwin().
- * Welcome in Curses hell.
- */
-static void
-teco_interface_restore_colors(void)
-{
- if (teco_xterm_version() < 0)
- return;
-
- /*
- * Looks like a real XTerm
- */
- fputs("\e]104\a", teco_interface.screen_tty);
- fflush(teco_interface.screen_tty);
-}
-
-#else /* (!__PDCURSES__ || PDCURSES_GUI) && !CURSES_TTY */
-
-static void
-teco_interface_restore_colors(void)
-{
- /*
- * No way to restore the palette, or it's
- * unnecessary (e.g. XCurses)
- */
-}
-
-#endif
-
void
-teco_interface_init_color(guint color, guint32 rgb)
+teco_interface_disable_palette(void)
{
- if (color >= G_N_ELEMENTS(teco_interface.color_table))
- return;
-
-#if defined(__PDCURSES__) && !defined(PDC_RGB)
- /*
- * PDCurses will usually number color codes differently
- * (least significant bit is the blue component) while
- * SciTECO macros will assume a standard terminal color
- * code numbering with red as the LSB.
- * Therefore we have to swap the bit order of the least
- * significant 3 bits here.
- */
- color = (color & ~0x5) |
- ((color & 0x1) << 2) | ((color & 0x4) >> 2);
-#endif
-
- if (teco_interface.input_pad) {
- /* interactive mode */
- if (!can_change_color())
- return;
-
- teco_interface_init_color_safe(color, rgb);
- } else {
- /*
- * batch mode: store colors,
- * they can only be initialized after start_color()
- * which is called by Scinterm when interactive
- * mode is initialized
- */
- teco_interface.color_table[color] = (gint32)rgb;
- }
+ teco_interface.use_palette = FALSE;
+ scintilla_disable_color_palette();
}
#ifdef CURSES_TTY
@@ -739,8 +648,33 @@ teco_interface_init_interactive(GError **error)
teco_interface_init_screen();
+ teco_interface.color_table = g_hash_table_new(g_direct_hash, g_direct_equal);
teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal);
start_color();
+ scintilla_set_color_offsets(MAX_COLORS, MAX_PAIRS);
+
+ if (teco_interface.use_palette && has_colors()) {
+ teco_interface_insert_color(0x000000, COLOR_BLACK);
+ teco_interface_insert_color(0x000080, COLOR_RED);
+ teco_interface_insert_color(0x008000, COLOR_GREEN);
+ teco_interface_insert_color(0x008080, COLOR_YELLOW);
+ teco_interface_insert_color(0x800000, COLOR_BLUE);
+ teco_interface_insert_color(0x800080, COLOR_MAGENTA);
+ teco_interface_insert_color(0x808000, COLOR_CYAN);
+ teco_interface_insert_color(0xC0C0C0, COLOR_WHITE);
+ /*
+ * On 8-color terminals, we still support the
+ * higher (light) colors, but map them to the lower codes as well.
+ */
+ teco_interface_insert_color(0x404040, COLOR_LBLACK);
+ teco_interface_insert_color(0x0000FF, COLOR_LRED);
+ teco_interface_insert_color(0x00FF00, COLOR_LGREEN);
+ teco_interface_insert_color(0x00FFFF, COLOR_LYELLOW);
+ teco_interface_insert_color(0xFF0000, COLOR_LBLUE);
+ teco_interface_insert_color(0xFF00FF, COLOR_LMAGENTA);
+ teco_interface_insert_color(0xFFFF00, COLOR_LCYAN);
+ teco_interface_insert_color(0xFFFFFF, COLOR_LWHITE);
+ }
/*
* On UNIX terminals, the escape key is usually
@@ -784,8 +718,6 @@ teco_interface_init_interactive(GError **error)
cbreak();
noecho();
- /* Scintilla draws its own cursor */
- curs_set(0);
/*
* This has also been observed to reduce flickering
* in teco_interface_refresh().
@@ -824,19 +756,6 @@ teco_interface_init_interactive(GError **error)
teco_interface_show_view(teco_interface_current_view);
/*
- * Only now it's safe to redefine the 16 default colors.
- */
- if (can_change_color()) {
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) {
- /*
- * init_color() may still fail if COLORS < 16
- */
- if (teco_interface.color_table[i] >= 0)
- teco_interface_init_color_safe(i, (guint32)teco_interface.color_table[i]);
- }
- }
-
- /*
* Only now (in interactive mode), it's safe to initialize
* the clipboard Q-Registers on ncurses with a compatible terminal
* emulator since clipboard operations will no longer interfer
@@ -868,7 +787,6 @@ teco_interface_restore_batch(void)
* (i.e. return to batch mode)
*/
endwin();
- teco_interface_restore_colors();
/*
* Restore stdin, stdout and stderr, so output goes to
@@ -933,7 +851,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
teco_interface_stdio_msg(type, str, len);
#endif
- short fg, bg;
+ gshort fg, bg;
fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
@@ -955,7 +873,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
}
wmove(teco_interface.msg_window, 0, 0);
- wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
teco_curses_format_str(teco_interface.msg_window, str, len, -1);
teco_curses_clrtobot(teco_interface.msg_window);
}
@@ -966,11 +884,11 @@ teco_interface_msg_clear(void)
if (!teco_interface.input_pad) /* batch mode */
return;
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
wmove(teco_interface.msg_window, 0, 0);
- wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
teco_curses_clrtobot(teco_interface.msg_window);
}
@@ -984,25 +902,41 @@ teco_interface_getch(gboolean widechar)
/*
* Signal that we accept input by drawing a real cursor in the message bar.
+ * FIXME: curs_set(2) would be better, but isn't always visible.
*/
wmove(teco_interface.msg_window, 0, 0);
- curs_set(1);
wrefresh(teco_interface.msg_window);
+ curs_set(1);
gchar buf[4];
gint i = 0;
gint32 cp;
do {
- cp = teco_interface_blocking_getch();
- if (cp == TECO_CTL_KEY('C'))
- teco_interrupted = TRUE;
- if (cp == TECO_CTL_KEY('C') || cp == TECO_CTL_KEY('D')) {
- cp = -1;
- break;
- }
- if (cp < 0 || cp > 0xFF)
- continue;
+ do {
+ cp = teco_interface_blocking_getch();
+ switch (cp) {
+#ifdef KEY_RESIZE
+ case KEY_RESIZE:
+ teco_interface_resize_all_windows();
+ teco_interface_refresh(FALSE);
+ /* cursor was moved by the resize */
+ wmove(teco_interface.msg_window, 0, 0);
+ wrefresh(teco_interface.msg_window);
+ break;
+#endif
+ case KEY_BACKSPACE:
+ return TECO_CTL_KEY('H');
+ case KEY_ENTER:
+ return '\n';
+ case TECO_CTL_KEY('C'):
+ teco_interrupted = TECO_INTERRUPTED;
+ /* fall through */
+ case TECO_CTL_KEY('D'):
+ /* emulates EOF on stdin */
+ return -1;
+ }
+ } while (cp < 0 || cp > 0xFF || !teco_interface_check_key(cp));
if (!widechar || !cp)
break;
@@ -1014,7 +948,6 @@ teco_interface_getch(gboolean widechar)
i = 0;
} while (cp < 0);
- curs_set(0);
return cp;
}
@@ -1144,11 +1077,11 @@ teco_interface_draw_info(void)
* the current buffer's STYLE_DEFAULT.
* The same style is used for MSG_USER messages.
*/
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
wmove(teco_interface.info_window, 0, 0);
- wattrset(teco_interface.info_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.info_window, 0, fg, bg);
const gchar *info_type_str;
@@ -1229,7 +1162,20 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer)
* default clipboard ("~") as we do not know whether
* it corresponds to the X11 PRIMARY, SECONDARY or
* CLIPBOARD selections.
+ *
+ * On XCurses we must not (and don't have to) probe
+ * the clipboard as it would be before Xinitscr().
*/
+#ifdef XCURSES
+
+static void
+teco_interface_init_clipboard(void)
+{
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
+}
+
+#else /* XCURSES */
+
static void
teco_interface_init_clipboard(void)
{
@@ -1254,6 +1200,8 @@ teco_interface_init_clipboard(void)
teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
}
+#endif /* !XCURSES */
+
gboolean
teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, GError **error)
{
@@ -1702,11 +1650,13 @@ teco_interface_popup_show(gsize prefix_len)
/* batch mode */
return;
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
teco_interface.popup_prefix_len = prefix_len;
- teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg));
+ attr_t attr = 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ teco_curses_info_popup_show(&teco_interface.popup, attr, pair);
}
void
@@ -1757,7 +1707,7 @@ teco_interface_popup_clear(void)
gboolean
teco_interface_is_interrupted(void)
{
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
#else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 */
@@ -1776,7 +1726,7 @@ teco_interface_is_interrupted(void)
{
if (!teco_interface.input_pad)
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* NOTE: wgetch() is configured to be nonblocking.
@@ -1791,7 +1741,7 @@ teco_interface_is_interrupted(void)
GINT_TO_POINTER(key));
}
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
#endif
@@ -1824,7 +1774,31 @@ teco_interface_refresh(gboolean force)
wnoutrefresh(teco_interface.msg_window);
teco_view_noutrefresh(teco_cmdline.view);
teco_curses_info_popup_noutrefresh(&teco_interface.popup);
+ /*
+ * If hardware cursors (CARETSTYLE_CURSES) are enabled on the
+ * command-line view, make sure that the cursor is left
+ * in the correct position.
+ *
+ * FIXME: This shouldn't be necessary if we refreshed the command-line
+ * view last. Also, if we wanted to support the hardware cursor
+ * in the main view as well, we'd have to handle a possibly
+ * overlappig info popup.
+ */
+ teco_view_update_cursor(teco_cmdline.view);
doupdate();
+
+ /*
+ * Scinterm enables/disables the hardware cursor,
+ * but only if CARETSTYLE_CURSES is used.
+ * Disabling the cursor repeatedly ensures you can change
+ * the caret style interactively.
+ * It also makes sure the cursor is reset after
+ * teco_interface_getch().
+ * Also, window resizes sometimes enable the hardware cursor on
+ * PDCurses/wincon (FIXME).
+ */
+ if (!(teco_view_ssm(teco_cmdline.view, SCI_GETCARETSTYLE, 0, 0) & CARETSTYLE_CURSES))
+ curs_set(0);
}
#if NCURSES_MOUSE_VERSION >= 2
@@ -1882,9 +1856,11 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
else if (event->bstate & BUTTON_NUM(5))
teco_curses_info_popup_scroll(&teco_interface.popup, +2);
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
- teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ attr_t attr = 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ teco_curses_info_popup_show(&teco_interface.popup, attr, pair);
return TRUE;
}
@@ -2016,7 +1992,8 @@ teco_interface_blocking_getch(void)
gboolean new_mousekey = (teco_ed & TECO_ED_MOUSEKEY) != 0;
if (new_mousekey != old_mousekey) {
old_mousekey = new_mousekey;
- mmask_t mmask = BUTTON_EVENT(PRESSED) | BUTTON_EVENT(RELEASED);
+ mmask_t mmask = BUTTON_EVENT(PRESSED) | BUTTON_EVENT(RELEASED) |
+ BUTTON_SHIFT | BUTTON_CTRL | BUTTON_ALT;
#ifdef __PDCURSES__
/*
* On PDCurses it's crucial NOT to mask for BUTTONX_CLICKED.
@@ -2053,7 +2030,7 @@ teco_interface_blocking_getch(void)
gint key = wgetch(teco_interface.input_pad);
teco_memory_start_limiting();
/* allow asynchronous interruptions on <CTRL/C> */
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
wtimeout(teco_interface.input_pad, 0);
#if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32)
noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */
@@ -2069,6 +2046,31 @@ teco_interface_blocking_getch(void)
return key;
}
+#ifdef __PDCURSES__
+
+/*
+ * Especially PDCurses/WinGUI likes to report two keypresses,
+ * e.g. for CTRL+Shift+6 (CTRL+^).
+ * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT.
+ */
+static inline gboolean
+teco_interface_check_key(gint key)
+{
+ return (PDC_get_key_modifiers() &
+ (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) != PDC_KEY_MODIFIER_CONTROL ||
+ TECO_IS_CTL(key);
+}
+
+#else /* __PDCURSES__ */
+
+static inline gboolean
+teco_interface_check_key(gint key)
+{
+ return TRUE;
+}
+
+#endif
+
/**
* One iteration of the event loop.
*
@@ -2099,11 +2101,6 @@ teco_interface_event_loop_iter(void)
return;
#ifdef KEY_RESIZE
case KEY_RESIZE:
- /*
- * At least on PDCurses/Wincon, the hardware cursor is sometimes
- * reactivated.
- */
- curs_set(0);
teco_interface_resize_all_windows();
break;
#endif
@@ -2179,21 +2176,9 @@ teco_interface_event_loop_iter(void)
* Control keys and keys with printable representation
*/
default:
- if (key > 0xFF)
- /* unhandled function key */
- return;
-
-#ifdef __PDCURSES__
- /*
- * Especially PDCurses/WinGUI likes to report two keypresses,
- * e.g. for CTRL+Shift+6 (CTRL+^).
- * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT.
- */
- if ((PDC_get_key_modifiers() &
- (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) == PDC_KEY_MODIFIER_CONTROL &&
- !TECO_IS_CTL(key))
+ if (key > 0xFF || !teco_interface_check_key(key))
+ /* unhandled function key or bogus key press */
return;
-#endif
/*
* NOTE: There's also wget_wch(), but it requires
@@ -2325,6 +2310,8 @@ teco_interface_cleanup(void)
if (teco_interface.stdout_orig >= 0)
close(teco_interface.stdout_orig);
+ if (teco_interface.color_table)
+ g_hash_table_destroy(teco_interface.color_table);
if (teco_interface.pair_table)
g_hash_table_destroy(teco_interface.pair_table);
diff --git a/src/interface-gtk/gtk-info-popup.c b/src/interface-gtk/gtk-info-popup.c
index 769f772..f2c8dc8 100644
--- a/src/interface-gtk/gtk-info-popup.c
+++ b/src/interface-gtk/gtk-info-popup.c
@@ -30,6 +30,8 @@
#include "gtk-label.h"
#include "gtk-info-popup.h"
+#define TECO_UNNAMED_FILE "(Unnamed)"
+
/*
* FIXME: This is redundant with curses-info-popup.c.
*/
@@ -336,7 +338,7 @@ teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t typ
}
}
- GtkWidget *label = teco_gtk_label_new(name, len);
+ GtkWidget *label = teco_gtk_label_new(name, len, TECO_UNNAMED_FILE);
/*
* Gtk v3.20 changed the CSS element names.
* Adding a style class eases writing a portable fallback.css.
diff --git a/src/interface-gtk/gtk-label.c b/src/interface-gtk/gtk-label.c
index 5052cdc..6e05045 100644
--- a/src/interface-gtk/gtk-label.c
+++ b/src/interface-gtk/gtk-label.c
@@ -32,8 +32,6 @@
#include "gtk-label.h"
-#define TECO_UNNAMED_FILE "(Unnamed)"
-
#define GDK_TO_PANGO_COLOR(X) ((guint16)((X) * G_MAXUINT16))
struct _TecoGtkLabel {
@@ -42,8 +40,10 @@ struct _TecoGtkLabel {
PangoColor fg, bg;
guint16 fg_alpha, bg_alpha;
- /** text backing the label or empty string for "(Unnamed)" buffer */
+ /** text backing the label or empty string for fallback */
teco_string_t string;
+ /** fallback string to render if `string` is empty or NULL */
+ const gchar *fallback;
};
G_DEFINE_TYPE(TecoGtkLabel, teco_gtk_label, GTK_TYPE_LABEL)
@@ -125,11 +125,21 @@ teco_gtk_label_class_init(TecoGtkLabelClass *klass)
static void teco_gtk_label_init(TecoGtkLabel *self) {}
+/**
+ * Create new TECO label widget.
+ *
+ * @param str String to render (can be NULL)
+ * @param len Length of str or negative value for null-terminated strings.
+ * @param fallback Null-terminated fallback string to render
+ * instead of empty strings.
+ * Must be a string constant with global lifetime or NULL.
+ */
GtkWidget *
-teco_gtk_label_new(const gchar *str, gssize len)
+teco_gtk_label_new(const gchar *str, gssize len, const gchar *fallback)
{
TecoGtkLabel *widget = TECO_GTK_LABEL(g_object_new(TECO_TYPE_GTK_LABEL, NULL));
+ widget->fallback = fallback;
teco_gtk_label_set_text(widget, str, len);
return GTK_WIDGET(widget);
@@ -255,8 +265,8 @@ teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len)
teco_string_init(&self->string, str, len < 0 ? strlen(str) : len);
teco_string_t string = self->string;
- if (!string.len) {
- string.data = TECO_UNNAMED_FILE;
+ if (!string.len && self->fallback) {
+ string.data = (gchar *)self->fallback;
string.len = strlen(string.data);
}
diff --git a/src/interface-gtk/gtk-label.h b/src/interface-gtk/gtk-label.h
index ad39c6e..a84608a 100644
--- a/src/interface-gtk/gtk-label.h
+++ b/src/interface-gtk/gtk-label.h
@@ -24,7 +24,7 @@
#define TECO_TYPE_GTK_LABEL teco_gtk_label_get_type()
G_DECLARE_FINAL_TYPE(TecoGtkLabel, teco_gtk_label, TECO, GTK_LABEL, GtkLabel)
-GtkWidget *teco_gtk_label_new(const gchar *str, gssize len);
+GtkWidget *teco_gtk_label_new(const gchar *str, gssize len, const gchar *fallback);
void teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len);
teco_string_t teco_gtk_label_get_text(TecoGtkLabel *self);
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index 0e1507f..9ef86a1 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
+#include <unistd.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -77,7 +78,7 @@ static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong
gpointer user_data);
static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event,
gpointer user_data);
-static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
+static gboolean teco_interface_termination_handler(gpointer user_data) G_GNUC_UNUSED;
static gchar teco_interface_get_ansi_key(GdkEventKey *event);
#define TECO_UNNAMED_FILE "(Unnamed)"
@@ -132,10 +133,10 @@ static struct {
} teco_interface;
void
-teco_interface_init(void)
+teco_interface_init(gint argc, gchar **argv)
{
#ifdef G_OS_UNIX
- if (teco_interface.detach) {
+ if (teco_interface.detach && !g_getenv("__SCITECO_DETACHED")) {
/*
* NOTE: There is also daemon() on BSD/Linux,
* but the following should be more portable.
@@ -148,9 +149,29 @@ teco_interface_init(void)
setsid();
- g_freopen("/dev/null", "r", stdin);
- g_freopen("/dev/null", "a+", stdout);
- g_freopen("/dev/null", "a+", stderr);
+ if (isatty(0)) {
+ G_GNUC_UNUSED FILE *stdin_new = g_freopen("/dev/null", "r", stdin);
+ g_assert(stdin_new != NULL);
+ }
+ if (isatty(1)) {
+ G_GNUC_UNUSED FILE *stdout_new = g_freopen("/dev/null", "a+", stdout);
+ g_assert(stdout_new != NULL);
+ }
+ if (isatty(2)) {
+ G_GNUC_UNUSED FILE *stderr_new = g_freopen("/dev/null", "a+", stderr);
+ g_assert(stderr_new != NULL);
+ }
+
+ /*
+ * gtk_get_option_group() already initialized GTK and even though the
+ * display is not yet opened, it's unsafe to continue.
+ * Instead, we re-exec already in the child process.
+ * We cannot easily remove --detach from argv, but still guard against
+ * recursive forks by using an environment variable.
+ */
+ g_setenv("__SCITECO_DETACHED", "1", TRUE);
+ execv(argv[0], argv);
+ g_assert_not_reached();
}
#endif
@@ -202,7 +223,7 @@ teco_interface_init(void)
*/
teco_interface.info_bar_widget = gtk_header_bar_new();
gtk_widget_set_name(teco_interface.info_bar_widget, "sciteco-info-bar");
- teco_interface.info_name_widget = teco_gtk_label_new("", 0);
+ teco_interface.info_name_widget = teco_gtk_label_new(NULL, 0, NULL);
gtk_widget_set_valign(teco_interface.info_name_widget, GTK_ALIGN_CENTER);
/* eases writing portable fallback.css that avoids CSS element names */
gtk_style_context_add_class(gtk_widget_get_style_context(teco_interface.info_name_widget),
@@ -288,7 +309,7 @@ teco_interface_init(void)
gtk_widget_set_name(teco_interface.message_bar_widget, "sciteco-message-bar");
GtkWidget *message_bar_content =
gtk_info_bar_get_content_area(GTK_INFO_BAR(teco_interface.message_bar_widget));
- teco_interface.message_widget = teco_gtk_label_new(NULL, 0);
+ teco_interface.message_widget = teco_gtk_label_new(NULL, 0, NULL);
/* eases writing portable fallback.css that avoids CSS element names */
gtk_style_context_add_class(gtk_widget_get_style_context(teco_interface.message_widget),
"label");
@@ -383,7 +404,7 @@ teco_interface_get_options(void)
return group;
}
-void teco_interface_init_color(guint color, guint32 rgb) {}
+void teco_interface_disable_palette(void) {}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
@@ -430,14 +451,15 @@ teco_interface_getch_commit_cb(GtkIMContext *context, gchar *str, gpointer user_
*/
*cp = g_utf8_get_char_validated(str, -1);
g_assert(*cp >= 0);
- gtk_main_quit();
+
+ /* we might be invoked outside of a nested event loop */
+ if (gtk_main_level() > 1)
+ gtk_main_quit();
}
static gboolean
-teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+teco_interface_getch_process_event(GdkEvent *event, teco_int_t *cp)
{
- teco_int_t *cp = user_data;
-
g_assert(event->type == GDK_KEY_PRESS);
switch (event->key.keyval) {
@@ -455,19 +477,34 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_
*cp = TECO_CTL_KEY(g_ascii_toupper(*cp));
switch (*cp) {
case TECO_CTL_KEY('C'):
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
/* fall through */
case TECO_CTL_KEY('D'):
+ /* emulates EOF on stdin */
*cp = -1;
}
break;
}
+ /*
+ * NOTE: The teco_interface_getch_commit_cb() could be called immediately
+ * without returning to the event loop.
+ */
gtk_im_context_filter_keypress(teco_interface.input_method, &event->key);
- return TRUE;
+ return *cp >= 0;
}
- gtk_main_quit();
+ return TRUE;
+}
+
+static gboolean
+teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ teco_int_t *cp = user_data;
+
+ if (teco_interface_getch_process_event(event, cp))
+ gtk_main_quit();
+
return TRUE;
}
@@ -492,6 +529,22 @@ teco_interface_getch(gboolean widechar)
G_CALLBACK(teco_interface_getch_commit_cb), &cp);
/*
+ * The original teco_interface_input_cb() could have already enqueued events
+ * (also between ^T calls).
+ * This must be done after registering teco_interface_getch_commit_cb() already.
+ *
+ * NOTE: Already enqueued mouse events will currently be discarded silently.
+ */
+ for (;;) {
+ g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue);
+ if (!event)
+ break;
+ if (event->type == GDK_KEY_PRESS &&
+ teco_interface_getch_process_event(event, &cp))
+ goto cleanup;
+ }
+
+ /*
* Highlights the first character in the label.
* This mimics what the Curses UI does.
* Is there a better way to signal that we expect input?
@@ -505,6 +558,7 @@ teco_interface_getch(gboolean widechar)
gdk_window_freeze_updates(top_window);
+cleanup:
g_signal_handler_disconnect(teco_interface.input_method, commit_handler);
g_signal_handlers_unblock_by_func(teco_interface.input_method, teco_interface_cmdline_commit_cb, NULL);
g_signal_handler_disconnect(teco_interface.window, key_handler);
@@ -769,7 +823,7 @@ teco_interface_is_interrupted(void)
{
if (!gtk_main_level())
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* By polling only every TECO_POLL_INTERVAL microseconds
@@ -779,11 +833,11 @@ teco_interface_is_interrupted(void)
guint64 now_ts = g_get_monotonic_time();
if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts))
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
last_poll_ts = now_ts;
gtk_main_iteration_do(FALSE);
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
void
@@ -1244,13 +1298,12 @@ teco_interface_event_loop(GError **error)
#endif
/*
- * SIGTERM emulates the "Close" key just like when
- * closing the window if supported by this version of glib.
- * Note that this replaces SciTECO's default SIGTERM handler
- * so it will additionally raise(SIGINT).
+ * SIGTERM and SIGHUP terminate the program, but will
+ * dump recovery files first (if enabled).
*/
#ifdef G_OS_UNIX
- g_unix_signal_add(SIGTERM, teco_interface_sigterm_handler, NULL);
+ g_unix_signal_add(SIGTERM, teco_interface_termination_handler, NULL);
+ g_unix_signal_add(SIGHUP, teco_interface_termination_handler, NULL);
#endif
/* the interval might have been changed in the profile */
@@ -1374,7 +1427,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
* If the execution thread is currently blocking,
* the key is delivered like an ordinary key press.
*/
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
else
g_queue_push_tail(teco_interface.event_queue,
gdk_event_copy(event));
@@ -1415,7 +1468,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
switch (event->type) {
case GDK_KEY_PRESS:
teco_interface_handle_key_press(&event->key, &error);
@@ -1432,7 +1485,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
default:
g_assert_not_reached();
}
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
teco_interface_update(teco_interface_current_view != last_view);
/* always expand folds, even after mouse clicks */
@@ -1542,16 +1595,23 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer
}
static gboolean
-teco_interface_sigterm_handler(gpointer user_data)
+teco_interface_termination_handler(gpointer user_data)
{
+ teco_interface_msg(TECO_MSG_WARNING, "Received termination signal - dumping recovery files");
+ teco_interrupted = TECO_TERMINATED;
+
/*
- * Similar to window deletion - emulate "close" key press.
+ * This may be an emergency shutdown or another kind of
+ * termination that we cannot ignore, so better dump
+ * recovery files before terminating.
*/
- GtkWidget *widget = teco_interface.window;
+ if (teco_ring_recovery_interval != 0)
+ teco_ring_dump_recovery();
- g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS);
- close_event->key.window = gtk_widget_get_parent_window(widget);
- close_event->key.keyval = GDK_KEY_Close;
+ /*
+ * Otherwise we terminate and clean up as if by `-EX`.
+ */
+ gtk_main_quit();
- return teco_interface_input_cb(widget, close_event, NULL);
+ return G_SOURCE_REMOVE;
}
diff --git a/src/interface.h b/src/interface.h
index f196a83..ace0e3d 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -51,19 +51,20 @@
extern teco_view_t *teco_interface_current_view;
/** @pure */
-void teco_interface_init(void);
+void teco_interface_init(gint argc, gchar **argv);
/** @pure */
GOptionGroup *teco_interface_get_options(void);
/** @pure makes sense only on Curses */
-void teco_interface_init_color(guint color, guint32 rgb);
+void teco_interface_disable_palette(void);
typedef enum {
- TECO_MSG_USER,
+ TECO_MSG_USER = 0,
TECO_MSG_INFO,
TECO_MSG_WARNING,
- TECO_MSG_ERROR
+ TECO_MSG_ERROR,
+ TECO_MSG_MAX = TECO_MSG_ERROR
} teco_msg_t;
extern teco_msg_t teco_interface_msg_level;
diff --git a/src/main.c b/src/main.c
index 4d46817..29ef024 100644
--- a/src/main.c
+++ b/src/main.c
@@ -54,7 +54,7 @@
/*
* Define this to pause the program at the beginning
- * of main() (Windows only).
+ * of main().
* This is a useful hack on Windows, where gdbserver
* sometimes refuses to start SciTECO but attaches
* to a running process just fine.
@@ -66,12 +66,15 @@
teco_int_t teco_ed = TECO_ED_AUTOEOL;
/**
- * Whether there was an asyncronous interruption (usually after pressing CTRL+C).
+ * Whether there was an asynchronous interruption (usually after
+ * pressing CTRL+C) or termination (SIGTERM).
* However you should always use teco_interface_is_interrupted(),
* to check for interruptions because of its side effects.
* This variable is safe to set to TRUE from signal handlers and threads.
+ *
+ * This is a teco_interrupted_t.
*/
-volatile sig_atomic_t teco_interrupted = FALSE;
+volatile sig_atomic_t teco_interrupted = TECO_NORMAL;
/*
* FIXME: Move this into file-utils.c?
@@ -346,7 +349,7 @@ teco_initialize_environment(void)
static void
teco_sigint_handler(int signal)
{
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
}
#ifdef G_OS_WIN32
@@ -382,12 +385,24 @@ main(int argc, char **argv)
#endif
#ifdef DEBUG_PAUSE
- /* Windows debugging hack (see above) */
- system("pause");
+ getchar();
#endif
+ /*
+ * FIXME: Gracefully handle SIGTERM.
+ * This however would require polling for the flag in Curses
+ * getch() loops.
+ * GTK already catches SIGTERM.
+ */
+#ifdef HAVE_SIGACTION
+ static const struct sigaction sigint = {
+ .sa_handler = teco_sigint_handler,
+ .sa_flags = 0 /* don't auto-restart syscalls */
+ };
+ sigaction(SIGINT, &sigint, NULL);
+#else
signal(SIGINT, teco_sigint_handler);
- signal(SIGTERM, teco_sigint_handler);
+#endif
/*
* Important for Unicode handling in curses and glib.
@@ -445,7 +460,7 @@ main(int argc, char **argv)
*/
teco_qreg_table_init(&teco_qreg_table_globals, TRUE);
- teco_interface_init();
+ teco_interface_init(argc, argv);
/*
* QRegister view must be initialized only now
diff --git a/src/qreg.c b/src/qreg.c
index 4cf92f0..8ef82c0 100644
--- a/src/qreg.c
+++ b/src/qreg.c
@@ -1694,11 +1694,7 @@ teco_machine_qregspec_auto_complete(teco_machine_qregspec_t *ctx, teco_string_t
/* two-letter Q-Reg */
restrict_len = 2;
- /*
- * FIXME: This is not quite right as it will propose even
- * lower case single or two-letter Q-Register names.
- */
- return teco_rb3str_auto_complete(&ctx->result_table->tree, !restrict_len,
+ return teco_rb3str_auto_complete(&ctx->result_table->tree, TRUE,
ctx->name.data, ctx->name.len, restrict_len, insert) &&
ctx->nesting == 1;
}
diff --git a/src/ring.c b/src/ring.c
index 2445718..9feb444 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -143,7 +143,12 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
static inline void
teco_buffer_free(teco_buffer_t *ctx)
{
- if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ /*
+ * During graceful, but unexpected shutdowns (SIGTERM etc.),
+ * we must preserve the recovery files.
+ */
+ if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP &&
+ teco_interrupted != TECO_TERMINATED) {
g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
g_unlink(filename_recovery);
}
@@ -312,12 +317,13 @@ teco_ring_save_all_dirty_buffers(GError **error)
* Recovery creation interval in seconds or 0 if disabled.
* It's not currently enforced in batch mode.
*/
-guint teco_ring_recovery_interval = 5*60;
+guint teco_ring_recovery_interval = 2*60;
/**
* Create recovery files for all dirty buffers.
*
- * Should be called by the interface every teco_ring_recovery_interval seconds.
+ * Should be called by the interface every teco_ring_recovery_interval seconds
+ * or before graceful terminations (SIGTERM etc.).
* This does not generate or expect undo tokens, so it can be called
* even when idlying.
*/
@@ -731,13 +737,18 @@ teco_state_read_file_done(teco_machine_main_t *ctx, teco_string_t str, GError **
return &teco_state_start;
sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos);
g_autofree gchar *filename = teco_file_expand_path(str.data);
/* FIXME: Add wrapper to interface.h? */
if (!teco_view_load(teco_interface_current_view, filename, FALSE, error))
return NULL;
- if (teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0) != pos) {
+ pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_undo_int(teco_ranges[0].to) = teco_interface_bytes2glyphs(pos);
+ teco_undo_guint(teco_ranges_count) = 1;
+
+ if (teco_ranges[0].from != teco_ranges[0].to) {
teco_ring_dirtify();
if (teco_current_doc_must_undo())
diff --git a/src/sciteco.h b/src/sciteco.h
index 16dba69..88078c2 100644
--- a/src/sciteco.h
+++ b/src/sciteco.h
@@ -109,6 +109,12 @@ teco_default_codepage(void)
return teco_ed & TECO_ED_DEFAULT_ANSI ? SC_CHARSET_ANSI : SC_CP_UTF8;
}
+typedef enum {
+ TECO_NORMAL = 0,
+ TECO_INTERRUPTED,
+ TECO_TERMINATED
+} teco_interrupted_t;
+
/* in main.c */
extern volatile sig_atomic_t teco_interrupted;
diff --git a/src/spawn.c b/src/spawn.c
index 61718fd..716bafa 100644
--- a/src/spawn.c
+++ b/src/spawn.c
@@ -869,7 +869,7 @@ teco_spawn_idle_cb(gpointer user_data)
{
if (G_LIKELY(!teco_interface_is_interrupted()))
return G_SOURCE_CONTINUE;
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
/*
* The first CTRL+C will try to gracefully terminate the process.
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
index abb6566..c2a1c16 100644
--- a/src/stdio-commands.c
+++ b/src/stdio-commands.c
@@ -255,17 +255,42 @@ teco_state_print_string_initial(teco_machine_main_t *ctx, GError **error)
static teco_state_t *
teco_state_print_string_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
- teco_interface_msg_literal(TECO_MSG_USER, str.data, str.len);
+ teco_int_t type;
+
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return &teco_state_start;
+
+ if (!teco_expressions_pop_num_calc(&type, TECO_MSG_USER, error))
+ return NULL;
+ if (type < TECO_MSG_USER || type > TECO_MSG_MAX) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Invalid message level specified for <^A>");
+ return NULL;
+ }
+
+ teco_interface_msg_literal(type, str.data, str.len);
return &teco_state_start;
}
/*$ "^A" ":^A" print "print string"
- * ^A<string>^A -- Print string as message
- * @^A/string/
- * :^A<string>^A
+ * [type]^A<string>^A -- Print string as message
+ * [type]@^A/string/
+ * [type]:^A<string>^A
*
- * Print <string> as a message, i.e. in the message line
- * in interactive mode and if possible on the terminal (stdout) as well.
+ * Print <string> as a message with severity <type>, i.e. in the
+ * message line in interactive mode and if possible on the terminal
+ * as well.
+ * The following message levels are supported:
+ * .IP 0: 3
+ * User-level message:
+ * They are written without any prefixes directly to stdout.
+ * This is the default if <type> is omitted.
+ * .IP 1:
+ * Info-level message
+ * .IP 2:
+ * Warnings
+ * .IP 3:
+ * Errors
*
* \fB^A\fP differs from all other commands in the way <string>
* is terminated.
diff --git a/src/string-utils.c b/src/string-utils.c
index e9dd148..d98b6b0 100644
--- a/src/string-utils.c
+++ b/src/string-utils.c
@@ -87,8 +87,10 @@ teco_string_get_coord(const gchar *str, gsize off, guint *pos, guint *line, guin
}
/**
- * Get the length of the prefix common to two strings.
- * Works with UTF-8 and single-byte encodings.
+ * Get the length of the prefix common to two UTF-8 strings.
+ *
+ * The UTF-8 strings must be validated, which should be the case
+ * for help labels and short Q-Register names.
*
* @param a Left string.
* @param b Right string.
@@ -103,8 +105,8 @@ teco_string_diff(teco_string_t a, const gchar *b, gsize b_len)
gsize len = 0;
while (len < a.len && len < b_len &&
- a.data[len] == b[len])
- len++;
+ g_utf8_get_char(a.data+len) == g_utf8_get_char(b+len))
+ len = g_utf8_next_char(b+len) - b;
return len;
}
diff --git a/src/view.c b/src/view.c
index a522d1c..620c80a 100644
--- a/src/view.c
+++ b/src/view.c
@@ -112,9 +112,9 @@ teco_view_setup(teco_view_t *ctx)
teco_view_ssm(ctx, SCI_SETCARETFORE, 0xFFFFFF, 0);
teco_view_ssm(ctx, SCI_SETSELFORE, TRUE, 0x000000);
- teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xC0C0C0);
- teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xC0C0C0);
teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_DEFAULT, 0x000000);
teco_view_ssm(ctx, SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)"Monospace");
teco_view_ssm(ctx, SCI_STYLECLEARALL, 0, 0);
@@ -234,8 +234,8 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel,
*/
guint cp = teco_view_get_codepage(ctx);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_RELEASELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_RELEASELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
teco_view_ssm(ctx, SCI_BEGINUNDOACTION, 0, 0);
if (clear) {
@@ -314,8 +314,8 @@ cleanup:
teco_view_ssm(ctx, SCI_ENDUNDOACTION, 0, 0);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_ALLOCATELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_ALLOCATELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
return ret;
}
diff --git a/tests/atlocal.in b/tests/atlocal.in
index b2ceda1..81dc1fb 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -40,4 +40,4 @@ esac
# Default stack size on Linux (8M).
# Some platforms allow very large stack sizes, making it hard to test
# against potential stack overflows.
-ulimit -s 8192
+ulimit -s 8192 || true
diff --git a/tests/testsuite.at b/tests/testsuite.at
index f34eee3..74fab24 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -221,6 +221,13 @@ AT_DATA([expout], [[TEST
Line 2
]])
TE_CHECK([[@^A/TEST^JLine 2^J/]], 0, expout, ignore)
+# "Info" messages would be surpressed by --quiet.
+AT_CHECK([[$SCITECO -e '1@^A/TEST/']], 0, stdout, ignore)
+AT_FAIL_IF([! $GREP "^Info:" stdout])
+TE_CHECK([[2@^A/TEST/]], 0, ignore, stderr)
+AT_FAIL_IF([! $GREP "^Warning:" stderr])
+TE_CHECK([[3@^A/TEST/]], 0, ignore, stderr)
+AT_FAIL_IF([! $GREP "^Error:" stderr])
AT_CLEANUP
AT_SETUP([Type out buffer contents])
@@ -296,6 +303,13 @@ TE_CHECK([[@I/ABCDEF/J @FS/ABC/1234/ ^S+4"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@^Ua/XYZ/ Ga ^S+3"N(0/0)']], 0, ignore, ignore)
# NOTE: EN currently inserts another trailing linefeed.
TE_CHECK([[@EN/*/XYZ/ ^S+4"N(0/0)']], 0, ignore, ignore)
+AT_DATA([test.txt], [[0123456789
+]])
+TE_CHECK([[@ER"test.txt" ^S+11"N(0/0)']], 0, ignore, ignore)
+AT_CLEANUP
+
+AT_SETUP([Macro calls])
+TE_CHECK([[@^Ua{-$$} Ma+1"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP
AT_SETUP([Editing local registers in macro calls])
@@ -439,6 +453,11 @@ TE_CHECK_CMDLINE([[@^Um{^C^C} Mm]], 0, ignore, stderr)
AT_FAIL_IF([! $GREP "^Error:" stderr])
AT_CLEANUP
+AT_SETUP([Replace non-Unicode command-line])
+TE_CHECK_CMDLINE([[{0EE 255@I//}]], 0, ignore, stderr)
+AT_FAIL_IF([! $GREP "^Error:" stderr])
+AT_CLEANUP
+
AT_BANNER([Standard library])
AT_SETUP([Option parser])
@@ -659,6 +678,12 @@ TE_CHECK([[@^Um{U.a Q.a-100000"<%.aMm'} 0Mm]], 0, ignore, ignore)
AT_XFAIL_IF(true)
AT_CLEANUP
+AT_SETUP([Backtracking in patterns])
+# ^ES should be greedy and posessive
+TE_CHECK([[@I/ /J :@S/^ES^X/"S(0/0)']], 0, ignore, ignore)
+AT_XFAIL_IF(true)
+AT_CLEANUP
+
AT_SETUP([Rub out from empty string argument])
# Should rub out the modifiers as well.
# Will currently fail because it tries to execute `:@{`.
diff --git a/www/build.tes b/www/build.tes
index 848a63c..89ac70a 100755
--- a/www/build.tes
+++ b/www/build.tes
@@ -18,6 +18,7 @@
<html>
<head>
<title>SciTECO - &lt;Website&gt; Q[title]</title>
+ <link rel="canonical" href="https://sciteco.fmsbw.de/Q[file]">
<link rel="icon" type="image/x-icon" href="https://sciteco.fmsbw.de/graphics/sciteco.ico">
<meta name="description" content="Advanced TECO dialect and interactive screen editor based on Scintilla">
<style>
@@ -65,7 +66,7 @@
</html>
}
-EBindex.html HK
+[file] EBindex.html HK
[title]Home M[header]
EClowdown -thtml --html-no-skiphtml --html-no-escapehtml ../NEWS.md
I<hr>
@@ -73,7 +74,7 @@ EBindex.html HK
M[footer]
EW
-EBscreenshots.html HK
+[file]screenshots.html EBN[file] HK
[title]Screenshots M[header]
EClowdown -thtml --html-no-skiphtml --html-no-escapehtml screenshots.md
M[footer]
@@ -87,44 +88,44 @@ EW
* FIXME: Support out-of-tree builds.
* Perhaps pass in the biuld directory.
*!
-EBQ[builddir]/doc/sciteco.1.html
+[file]sciteco.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]sciteco(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWsciteco.1.html
+EWQ[file]
-EBQ[builddir]/doc/sciteco.7.html
+[file]sciteco.7.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]sciteco(7) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWsciteco.7.html
+EWQ[file]
!*
* These grohtml-generated documents are not in the header bar,
* but still postprocessed for consinstency.
*!
-EBQ[builddir]/doc/grosciteco.tes.1.html
+[file]grosciteco.tes.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]grosciteco.tes(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWgrosciteco.tes.1.html
+EWQ[file]
-EBQ[builddir]/doc/tedoc.tes.1.html
+[file]tedoc.tes.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]tedoc.tes(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWtedoc.tes.1.html
+EWQ[file]
-EBQ[builddir]/doc/tutorial.html
+[file]tutorial.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]Tutorial M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWtutorial.html
+EWQ[file]
EX
diff --git a/www/screenshots.md b/www/screenshots.md
index df8925a..471374e 100644
--- a/www/screenshots.md
+++ b/www/screenshots.md
@@ -1,5 +1,9 @@
# Screenshots
+## v2.5.1
+
+![Windows 10, new command line](https://sciteco.fmsbw.de/screenshots/v2.5.1-win10-pdcurses.png "Windows 10, new command line")
+
## v2.4.0
![FreeBSD/ncurses, VT240 emulation](https://sciteco.fmsbw.de/screenshots/v2.4.0-freebsd-vt240.png "FreeBSD/ncurses, VT240 emulation")