summaryrefslogtreecommitdiff
path: root/pico/osdep
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pico/osdep
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pico/osdep')
-rw-r--r--pico/osdep/Makefile.am20
-rw-r--r--pico/osdep/Makefile.in549
-rw-r--r--pico/osdep/altedit.c712
-rw-r--r--pico/osdep/altedit.h29
-rw-r--r--pico/osdep/chkpoint.c98
-rw-r--r--pico/osdep/chkpoint.h24
-rw-r--r--pico/osdep/color.c1797
-rw-r--r--pico/osdep/color.h38
-rw-r--r--pico/osdep/filesys.c1027
-rw-r--r--pico/osdep/filesys.h43
-rw-r--r--pico/osdep/fsync.c31
-rw-r--r--pico/osdep/fsync.h30
-rw-r--r--pico/osdep/getkey.c565
-rw-r--r--pico/osdep/getkey.h36
-rw-r--r--pico/osdep/makefile.wnt69
-rw-r--r--pico/osdep/mouse.c446
-rw-r--r--pico/osdep/mouse.h33
-rw-r--r--pico/osdep/msdlg.c1050
-rw-r--r--pico/osdep/msmenu.h97
-rw-r--r--pico/osdep/mswin.bmpbin0 -> 2166 bytes
-rw-r--r--pico/osdep/mswin.c12448
-rw-r--r--pico/osdep/mswin.h404
-rw-r--r--pico/osdep/mswin.icobin0 -> 3262 bytes
-rw-r--r--pico/osdep/mswin.rc236
-rw-r--r--pico/osdep/mswin_aspell.c427
-rw-r--r--pico/osdep/mswin_aspell.h30
-rw-r--r--pico/osdep/mswin_spell.DLG18
-rw-r--r--pico/osdep/mswin_spell.c547
-rw-r--r--pico/osdep/mswin_spell.h9
-rw-r--r--pico/osdep/mswin_tw.c765
-rw-r--r--pico/osdep/mswin_tw.h58
-rw-r--r--pico/osdep/mswinhnd.curbin0 -> 326 bytes
-rw-r--r--pico/osdep/newmail.c66
-rw-r--r--pico/osdep/newmail.h25
-rw-r--r--pico/osdep/os-win.h183
-rw-r--r--pico/osdep/os-wnt.h181
-rw-r--r--pico/osdep/pico.icobin0 -> 2238 bytes
-rw-r--r--pico/osdep/popen.c68
-rw-r--r--pico/osdep/popen.h26
-rw-r--r--pico/osdep/raw.c449
-rw-r--r--pico/osdep/raw.h38
-rw-r--r--pico/osdep/read.c223
-rw-r--r--pico/osdep/read.h35
-rw-r--r--pico/osdep/resource.h189
-rw-r--r--pico/osdep/shell.c171
-rw-r--r--pico/osdep/shell.h27
-rw-r--r--pico/osdep/signals.c181
-rw-r--r--pico/osdep/signals.h62
-rw-r--r--pico/osdep/spell.c323
-rw-r--r--pico/osdep/spell.h25
-rw-r--r--pico/osdep/terminal.c1762
-rw-r--r--pico/osdep/terminal.h50
-rw-r--r--pico/osdep/truncate.c20
-rw-r--r--pico/osdep/truncate.h21
-rw-r--r--pico/osdep/tty.c372
-rw-r--r--pico/osdep/tty.h36
56 files changed, 26169 insertions, 0 deletions
diff --git a/pico/osdep/Makefile.am b/pico/osdep/Makefile.am
new file mode 100644
index 00000000..85cffef3
--- /dev/null
+++ b/pico/osdep/Makefile.am
@@ -0,0 +1,20 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libpicoosd.a
+
+libpicoosd_a_SOURCES = altedit.c chkpoint.c color.c filesys.c fsync.c getkey.c mouse.c \
+ newmail.c popen.c raw.c read.c shell.c signals.c spell.c terminal.c truncate.c tty.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/pico/osdep/Makefile.in b/pico/osdep/Makefile.in
new file mode 100644
index 00000000..65619a10
--- /dev/null
+++ b/pico/osdep/Makefile.in
@@ -0,0 +1,549 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = pico/osdep
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpicoosd_a_AR = $(AR) $(ARFLAGS)
+libpicoosd_a_LIBADD =
+am_libpicoosd_a_OBJECTS = altedit.$(OBJEXT) chkpoint.$(OBJEXT) \
+ color.$(OBJEXT) filesys.$(OBJEXT) fsync.$(OBJEXT) \
+ getkey.$(OBJEXT) mouse.$(OBJEXT) newmail.$(OBJEXT) \
+ popen.$(OBJEXT) raw.$(OBJEXT) read.$(OBJEXT) shell.$(OBJEXT) \
+ signals.$(OBJEXT) spell.$(OBJEXT) terminal.$(OBJEXT) \
+ truncate.$(OBJEXT) tty.$(OBJEXT)
+libpicoosd_a_OBJECTS = $(am_libpicoosd_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpicoosd_a_SOURCES)
+DIST_SOURCES = $(libpicoosd_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libpicoosd.a
+libpicoosd_a_SOURCES = altedit.c chkpoint.c color.c filesys.c fsync.c getkey.c mouse.c \
+ newmail.c popen.c raw.c read.c shell.c signals.c spell.c terminal.c truncate.c tty.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pico/osdep/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pico/osdep/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpicoosd.a: $(libpicoosd_a_OBJECTS) $(libpicoosd_a_DEPENDENCIES)
+ -rm -f libpicoosd.a
+ $(libpicoosd_a_AR) libpicoosd.a $(libpicoosd_a_OBJECTS) $(libpicoosd_a_LIBADD)
+ $(RANLIB) libpicoosd.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/altedit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chkpoint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getkey.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mouse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmail.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/popen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signals.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spell.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/truncate.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pico/osdep/altedit.c b/pico/osdep/altedit.c
new file mode 100644
index 00000000..934ed016
--- /dev/null
+++ b/pico/osdep/altedit.c
@@ -0,0 +1,712 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: altedit.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../../pith/osdep/pipe.h"
+#include "../../pith/osdep/forkwait.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+#include "../utf8stub.h"
+#include "tty.h"
+#include "filesys.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "altedit.h"
+#ifndef _WINDOWS
+#include "signals.h"
+
+#ifdef SIGCHLD
+static jmp_buf pico_child_state;
+static short pico_child_jmp_ok, pico_child_done;
+#endif /* SIGCHLD */
+
+#endif /* !_WINDOWS */
+
+
+/* internal prototypes */
+#ifndef _WINDOWS
+#ifdef SIGCHLD
+RETSIGTYPE child_handler(int);
+#endif /* SIGCHLD */
+#else /* _WINDOWS */
+int alt_editor_valid(char *, char *, size_t);
+int alt_editor_valid_fp(char *);
+#endif /* _WINDOWS */
+
+
+/*
+ * alt_editor - fork off an alternate editor for mail message composition
+ * if one is configured and passed from pine. If not, only
+ * ask for the editor if advanced user flag is set, and
+ * suggest environment's EDITOR value as default.
+ */
+int
+alt_editor(int f, int n)
+{
+#ifndef _WINDOWS
+ char eb[NLINE]; /* buf holding edit command */
+ char *fn; /* tmp holder for file name */
+ char result[128]; /* result string */
+ char prmpt[128];
+ int i, done = 0, ret = 0, rv, trv;
+ pid_t child, pid;
+ RETSIGTYPE (*ohup)(int);
+ RETSIGTYPE (*oint)(int);
+ RETSIGTYPE (*osize)(int);
+ int status;
+ EML eml;
+
+ eml.s = f ? "speller" : "editor";
+
+ if(gmode&MDSCUR){
+ emlwrite("Alternate %s not available in restricted mode", &eml);
+ return(-1);
+ }
+
+ strncpy(result, "Alternate %s complete.", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+
+ if(f){
+ if(alt_speller){
+ strncpy(eb, alt_speller, sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ return(-1);
+ }
+ else if(Pmaster == NULL){
+ return(-1);
+ }
+ else{
+ eb[0] = '\0';
+
+ if(Pmaster->alt_ed){
+ char **lp, *wsp, *path, fname[MAXPATH+1];
+ int c;
+
+ for(lp = Pmaster->alt_ed; *lp && **lp; lp++){
+ if((wsp = strpbrk(*lp, " \t")) != NULL){
+ c = *wsp;
+ *wsp = '\0';
+ }
+
+ if(strchr(*lp, '/')){
+ rv = fexist(*lp, "x", (off_t *)NULL);
+ }
+ else{
+ if(!(path = getenv("PATH")))
+ path = ":/bin:/usr/bin";
+
+ rv = ~FIOSUC;
+ while(rv != FIOSUC && *path && pathcat(fname, &path, *lp))
+ rv = fexist(fname, "x", (off_t *)NULL);
+ }
+
+ if(wsp)
+ *wsp = c;
+
+ if(rv == FIOSUC){
+ strncpy(eb, *lp, sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ break;
+ }
+ }
+ }
+
+ if(!eb[0]){
+ if(!(gmode&MDADVN)){
+ unknown_command(0);
+ return(-1);
+ }
+
+ if(getenv("EDITOR")){
+ strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ *eb = '\0';
+
+ while(!done){
+ pid = mlreplyd_utf8("Which alternate editor ? ", eb,
+ sizeof(eb), QDEFLT, NULL);
+ switch(pid){
+ case ABORT:
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ case HELPCH:
+ emlwrite("no alternate editor help yet", NULL);
+
+/* take sleep and break out after there's help */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ sgarbf = TRUE;
+ update();
+ break;
+ case TRUE:
+ case FALSE: /* does editor exist ? */
+ if(*eb == '\0'){ /* leave silently? */
+ mlerase();
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ }
+
+ done++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
+ emlwrite("Problem writing temp file for alt editor", NULL);
+ return(-1);
+ }
+
+ strncat(eb, " ", sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ strncat(eb, fn, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+
+
+ for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){
+ movecursor(i, 0);
+ if(!i){
+ fputs("Invoking alternate ", stdout);
+ fputs(f ? "speller..." : "editor...", stdout);
+ }
+
+ peeol();
+ }
+
+ (*term.t_flush)();
+ if(Pmaster)
+ (*Pmaster->tty_fix)(0);
+ else
+ vttidy();
+
+#ifdef SIGCHLD
+ if(Pmaster){
+ /*
+ * The idea here is to keep any mail connections our caller
+ * may have open while our child's out running around...
+ */
+ pico_child_done = pico_child_jmp_ok = 0;
+ (void) signal(SIGCHLD, child_handler);
+ }
+#endif
+
+ if((child = fork()) > 0){ /* wait for the child to finish */
+ ohup = signal(SIGHUP, SIG_IGN); /* ignore signals for now */
+ oint = signal(SIGINT, SIG_IGN);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ osize = signal(SIGWINCH, SIG_IGN);
+#endif
+
+#ifdef SIGCHLD
+ if(Pmaster){
+ while(!pico_child_done){
+ (*Pmaster->newmail)(0, 0);
+ if(!pico_child_done){
+ if(setjmp(pico_child_state) == 0){
+ pico_child_jmp_ok = 1;
+ sleep(600);
+ }
+ else
+ our_sigunblock(SIGCHLD);
+ }
+
+ pico_child_jmp_ok = 0;
+ }
+ }
+#endif
+
+ trv = process_reap(child, &status, PR_NONE);
+
+ signal(SIGHUP, ohup); /* restore signals */
+ signal(SIGINT, oint);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, osize);
+#endif
+
+ /*
+ * Report child's unnatural or unhappy exit...
+ */
+ if(trv > 0 && status == 0){
+ strncpy(result, "Alternate %s done", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+ }
+ else {
+ snprintf(result, sizeof(result), "Alternate %%s terminated abnormally (%d)",
+ (trv == 0) ? status : -1);
+ if(f)
+ ret = -1;
+ else{
+ snprintf(prmpt, sizeof(prmpt), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn);
+ ret = -2;
+ }
+ }
+ }
+ else if(child == 0){ /* spawn editor */
+ signal(SIGHUP, SIG_DFL); /* let editor handle signals */
+ signal(SIGINT, SIG_DFL);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, SIG_DFL);
+#endif
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb), (char *) NULL) < 0)
+ exit(-1);
+ }
+ else { /* error! */
+ snprintf(result, sizeof(result), "\007Can't fork %%s: %s", errstr(errno));
+ ret = -1;
+ }
+
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+
+ if(Pmaster)
+ (*Pmaster->tty_fix)(1);
+
+ /*
+ * replace edited text with new text
+ */
+ curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
+
+ if(ret == -2){
+ off_t filesize;
+ char prompt[128];
+
+ rv = fexist(fn, "r", &filesize);
+ if(rv == FIOSUC && filesize > 0){
+ snprintf(prompt, sizeof(prompt), prmpt, (long) filesize);
+ /* clear bottom 3 rows */
+ pclear(term.t_nrow-2, term.t_nrow);
+ i = mlyesno_utf8(prompt, FALSE);
+ if(i == TRUE){
+ ret = 0;
+ strncpy(result, "OK, alternate %s done", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+ }
+ else
+ ret = -1;
+ }
+ else
+ ret = -1;
+ }
+
+ if(ret == 0)
+ readin(fn, 0, 0); /* read new text overwriting old */
+
+ our_unlink(fn); /* blast temp file */
+ curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */
+ ttopen(); /* reset the signals */
+ pico_refresh(0, 1); /* redraw */
+ update();
+ emlwrite(result, &eml);
+ return(ret);
+#else /* _WINDOWS */
+ char eb[2 * PATH_MAX]; /* buf holding edit command */
+ char *fn; /* tmp holder for file name */
+ char errbuf[128];
+ char **lp;
+ int status;
+ int done;
+ int rc;
+
+
+ if(Pmaster == NULL)
+ return(-1);
+
+ if(gmode&MDSCUR){
+ emlwrite("Alternate editor not available in restricted mode", NULL);
+ return(-1);
+ }
+
+ eb[0] = '\0';
+
+ if(Pmaster->alt_ed){
+ char *p;
+
+ for(lp = Pmaster->alt_ed; *lp && **lp; lp++)
+ if(*lp[0] == '\"'){
+ if(p = strchr(*lp + 1, '\"')){
+ *p = '\0';
+ rc = alt_editor_valid((*lp) + 1, eb, sizeof(eb));
+ *p = '\"';
+ if(rc){
+ strncat(eb, p + 1, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ break;
+ }
+ }
+ }
+ else if(p = strchr(*lp, ' ')){
+ for(rc = 0; !rc; ){
+ if(p)
+ *p = '\0';
+
+ rc = alt_editor_valid(*lp, eb, sizeof(eb));
+
+ if(p)
+ *p = ' ';
+
+ if(rc){
+ if(p){
+ strncat(eb, p, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ }
+
+ break;
+ }
+ else if(p)
+ p = strchr(p + 1, ' ');
+ else
+ break;
+ }
+
+ if(rc)
+ break;
+ }
+ else if(alt_editor_valid(*lp, eb, sizeof(eb)))
+ break;
+ }
+
+ if(!eb[0]){
+ if(!(gmode&MDADVN)){
+ unknown_command(0);
+ return(-1);
+ }
+
+ /* Guess which editor they want. */
+ if(getenv("EDITOR")){
+ strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ *eb = '\0';
+
+ done = FALSE;
+ while(!done){
+ rc = mlreplyd_utf8("Which alternate editor ? ", eb,
+ sizeof(eb),QDEFLT,NULL);
+
+ switch(rc){
+ case ABORT:
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ case HELPCH:
+ emlwrite("no alternate editor help yet", NULL);
+
+/* take sleep and break out after there's help */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ sgarbf = TRUE;
+ update();
+ break;
+ case TRUE:
+ case FALSE: /* does editor exist ? */
+ if(*eb == '\0'){ /* leave silently? */
+ mlerase();
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ }
+
+ done++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
+ emlwrite("Problem writing temp file for alt editor", NULL);
+ return(-1);
+ }
+
+ emlwrite("Waiting for alternate editor to finish...", NULL);
+
+#ifdef ALTED_DOT
+ if(eb[0] == '.' && eb[1] == '\0') {
+ status = mswin_exec_and_wait ("alternate editor", eb, fn, fn,
+ NULL, 0);
+ }
+ else
+#endif /* ALTED_DOT */
+ { /* just here in case ALTED_DOT is defined */
+ strncat(eb, " ", sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ strncat(eb, fn, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+
+ status = mswin_exec_and_wait ("alternate editor", eb, NULL, NULL,
+ NULL, 0);
+ }
+
+ switch (status) {
+
+ case 0:
+ /*
+ * Success: replace edited text with new text
+ */
+ curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
+ readin(fn, 0, 0); /* read new text overwriting old */
+ our_unlink(fn); /* blast temp file */
+ curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */
+ ttopen(); /* reset the signals */
+ pico_refresh(0, 1); /* redraw */
+ return(0);
+
+
+ /*
+ * Possible errors.
+ */
+ case -1:
+ /* Failed to map return from WinExec to a HTASK. */
+ emlwrite("Problem finding alternate editor task handle.", NULL);
+ return (-1);
+
+ case -2:
+ /* User decided to abandon the alternate editor.*/
+ emlwrite("Alternate editor abandoned.", NULL);
+ return (-1);
+
+ default:
+ mswin_exec_err_msg ("alternate editor", status, errbuf, sizeof(errbuf));
+ emlwrite (errbuf, NULL);
+ return (-1);
+ }
+ return (-1);
+#endif /*_WINDOWS */
+}
+
+
+
+#ifndef _WINDOWS
+int
+pathcat(char *buf, char **path, char *file)
+{
+ register int n = 0;
+
+ while(**path && **path != ':'){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ *buf++ = *(*path)++;
+ }
+
+ if(n){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ *buf++ = '/';
+ }
+
+ while((*buf = *file++) != '\0'){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ buf++;
+ }
+
+ if(**path == ':')
+ (*path)++;
+
+ return(TRUE);
+}
+
+
+#ifdef SIGCHLD
+/*
+ * child_handler - handle child status change signal
+ */
+RETSIGTYPE
+child_handler(int sig)
+{
+ pico_child_done = 1;
+ if(pico_child_jmp_ok){
+#ifdef sco
+ /*
+ * Villy Kruse <vek@twincom.nl> says:
+ * This probably only affects older unix systems with a "broken" sleep
+ * function such as AT&T System V rel 3.2 and systems derived from
+ * that version. The problem is that the sleep function on these
+ * versions of unix will set up a signal handler for SIGALRM which
+ * will longjmp into the sleep function when the alarm goes off.
+ * This gives problems if another signal handler longjmps out of the
+ * sleep function, thus making the stack frame for the signal function
+ * invalid, and when the ALARM handler later longjmps back into the
+ * sleep function it does no longer have a valid stack frame.
+ * My sugested fix is to cancel the pending alarm in the SIGCHLD
+ * handler before longjmp'ing. This shouldn't hurt as there
+ * shouldn't be any ALARM pending at this point except possibly from
+ * the sleep call.
+ *
+ *
+ * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
+ *
+ *
+ * The sleep call might have set up a signal handler which would
+ * longjmp back into the sleep code, and that would cause a crash.
+ */
+ signal(SIGALRM, SIG_IGN); /* Cancel signal handeler */
+ alarm(0); /* might longjmp back into sleep */
+#endif
+ longjmp(pico_child_state, 1);
+ }
+}
+#endif /* SIGCHLD */
+
+#else /* _WINDOWS */
+/*
+ * alt_editor_valid -- test the given exe name exists, and if so
+ * return full name in "cmdbuf".
+ */
+int
+alt_editor_valid(char *exe, char *cmdbuf, size_t ncmdbuf)
+{
+
+/****
+ **** This isn't the right way to do this. I believe we
+ **** should be doing all of this in TCHARs starting in
+ **** the alt_editor function instead of trying to convert
+ **** to char * here.
+ ****/
+
+ if(!exe)
+ return(FALSE);
+
+#ifdef ALTED_DOT
+ if(exe[0] == '.' && exe[1] == '\0') {
+ cmdbuf[0] = '.';
+ cmdbuf[1] = '\0';
+ return(TRUE);
+ }
+ else
+#endif /* ALTED_DOT */
+
+ if((isalpha((unsigned char) *exe) && *exe == ':') || strchr(exe, '\\')){
+ char *path;
+
+ if(alt_editor_valid_fp(path = exe)){
+ if(strchr(path, ' '))
+ snprintf(cmdbuf, ncmdbuf, "\"%s\"", path);
+ else{
+ strncpy(cmdbuf, path, ncmdbuf);
+ cmdbuf[ncmdbuf-1] = '\0';
+ }
+
+ return(TRUE);
+ }
+ }
+ else{
+ TCHAR pathbuflpt[PATH_MAX+1];
+ LPTSTR name, exelpt;
+ LPTSTR cmdbuflpt;
+ char *utf8;
+
+ cmdbuflpt = (LPTSTR) MemAlloc(ncmdbuf * sizeof(TCHAR));
+ cmdbuflpt[0] = L'0';
+
+ exelpt = utf8_to_lptstr(exe);
+
+ if(SearchPath(NULL, exelpt, NULL, PATH_MAX+1, pathbuflpt, &name)){
+
+ if(_tcschr(pathbuflpt, L' '))
+ _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("\"%s\""), pathbuflpt);
+ else
+ _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("%s"), pathbuflpt);
+
+ if(exelpt)
+ fs_give((void **) &exelpt);
+
+ utf8 = lptstr_to_utf8(cmdbuflpt);
+ if(utf8){
+ strncpy(cmdbuf, utf8, ncmdbuf);
+ cmdbuf[ncmdbuf-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(cmdbuflpt)
+ MemFree((void *) cmdbuflpt);
+
+ return(TRUE);
+ }
+
+ if(exelpt)
+ fs_give((void **) &exelpt);
+
+ if(cmdbuflpt)
+ MemFree((void *) cmdbuflpt);
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * alt_editor_valid_fp -- test the given full path name exists
+ */
+int
+alt_editor_valid_fp(path)
+ char *path;
+{
+ static char *exts[] = {".exe", ".com", ".bat", NULL};
+ char pathcopy[PATH_MAX + 1], *dot = NULL;
+ int i, j;
+
+ for(i = 0; pathcopy[i] = path[i]; i++)
+ if(path[i] == '.')
+ dot = &path[i];
+
+ if(dot && (!strucmp(dot, ".exe")
+ || !strucmp(dot, ".com") || !strucmp(dot, ".bat"))){
+ if(fexist(path, "x", (off_t *) NULL) == FIOSUC)
+ return(TRUE);
+ }
+ else{
+ for(j = 0; exts[j]; j++){
+ strncpy(&pathcopy[i], exts[j], sizeof(pathcopy)-i);
+ pathcopy[sizeof(pathcopy)-1] = '\0';
+ if(fexist(pathcopy, "x", (off_t *) NULL) == FIOSUC)
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+#endif
diff --git a/pico/osdep/altedit.h b/pico/osdep/altedit.h
new file mode 100644
index 00000000..7e592f6f
--- /dev/null
+++ b/pico/osdep/altedit.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: altedit.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PICO_OSDEP_ALTEDIT_INCLUDED
+#define PICO_OSDEP_ALTEDIT_INCLUDED
+
+
+
+/* exported prototypes */
+int alt_editor(int, int);
+#ifndef _WINDOWS
+int pathcat(char *, char **, char *);
+#endif
+
+
+#endif /* PICO_OSDEP_ALTEDIT_INCLUDED */
diff --git a/pico/osdep/chkpoint.c b/pico/osdep/chkpoint.c
new file mode 100644
index 00000000..235cf83b
--- /dev/null
+++ b/pico/osdep/chkpoint.c
@@ -0,0 +1,98 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: chkpoint.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+#include "filesys.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "chkpoint.h"
+
+
+/*
+ * chkptinit -- initialize anything we need to support composer
+ * checkpointing
+ */
+void
+chkptinit(char *file, size_t filelen)
+{
+#ifndef _WINDOWS
+ unsigned pid;
+ char *chp;
+#endif
+
+ if(!file[0]){
+ long gmode_save = gmode;
+
+ if(gmode&MDCURDIR)
+ gmode &= ~MDCURDIR; /* so fixpath will use home dir */
+
+#ifndef _WINDOWS
+ strncpy(file, "#picoXXXXX#", filelen);
+#else
+ strncpy(file, "#picoTM0.txt", filelen);
+#endif
+ file[filelen-1] = '\0';
+ fixpath(file, filelen);
+ gmode = gmode_save;
+ }
+ else{
+ size_t l = strlen(file);
+
+ if(l+2 <= filelen && file[l-1] != C_FILESEP){
+ file[l++] = C_FILESEP;
+ file[l] = '\0';
+ }
+
+#ifndef _WINDOWS
+ strncpy(file + l, "#picoXXXXX#", filelen-l);
+#else
+ strncpy(file + l, "#picoTM0.txt", filelen-l);
+#endif
+ file[filelen-1] = '\0';
+ }
+
+#ifndef _WINDOWS
+ pid = (unsigned)getpid();
+ for(chp = file+strlen(file) - 2; *chp == 'X'; chp--){
+ *chp = (pid % 10) + '0';
+ pid /= 10;
+ }
+#else
+ if(fexist(file, "r", (off_t *)NULL) == FIOSUC){ /* does file exist? */
+ char copy[NLINE];
+
+ strncpy(copy, "#picoTM1.txt", sizeof(copy));
+ copy[sizeof(copy)-1] = '\0';
+ fixpath(copy, sizeof(copy));
+ our_rename(file, copy); /* save so we don't overwrite it */
+ }
+#endif /* _WINDOWS */
+
+ our_unlink(file);
+}
+
diff --git a/pico/osdep/chkpoint.h b/pico/osdep/chkpoint.h
new file mode 100644
index 00000000..66ca1baf
--- /dev/null
+++ b/pico/osdep/chkpoint.h
@@ -0,0 +1,24 @@
+/*
+ * $Id: chkpoint.h 776 2007-10-24 23:14:26Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PICO_OSDEP_CHKPOINT_INCLUDED
+#define PICO_OSDEP_CHKPOINT_INCLUDED
+
+
+/* exported prototypes */
+void chkptinit(char *, size_t);
+
+
+#endif /* PICO_OSDEP_CHKPOINT_INCLUDED */
diff --git a/pico/osdep/color.c b/pico/osdep/color.c
new file mode 100644
index 00000000..5a9d3710
--- /dev/null
+++ b/pico/osdep/color.c
@@ -0,0 +1,1797 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "terminal.h"
+#include "color.h"
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/collate.h"
+
+#ifndef _WINDOWS
+
+struct color_name_list {
+ char *name;
+ int namelen;
+ struct color_name_list *next;
+};
+
+struct color_table {
+ struct color_name_list *names;
+ char *rgb;
+ int red, green, blue;
+ int val;
+};
+
+
+/* useful definitions */
+#define ANSI8_COLOR() (color_options & COLOR_ANSI8_OPT)
+#define ANSI16_COLOR() (color_options & COLOR_ANSI16_OPT)
+#define ANSI256_COLOR() (color_options & COLOR_ANSI256_OPT)
+#define ANSI_COLOR() (color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT | COLOR_ANSI256_OPT))
+/* transparent (default) color is possible */
+#define COL_TRANS() ((color_options & COLOR_TRANS_OPT) ? 1 : 0) /* transparent */
+#define END_PSEUDO_REVERSE "EndInverse"
+#define A_UNKNOWN -1
+
+
+/* local globals */
+static unsigned color_options;
+static COLOR_PAIR *the_rev_color, *the_normal_color;
+static COLOR_PAIR *color_blasted_by_attrs;
+static int pinvstate; /* physical state of inverse (standout) attribute */
+static int pboldstate; /* physical state of bold attribute */
+static int pulstate; /* physical state of underline attribute */
+static int rev_color_state;
+
+static int boldstate; /* should-be state of bold attribute */
+static int ulstate; /* should-be state of underline attribute */
+static int invstate; /* should-be state of Inverse, which could be a color
+ change or an actual setinverse */
+static int _color_inited, _using_color;
+static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
+static char *_last_fg_color, *_last_bg_color;
+static int _force_fg_color_change;
+static int _force_bg_color_change;
+
+static struct color_table *color_tbl;
+
+/* external references */
+extern char *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+extern int _bce, _colors;
+
+
+/* internal prototypes */
+void flip_rev_color(int);
+void flip_bold(int);
+void flip_ul(int);
+void reset_attr_state(void);
+void add_to_color_name_list(struct color_table *t, char *name);
+void free_color_name_list(struct color_name_list **nl);
+
+
+#if HAS_TERMINFO
+extern char *tparm(char *, ...);
+#endif
+extern char *tgoto();
+void tinitcolor(void);
+int tfgcolor(int);
+int tbgcolor(int);
+struct color_table *init_color_table(void);
+void free_color_table(struct color_table **);
+int color_to_val(char *);
+
+
+
+/*
+ * Start or end bold mode
+ *
+ * Result: escape sequence to go into or out of reverse color is output
+ *
+ * (This is only called when there is a reverse color.)
+ *
+ * Arg state = ON set bold
+ * OFF set normal
+ */
+void
+flip_rev_color(int state)
+{
+ if((rev_color_state = state) == TRUE)
+ (void)pico_set_colorp(the_rev_color, PSC_NONE);
+ else
+ pico_set_normal_color();
+}
+
+
+/*
+ * Start or end bold mode
+ *
+ * Result: escape sequence to go into or out of bold is output
+ *
+ * Arg state = ON set bold
+ * OFF set normal
+ */
+void
+flip_bold(int state)
+{
+ extern char *_setbold, *_clearallattr;
+
+ if((pboldstate = state) == TRUE){
+ if(_setbold != NULL)
+ putpad(_setbold);
+ }
+ else{
+ if(_clearallattr != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearallattr);
+ pinvstate = state;
+ pulstate = state;
+ rev_color_state = state;
+ }
+ }
+}
+
+
+/*
+ * Start or end inverse mode
+ *
+ * Result: escape sequence to go into or out of inverse is output
+ *
+ * Arg state = ON set inverse
+ * OFF set normal
+ */
+void
+flip_inv(int state)
+{
+ extern char *_setinverse, *_clearinverse;
+
+ if((pinvstate = state) == TRUE){
+ if(_setinverse != NULL)
+ putpad(_setinverse);
+ }
+ else{
+ /*
+ * Unfortunately, some termcap entries configure end standout to
+ * be clear all attributes.
+ */
+ if(_clearinverse != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearinverse);
+ pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
+ pulstate = (pulstate == FALSE) ? pulstate : A_UNKNOWN;
+ rev_color_state = A_UNKNOWN;
+ }
+ }
+}
+
+
+/*
+ * Start or end underline mode
+ *
+ * Result: escape sequence to go into or out of underline is output
+ *
+ * Arg state = ON set underline
+ * OFF set normal
+ */
+void
+flip_ul(int state)
+{
+ extern char *_setunderline, *_clearunderline;
+
+ if((pulstate = state) == TRUE){
+ if(_setunderline != NULL)
+ putpad(_setunderline);
+ }
+ else{
+ /*
+ * Unfortunately, some termcap entries configure end underline to
+ * be clear all attributes.
+ */
+ if(_clearunderline != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearunderline);
+ pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
+ pinvstate = (pinvstate == FALSE) ? pinvstate : A_UNKNOWN;
+ rev_color_state = A_UNKNOWN;
+ }
+ }
+}
+
+
+void
+StartInverse(void)
+{
+ invstate = TRUE;
+ reset_attr_state();
+}
+
+
+void
+EndInverse(void)
+{
+ invstate = FALSE;
+ reset_attr_state();
+}
+
+
+int
+InverseState(void)
+{
+ return(invstate);
+}
+
+
+int
+StartBold(void)
+{
+ extern char *_setbold;
+
+ boldstate = TRUE;
+ reset_attr_state();
+ return(_setbold != NULL);
+}
+
+
+void
+EndBold(void)
+{
+ boldstate = FALSE;
+ reset_attr_state();
+}
+
+
+void
+StartUnderline(void)
+{
+ ulstate = TRUE;
+ reset_attr_state();
+}
+
+
+void
+EndUnderline(void)
+{
+ ulstate = FALSE;
+ reset_attr_state();
+}
+
+
+void
+reset_attr_state(void)
+{
+ /*
+ * If we have to turn some attributes off, do that first since that
+ * may turn off other attributes as a side effect.
+ */
+ if(boldstate == FALSE && pboldstate != boldstate)
+ flip_bold(boldstate);
+
+ if(ulstate == FALSE && pulstate != ulstate)
+ flip_ul(ulstate);
+
+ if(invstate == FALSE){
+ if(pico_get_rev_color()){
+ if(rev_color_state != invstate)
+ flip_rev_color(invstate);
+ }
+ else{
+ if(pinvstate != invstate)
+ flip_inv(invstate);
+ }
+ }
+
+ /*
+ * Now turn everything on that needs turning on.
+ */
+ if(boldstate == TRUE && pboldstate != boldstate)
+ flip_bold(boldstate);
+
+ if(ulstate == TRUE && pulstate != ulstate)
+ flip_ul(ulstate);
+
+ if(invstate == TRUE){
+ if(pico_get_rev_color()){
+ if(rev_color_state != invstate)
+ flip_rev_color(invstate);
+ }
+ else{
+ if(pinvstate != invstate)
+ flip_inv(invstate);
+ }
+ }
+
+ if(color_blasted_by_attrs){
+ (void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE);
+ free_color_pair(&color_blasted_by_attrs);
+ }
+
+}
+
+
+void
+tinitcolor(void)
+{
+ if(_color_inited || panicking())
+ return;
+
+ if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb)
+/**** not sure how to do this yet
+ || _scp
+****/
+ ))){
+ _color_inited = 1;
+ color_tbl = init_color_table();
+
+ if(ANSI_COLOR())
+ putpad("\033[39;49m");
+ else{
+ if(_op)
+ putpad(_op);
+ if(_oc)
+ putpad(_oc);
+ }
+ }
+}
+
+
+#if HAS_TERMCAP
+/*
+ * Treading on thin ice here. Tgoto wasn't designed for this. It takes
+ * arguments column and row. We only use one of them, so we put it in
+ * the row argument. The 0 is just a placeholder.
+ */
+#define tparm(s, c) tgoto(s, 0, c)
+#endif
+
+int
+tfgcolor(int color)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ if(!_color_inited)
+ return(-1);
+
+ if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
+ (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
+ (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
+ (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
+ return(-1);
+
+ if(ANSI_COLOR()){
+ char buf[20];
+
+ if(COL_TRANS() && color == pico_count_in_color_table()-1)
+ snprintf(buf, sizeof(buf), "\033[39m");
+ else if(ANSI256_COLOR())
+ snprintf(buf, sizeof(buf), "\033[38;5;%dm", color);
+ else{
+ if(color < 8)
+ snprintf(buf, sizeof(buf), "\033[3%cm", color + '0');
+ else
+ snprintf(buf, sizeof(buf), "\033[9%cm", (color-8) + '0');
+ }
+
+ putpad(buf);
+ }
+ else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
+ char bg_color_was[MAXCOLORLEN+1];
+
+ bg_color_was[0] = '\0';
+
+ /*
+ * Setting transparent (default) foreground color.
+ * We don't know how to set only the default foreground color,
+ * _op sets both foreground and background. So we need to
+ *
+ * get current background color
+ * set default colors
+ * if (current background was not default) reset it
+ */
+ if(_last_bg_color && strncmp(_last_bg_color, MATCH_TRAN_COLOR, RGBLEN)){
+ strncpy(bg_color_was, _last_bg_color, sizeof(bg_color_was));
+ bg_color_was[sizeof(bg_color_was)-1] = '\0';
+ }
+
+ putpad(_op);
+ if(bg_color_was[0]){
+ _force_bg_color_change = 1;
+ pico_set_bg_color(bg_color_was);
+ }
+ }
+ else if(_setaf)
+ putpad(tparm(_setaf, color));
+ else if(_setf)
+ putpad(tparm(_setf, color));
+ else if(_scp){
+ /* set color pair method */
+ }
+
+ return(0);
+}
+
+
+int
+tbgcolor(int color)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ if(!_color_inited)
+ return(-1);
+
+ if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
+ (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
+ (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
+ (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
+ return(-1);
+
+ if(ANSI_COLOR()){
+ char buf[20];
+
+ if(COL_TRANS() && color == pico_count_in_color_table()-1)
+ snprintf(buf, sizeof(buf), "\033[49m");
+ else if(ANSI256_COLOR())
+ snprintf(buf, sizeof(buf), "\033[48;5;%dm", color);
+ else{
+ if(color < 8)
+ snprintf(buf, sizeof(buf), "\033[4%cm", color + '0');
+ else
+ snprintf(buf, sizeof(buf), "\033[10%cm", (color-8) + '0');
+ }
+
+ putpad(buf);
+ }
+ else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
+ char fg_color_was[MAXCOLORLEN+1];
+
+ fg_color_was[0] = '\0';
+
+ /*
+ * Setting transparent (default) background color.
+ * We don't know how to set only the default background color,
+ * _op sets both foreground and background. So we need to
+ *
+ * get current foreground color
+ * set default colors
+ * if (current foreground was not default) reset it
+ */
+ if(_last_fg_color && strncmp(_last_fg_color, MATCH_TRAN_COLOR, RGBLEN)){
+ strncpy(fg_color_was, _last_fg_color, sizeof(fg_color_was));
+ fg_color_was[sizeof(fg_color_was)-1] = '\0';
+ }
+
+ putpad(_op);
+ if(fg_color_was[0]){
+ _force_fg_color_change = 1;
+ pico_set_fg_color(fg_color_was);
+ }
+ }
+ else if(_setab)
+ putpad(tparm(_setab, color));
+ else if(_setb)
+ putpad(tparm(_setb, color));
+ else if(_scp){
+ /* set color pair method */
+ }
+
+ return(0);
+}
+
+
+
+/*
+ * We're not actually using the RGB value other than as a string which
+ * maps into the color.
+ * In fact, on some systems color 1 and color 4 are swapped, and color 3
+ * and color 6 are swapped. So don't believe the RGB values.
+ * Still, it feels right to have them be the canonical values, so we do that.
+ *
+ * Actually we are using them more now. In color_to_val we map to the closest
+ * color if we don't get an exact match. We ignore values over 255.
+ *
+ * More than one "name" can map to the same "rgb".
+ * More than one "name" can map to the same "val".
+ * The "val" for a "name" and for its "rgb" are the same.
+ */
+struct color_table *
+init_color_table(void)
+{
+ struct color_table *ct = NULL, *t;
+ int i, count;
+ char colorname[12];
+
+ count = pico_count_in_color_table();
+
+ if(count > 0 && count <= 256+COL_TRANS()){
+ int ind, graylevel;
+ struct {
+ char rgb[RGBLEN+1];
+ int red, green, blue;
+ } cube[256];
+
+ ct = (struct color_table *) fs_get((count+1) * sizeof(struct color_table));
+ if(ct)
+ memset(ct, 0, (count+1) * sizeof(struct color_table));
+
+ /*
+ * We boldly assume that 256 colors means xterm 256-color
+ * color cube and grayscale.
+ */
+ if(ANSI_COLOR() && (count == 256+COL_TRANS())){
+ int r, g, b, gray;
+
+ for(r = 0; r < 6; r++)
+ for(g = 0; g < 6; g++)
+ for(b = 0; b < 6; b++){
+ ind = 16 + 36*r + 6*g + b;
+ cube[ind].red = r ? (40*r + 55) : 0;
+ cube[ind].green = g ? (40*g + 55) : 0;
+ cube[ind].blue = b ? (40*b + 55) : 0;
+ snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
+ cube[ind].red, cube[ind].green, cube[ind].blue);
+ }
+
+ for(gray = 0; gray < 24; gray++){
+ ind = gray + 232;
+ graylevel = 10*gray + 8;
+ cube[ind].red = graylevel;
+ cube[ind].green = graylevel;
+ cube[ind].blue = graylevel;
+ snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
+ graylevel, graylevel, graylevel);
+ }
+ }
+
+ for(i = 0, t = ct; t && i < count; i++, t++){
+ t->val = i;
+
+ switch(i){
+ case COL_BLACK:
+ strncpy(colorname, "black", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_RED:
+ strncpy(colorname, "red", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_GREEN:
+ strncpy(colorname, "green", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_YELLOW:
+ strncpy(colorname, "yellow", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_BLUE:
+ strncpy(colorname, "blue", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_MAGENTA:
+ strncpy(colorname, "magenta", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_CYAN:
+ strncpy(colorname, "cyan", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_WHITE:
+ strncpy(colorname, "white", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ default:
+ snprintf(colorname, sizeof(colorname), "color%3.3d", i);
+ break;
+ }
+
+ if(COL_TRANS() && i == count-1){
+ strncpy(colorname, MATCH_TRAN_COLOR, sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ }
+
+ add_to_color_name_list(t, colorname);
+
+ if(count == 8+COL_TRANS()){
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ add_to_color_name_list(t, "color008");
+ add_to_color_name_list(t, "colordgr");
+ add_to_color_name_list(t, "colormgr");
+ break;
+ case COL_RED:
+ t->red = 255;
+ t->green = t->blue = 0;
+ add_to_color_name_list(t, "color009");
+ break;
+ case COL_GREEN:
+ t->green = 255;
+ t->red = t->blue = 0;
+ add_to_color_name_list(t, "color010");
+ break;
+ case COL_YELLOW:
+ t->red = t->green = 255;
+ t->blue = 0;
+ add_to_color_name_list(t, "color011");
+ break;
+ case COL_BLUE:
+ t->red = t->green = 0;
+ t->blue = 255;
+ add_to_color_name_list(t, "color012");
+ break;
+ case COL_MAGENTA:
+ t->red = t->blue = 255;
+ t->green = 0;
+ add_to_color_name_list(t, "color013");
+ break;
+ case COL_CYAN:
+ t->red = 0;
+ t->green = t->blue = 255;
+ add_to_color_name_list(t, "color014");
+ break;
+ case COL_WHITE:
+ t->red = t->green = t->blue = 255;
+ add_to_color_name_list(t, "color015");
+ add_to_color_name_list(t, "colorlgr");
+ break;
+ }
+ }
+ else if(count == 16+COL_TRANS() || count == 256+COL_TRANS()){
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ /*
+ * This set of RGB values seems to come close to describing
+ * what a 16-color xterm gives you.
+ */
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ break;
+ case COL_RED: /* actually dark red */
+ t->red = 174;
+ t->green = t->blue = 0;
+ break;
+ case COL_GREEN: /* actually dark green */
+ t->green = 174;
+ t->red = t->blue = 0;
+ break;
+ case COL_YELLOW: /* actually dark yellow */
+ t->blue = 0;
+ t->red = t->green = 174;
+ break;
+ case COL_BLUE: /* actually dark blue */
+ t->blue = 174;
+ t->red = t->green = 0;
+ break;
+ case COL_MAGENTA: /* actually dark magenta */
+ t->green = 0;
+ t->red = t->blue = 174;
+ break;
+ case COL_CYAN: /* actually dark cyan */
+ t->red = 0;
+ t->green = t->blue = 174;
+ break;
+ case COL_WHITE: /* actually light gray */
+ t->red = t->green = t->blue = 174;
+ if(count == 16)
+ add_to_color_name_list(t, "colorlgr");
+
+ break;
+ case 8: /* dark gray */
+ t->red = t->green = t->blue = 64;
+ if(count == 16){
+ add_to_color_name_list(t, "colordgr");
+ add_to_color_name_list(t, "colormgr");
+ }
+
+ break;
+ case 9: /* red */
+ t->red = 255;
+ t->green = t->blue = 0;
+ break;
+ case 10: /* green */
+ t->green = 255;
+ t->red = t->blue = 0;
+ break;
+ case 11: /* yellow */
+ t->blue = 0;
+ t->red = t->green = 255;
+ break;
+ case 12: /* blue */
+ t->blue = 255;
+ t->red = t->green = 0;
+ break;
+ case 13: /* magenta */
+ t->green = 0;
+ t->red = t->blue = 255;
+ break;
+ case 14: /* cyan */
+ t->red = 0;
+ t->green = t->blue = 255;
+ break;
+ case 15: /* white */
+ t->red = t->green = t->blue = 255;
+ break;
+ default:
+ t->red = cube[i].red;
+ t->green = cube[i].green;
+ t->blue = cube[i].blue;
+ switch(i){
+ case 238:
+ add_to_color_name_list(t, "colordgr");
+ break;
+
+ case 244:
+ add_to_color_name_list(t, "colormgr");
+ break;
+
+ case 250:
+ add_to_color_name_list(t, "colorlgr");
+ break;
+ }
+
+ break;
+ }
+ }
+ else{
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ break;
+ case COL_RED: /* actually dark red */
+ t->red = 255;
+ t->green = t->blue = 0;
+ break;
+ case COL_GREEN: /* actually dark green */
+ t->green = 255;
+ t->red = t->blue = 0;
+ break;
+ case COL_YELLOW: /* actually dark yellow */
+ t->blue = 0;
+ t->red = t->green = 255;
+ break;
+ case COL_BLUE: /* actually dark blue */
+ t->blue = 255;
+ t->red = t->green = 0;
+ break;
+ case COL_MAGENTA: /* actually dark magenta */
+ t->green = 0;
+ t->red = t->blue = 255;
+ break;
+ case COL_CYAN: /* actually dark cyan */
+ t->red = 0;
+ t->green = t->blue = 255;
+ break;
+ case COL_WHITE: /* actually light gray */
+ t->red = t->green = t->blue = 255;
+ break;
+ default:
+ /* this will just be a string match */
+ t->red = t->green = t->blue = 256+i;
+ break;
+ }
+ }
+ }
+
+ for(i = 0, t = ct; t && i < count; i++, t++){
+ t->rgb = (char *)fs_get((RGBLEN+1) * sizeof(char));
+ if(COL_TRANS() && i == count-1)
+ snprintf(t->rgb, RGBLEN+1, MATCH_TRAN_COLOR);
+ else
+ snprintf(t->rgb, RGBLEN+1, "%3.3d,%3.3d,%3.3d", t->red, t->green, t->blue);
+ }
+ }
+
+ return(ct);
+}
+
+
+void
+add_to_color_name_list(struct color_table *t, char *name)
+{
+ if(t && name && *name){
+ struct color_name_list *new_name;
+
+ new_name = (struct color_name_list *) fs_get(sizeof(struct color_name_list));
+ if(new_name){
+ memset(new_name, 0, sizeof(*new_name));
+ new_name->namelen = strlen(name);
+
+ new_name->name = (char *) fs_get((new_name->namelen+1) * sizeof(char));
+ if(new_name->name){
+ strncpy(new_name->name, name, new_name->namelen+1);
+ new_name->name[new_name->namelen] = '\0';
+
+ if(t->names){
+ struct color_name_list *nl;
+ for(nl = t->names; nl->next; nl = nl->next)
+ ;
+
+ nl->next = new_name;
+ }
+ else
+ t->names = new_name;
+ }
+ }
+ }
+}
+
+
+void
+free_color_name_list(struct color_name_list **nl)
+{
+ if(nl && *nl){
+ if((*nl)->next)
+ free_color_name_list(&(*nl)->next);
+
+ if((*nl)->name)
+ fs_give((void **) &(*nl)->name);
+
+ fs_give((void **) nl);
+ }
+}
+
+
+/*
+ * Map from integer color value to canonical color name.
+ */
+char *
+colorx(int color)
+{
+ struct color_table *ct;
+ static char cbuf[12];
+
+ /* before we get set up, we still need to use this a bit */
+ if(!color_tbl){
+ switch(color){
+ case COL_BLACK:
+ return("black");
+ case COL_RED:
+ return("red");
+ case COL_GREEN:
+ return("green");
+ case COL_YELLOW:
+ return("yellow");
+ case COL_BLUE:
+ return("blue");
+ case COL_MAGENTA:
+ return("magenta");
+ case COL_CYAN:
+ return("cyan");
+ case COL_WHITE:
+ return("white");
+ default:
+ snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
+ return(cbuf);
+ }
+ }
+
+ for(ct = color_tbl; ct->names; ct++)
+ if(ct->val == color)
+ break;
+
+ /* rgb _is_ the canonical name */
+ if(ct->names)
+ return(ct->rgb);
+
+ /* not supposed to get here */
+ snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Argument is a color name which could be an RGB string, a name like "blue",
+ * or a name like "color011".
+ *
+ * Returns a pointer to the canonical name of the color.
+ */
+char *
+color_to_canonical_name(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(NULL);
+
+ if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ /* rgb is the canonical name */
+ if(ct->names)
+ return(ct->rgb);
+ else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+
+ return("");
+}
+
+
+/*
+ * Argument is the name of a color or an RGB value that we recognize.
+ * The table should be set up so that the val returned is the same whether
+ * or not we choose the canonical name.
+ *
+ * Returns the integer value for the color.
+ */
+int
+color_to_val(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(-1);
+
+ if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+
+ /*
+ * Didn't match any. Find "closest" to match.
+ */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, s, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
+ struct color_table *closest = NULL;
+ int closest_value = 1000000;
+ int cv;
+
+ for(ct = color_tbl; ct->rgb; ct++){
+
+ if(ct->red >= 0 && ct->red <= 255
+ && ct->green >= 0 && ct->green <= 255
+ && ct->blue >= 0 && ct->blue <= 255){
+ cv = (ct->red - r) * (ct->red - r) +
+ (ct->green - g) * (ct->green - g) +
+ (ct->blue - b) * (ct->blue - b);
+ if(cv < closest_value){
+ closest_value = cv;
+ closest = ct;
+ }
+ }
+ }
+
+ if(closest)
+ ct = closest;
+ }
+ }
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ if(ct->names)
+ return(ct->val);
+ else
+ return(-1);
+}
+
+
+void
+free_color_table(struct color_table **ctbl)
+{
+ struct color_table *t;
+
+ if(ctbl && *ctbl){
+ for(t = *ctbl; t->names; t++){
+ free_color_name_list(&t->names);
+
+ if(t->rgb)
+ fs_give((void **) &t->rgb);
+ }
+
+ fs_give((void **) ctbl);
+ }
+}
+
+
+int
+pico_count_in_color_table(void)
+{
+ return(
+ (ANSI_COLOR()
+ ? (ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : 256)
+ : _colors)
+ + COL_TRANS());
+}
+
+
+void
+pico_nfcolor(char *s)
+{
+ if(_nfcolor)
+ fs_give((void **) &_nfcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _nfcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_nfcolor){
+ strncpy(_nfcolor, s, len+1);
+ _nfcolor[len] = '\0';
+ }
+
+ if(the_normal_color){
+ strncpy(the_normal_color->fg, _nfcolor, MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ if(_nbcolor)
+ fs_give((void **) &_nbcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _nbcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_nbcolor){
+ strncpy(_nbcolor, s, len+1);
+ _nbcolor[len] = '\0';
+ }
+
+ if(the_normal_color){
+ strncpy(the_normal_color->bg, _nbcolor, MAXCOLORLEN+1);
+ the_normal_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+void
+pico_rfcolor(char *s)
+{
+ if(_rfcolor)
+ fs_give((void **) &_rfcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _rfcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_rfcolor){
+ strncpy(_rfcolor, s, len+1);
+ _rfcolor[len] = '\0';
+ }
+
+ if(the_rev_color){
+ strncpy(the_rev_color->fg, _rfcolor, MAXCOLORLEN+1);
+ the_rev_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+void
+pico_rbcolor(char *s)
+{
+ if(_rbcolor)
+ fs_give((void **) &_rbcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _rbcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_rbcolor){
+ strncpy(_rbcolor, s, len+1);
+ _rbcolor[len] = '\0';
+ }
+
+ if(the_rev_color){
+ strncpy(the_rev_color->bg, _rbcolor, MAXCOLORLEN+1);
+ the_rev_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+int
+pico_hascolor(void)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ return(_color_inited);
+}
+
+
+int
+pico_usingcolor(void)
+{
+ return(_using_color && pico_hascolor());
+}
+
+
+/*
+ * This should only be called when we're using the
+ * unix termdef color, as opposed to the ANSI defined
+ * color stuff or the Windows stuff.
+ */
+int
+pico_trans_color(void)
+{
+ return(_bce && _op);
+}
+
+
+void
+pico_toggle_color(int on)
+{
+ if(on){
+ if(pico_hascolor())
+ _using_color = 1;
+ }
+ else{
+ _using_color = 0;
+ if(_color_inited){
+ _color_inited = 0;
+ if(!panicking())
+ free_color_table(&color_tbl);
+
+ if(ANSI_COLOR())
+ putpad("\033[39;49m");
+ else{
+ if(_op)
+ putpad(_op);
+ if(_oc)
+ putpad(_oc);
+ }
+ }
+ }
+}
+
+
+unsigned
+pico_get_color_options(void)
+{
+ return(color_options);
+}
+
+
+int
+pico_trans_is_on(void)
+{
+ return(COL_TRANS());
+}
+
+
+/*
+ * Absolute set of options. Caller has to OR things together and so forth.
+ */
+void
+pico_set_color_options(unsigned flags)
+{
+ color_options = flags;
+}
+
+void
+pico_endcolor(void)
+{
+ pico_toggle_color(0);
+ if(panicking())
+ return;
+
+ if(_nfcolor)
+ fs_give((void **) &_nfcolor);
+
+ if(_nbcolor)
+ fs_give((void **) &_nbcolor);
+
+ if(_rfcolor)
+ fs_give((void **) &_rfcolor);
+
+ if(_rbcolor)
+ fs_give((void **) &_rbcolor);
+
+ if(_last_fg_color)
+ fs_give((void **) &_last_fg_color);
+
+ if(_last_bg_color)
+ fs_give((void **) &_last_bg_color);
+
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+
+void
+pico_set_nfg_color(void)
+{
+ if(_nfcolor)
+ (void)pico_set_fg_color(_nfcolor);
+}
+
+
+void
+pico_set_nbg_color(void)
+{
+ if(_nbcolor)
+ (void)pico_set_bg_color(_nbcolor);
+}
+
+
+void
+pico_set_normal_color(void)
+{
+ if(!_nfcolor || !_nbcolor ||
+ !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
+ (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
+ (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
+ }
+}
+
+
+/*
+ * If inverse is a color, returns a pointer to that color.
+ * If not, returns NULL.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_rev_color(void)
+{
+ if(pico_usingcolor() && _rfcolor && _rbcolor &&
+ pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
+ if(!the_rev_color)
+ the_rev_color = new_color_pair(_rfcolor, _rbcolor);
+
+ return(the_rev_color);
+ }
+ else
+ return(NULL);
+}
+
+
+/*
+ * Returns a pointer to the normal color.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_normal_color(void)
+{
+ if(pico_usingcolor() && _nfcolor && _nbcolor &&
+ pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){
+ if(!the_normal_color)
+ the_normal_color = new_color_pair(_nfcolor, _nbcolor);
+
+ return(the_normal_color);
+ }
+ else
+ return(NULL);
+}
+
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ int uc;
+ COLOR_PAIR *cp = NULL, *rev = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){
+ EndInverse();
+ if(cp)
+ free_color_pair(&cp);
+ }
+ else if(!((uc=pico_usingcolor()) && fg && bg &&
+ pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(uc && flags & PSC_NORM)
+ pico_set_normal_color();
+ else if(flags & PSC_REV){
+ if((rev = pico_get_rev_color()) != NULL){
+ pico_set_fg_color(rev->fg); /* these will succeed */
+ pico_set_bg_color(rev->bg);
+ }
+ else{
+ StartInverse();
+ if(cp){
+ strncpy(cp->fg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
+ cp->fg[MAXCOLORLEN] = '\0';
+ strncpy(cp->bg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
+ cp->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ }
+ }
+
+ return(cp);
+}
+
+
+int
+pico_is_good_color(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE))
+ return(TRUE);
+ else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+ else if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+
+ /* if no match it's still ok if rgb */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, s, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
+ ct = color_tbl; /* to force TRUE */
+ }
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ return(ct->names ? TRUE : FALSE);
+}
+
+
+/*
+ * Return TRUE on success.
+ */
+int
+pico_set_fg_color(char *s)
+{
+ int val;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE)){
+ EndInverse();
+ return(TRUE);
+ }
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nfcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ if((val = color_to_val(s)) >= 0){
+ size_t len;
+ int changed;
+
+ changed = !_last_fg_color || strcmp(_last_fg_color,colorx(val));
+
+ /* already set correctly */
+ if(!_force_fg_color_change && !changed)
+ return(TRUE);
+
+ _force_fg_color_change = 0;
+
+ if(changed){
+ if(_last_fg_color)
+ fs_give((void **) &_last_fg_color);
+
+ len = strlen(colorx(val));
+ if((_last_fg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(_last_fg_color, colorx(val), len+1);
+ _last_fg_color[len] = '\0';
+ }
+ }
+
+ tfgcolor(val);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+int
+pico_set_bg_color(char *s)
+{
+ int val;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE)){
+ EndInverse();
+ return(TRUE);
+ }
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nbcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ if((val = color_to_val(s)) >= 0){
+ size_t len;
+ int changed;
+
+ changed = !_last_bg_color || strcmp(_last_bg_color,colorx(val));
+
+ /* already set correctly */
+ if(!_force_bg_color_change && !changed)
+ return(TRUE);
+
+ _force_bg_color_change = 0;
+
+ if(changed){
+ if(_last_bg_color)
+ fs_give((void **) &_last_bg_color);
+
+ len = strlen(colorx(val));
+ if((_last_bg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(_last_bg_color, colorx(val), len+1);
+ _last_bg_color[len] = '\0';
+ }
+ }
+
+ tbgcolor(val);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string. Can use up
+ * to three returned values at once before the first is overwritten.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ c_to_a_buf[whichbuf][0] = '\0';
+
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, colorName, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+
+ if(ct && ct->names){
+ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
+ c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
+ }
+ else if(*colorName == ' ' || isdigit(*colorName)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, colorName, RGBLEN))
+ break;
+
+ /* if no match it's still ok if rgb */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, colorName, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
+ /* it was already RGB */
+ snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%3.3d,%3.3d,%3.3d", r, g, b);
+ }
+ }
+ else{
+ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
+ c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
+ }
+ }
+
+ if(!c_to_a_buf[whichbuf][0]){
+ int l;
+
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */
+ l = strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ }
+
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+char *
+pico_get_last_fg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_fg_color){
+ size_t len;
+
+ len = strlen(_last_fg_color);
+ if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(ret, _last_fg_color, len+1);
+ ret[len] = '\0';
+ }
+ }
+
+ return(ret);
+}
+
+
+char *
+pico_get_last_bg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_bg_color){
+ size_t len;
+
+ len = strlen(_last_bg_color);
+ if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(ret, _last_bg_color, len+1);
+ ret[len] = '\0';
+ }
+ }
+
+ return(ret);
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color(void)
+{
+ return(new_color_pair(_last_fg_color, _last_bg_color));
+}
+
+#else /* _WINDOWS */
+static short _in_inverse, _in_bold, _in_uline;
+
+int
+pico_trans_is_on(void)
+{
+ return(0);
+}
+
+void
+StartInverse()
+{
+ if(!_in_inverse)
+ mswin_rev(_in_inverse = 1);
+}
+
+void
+EndInverse()
+{
+ if(_in_inverse)
+ mswin_rev(_in_inverse = 0);
+}
+
+int
+InverseState()
+{
+ return(_in_inverse);
+}
+
+void
+StartUnderline()
+{
+ if(!_in_uline)
+ mswin_uline(_in_uline = 1);
+}
+
+void
+EndUnderline()
+{
+ if(_in_uline)
+ mswin_uline(_in_uline = 0);
+}
+
+int
+StartBold()
+{
+ if(!_in_bold)
+ mswin_bold(_in_bold = 1);
+
+ return(1);
+}
+
+void
+EndBold()
+{
+ if(_in_bold)
+ mswin_bold(_in_bold = 0);
+}
+
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/color.h b/pico/osdep/color.h
new file mode 100644
index 00000000..0dced80c
--- /dev/null
+++ b/pico/osdep/color.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: color.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_COLOR_INCLUDED
+#define PICO_OSDEP_COLOR_INCLUDED
+
+
+/* exported prototypes */
+void StartInverse(void);
+void flip_inv(int);
+void EndInverse(void);
+int InverseState(void);
+int StartBold(void);
+void EndBold(void);
+void StartUnderline(void);
+void EndUnderline(void);
+int pico_trans_color(void);
+void pico_endcolor(void);
+void pico_toggle_color(int);
+void pico_set_nfg_color(void);
+void pico_set_nbg_color(void);
+
+
+#endif /* PICO_OSDEP_COLOR_INCLUDED */
diff --git a/pico/osdep/filesys.c b/pico/osdep/filesys.c
new file mode 100644
index 00000000..4471882e
--- /dev/null
+++ b/pico/osdep/filesys.c
@@ -0,0 +1,1027 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#include "../../pith/osdep/temp_nam.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "fsync.h"
+#include "filesys.h"
+
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# define dirent direct
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+
+/*
+ * fexist - returns TRUE if the file exists with mode passed in m,
+ * FALSE otherwise. By side effect returns length of file in l
+ * File is assumed to be a UTF-8 string.
+ */
+int
+fexist(char *file,
+ char *m, /* files mode: r,w,rw,t or x */
+ off_t *l) /* t means use lstat */
+{
+#ifndef _WINDOWS
+ struct stat sbuf;
+ int rv;
+ int (*stat_f)() = (m && *m == 't') ? our_lstat : our_stat;
+
+ if(l)
+ *l = (off_t)0;
+
+ rv = (*stat_f)(file, &sbuf);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOFNF;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if(l)
+ *l = (off_t)sbuf.st_size;
+
+ if((sbuf.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't'){
+ struct stat sbuf2;
+
+ /*
+ * If it is a symbolic link pointing to a directory, treat
+ * it like it is a directory, not a link.
+ */
+ if((sbuf.st_mode&S_IFMT) == S_IFLNK){
+ rv = our_stat(file, &sbuf2);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOSYM;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ }
+
+ return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
+ }
+
+ if(*m == 'r'){ /* read access? */
+ if(*(m+1) == 'w'){ /* and write access? */
+ rv = (can_access(file,READ_ACCESS)==0)
+ ? (can_access(file,WRITE_ACCESS)==0)
+ ? FIOSUC
+ : FIONWT
+ : FIONRD;
+ }
+ else if(!*(m+1)){ /* just read access? */
+ rv = (can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD;
+ }
+ }
+ else if(*m == 'w' && !*(m+1)) /* write access? */
+ rv = (can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT;
+ else if(*m == 'x' && !*(m+1)) /* execute access? */
+ rv = (can_access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX;
+ else
+ rv = FIOERR; /* bad m arg */
+
+ return(rv);
+#else /* _WINDOWS */
+ struct stat sbuf;
+
+ if(l != NULL)
+ *l = (off_t)0;
+
+ if(our_stat(file, &sbuf) < 0){
+ if(errno == ENOENT) /* File not found */
+ return(FIOFNF);
+ else
+ return(FIOERR);
+ }
+
+ if(l != NULL)
+ *l = (off_t)sbuf.st_size;
+
+ if(sbuf.st_mode & S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't') /* no links, just say yes */
+ return(FIOSUC);
+
+ if(m[0] == 'r') /* read access? */
+ return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
+ else if(m[0] == 'w') /* write access? */
+ return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
+ else if(m[0] == 'x') /* execute access? */
+ return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
+ return(FIOERR); /* what? */
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * isdir - returns true if fn is a readable directory, false otherwise
+ * silent on errors (we'll let someone else notice the problem;)).
+ */
+int
+isdir(char *fn, long *l, time_t *d)
+{
+ struct stat sbuf;
+
+ if(l)
+ *l = 0;
+
+ if(our_stat(fn, &sbuf) < 0)
+ return(0);
+
+ if(l)
+ *l = sbuf.st_size;
+
+ if(d)
+ *d = sbuf.st_mtime;
+
+ return((sbuf.st_mode&S_IFMT) == S_IFDIR);
+}
+
+
+/*
+ * gethomedir - returns the users home directory in UTF-8 string
+ * Note: home is malloc'd for life of pico
+ */
+char *
+gethomedir(int *l)
+{
+ static char *home = NULL;
+ static short hlen = 0;
+
+ if(home == NULL){
+ char buf[NLINE];
+
+#ifndef _WINDOWS
+ strncpy(buf, "~", sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ fixpath(buf, sizeof(buf)); /* let fixpath do the work! */
+#else /* _WINDOWS */
+ if(Pmaster && Pmaster->home_dir)
+ snprintf(buf, sizeof(buf), "%s", Pmaster->home_dir);
+ else
+ snprintf(buf, sizeof(buf), "%c:\\", _getdrive() + 'A' - 1);
+#endif /* _WINDOWS */
+ hlen = strlen(buf);
+ if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
+ emlwrite("Problem allocating space for home dir", NULL);
+ return(0);
+ }
+
+ strncpy(home, buf, hlen);
+ home[hlen] = '\0';
+ }
+
+ if(l)
+ *l = hlen;
+
+ return(home);
+}
+
+
+/*
+ * homeless - returns true if given file does not reside in the current
+ * user's home directory tree.
+ */
+int
+homeless(char *f)
+{
+ char *home;
+ int len;
+
+ home = gethomedir(&len);
+ return(strncmp(home, f, len));
+}
+
+
+/*
+ * getfnames - return all file names in the given directory in a single
+ * malloc'd string. n contains the number of names
+ */
+char *
+getfnames(char *dn, char *pat, int *n, char *e, size_t elen)
+{
+ size_t l, avail, alloced, incr = 1024;
+ char *names, *np, *p;
+ struct stat sbuf;
+#if defined(ct)
+ FILE *dirp;
+ char fn[DIRSIZ+1];
+#else
+#ifdef _WINDOWS
+ char buf[NLINE+1];
+ struct _finddata_t dbuf;
+ long findrv;
+#else
+ DIR *dirp; /* opened directory */
+#endif
+#endif
+#if defined(ct) || !defined(_WINDOWS)
+ struct dirent *dp;
+#endif
+
+ *n = 0;
+
+ if(our_stat(dn, &sbuf) < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ if(e)
+ snprintf(e, elen, "\007File not found: \"%s\"", dn);
+
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ if(e)
+ snprintf(e, elen, "\007File name too long: \"%s\"", dn);
+
+ break;
+#endif
+ default: /* Some other error */
+ if(e)
+ snprintf(e, elen, "\007Error getting file info: \"%s\"", dn);
+
+ break;
+ }
+ return(NULL);
+ }
+ else{
+ /*
+ * We'd like to use 512 * st_blocks as an initial estimate but
+ * some systems have a stat struct with no st_blocks in it.
+ */
+ avail = alloced = MAX(sbuf.st_size, incr);
+ if((sbuf.st_mode&S_IFMT) != S_IFDIR){
+ if(e)
+ snprintf(e, elen, "\007Not a directory: \"%s\"", dn);
+
+ return(NULL);
+ }
+ }
+
+ if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc space for file names");
+
+ return(NULL);
+ }
+
+#ifndef _WINDOWS
+ errno = 0;
+ if((dirp=opendir(fname_to_locale(dn))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't open \"%s\": %s", dn, errstr(errno));
+
+ free((char *)names);
+ return(NULL);
+ }
+#endif
+
+ np = names;
+
+#if defined(ct)
+ while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
+ /* skip empty slots with inode of 0 */
+ if(dp.d_ino == 0)
+ continue;
+ (*n)++; /* count the number of active slots */
+ (void)strncpy(fn, dp.d_name, DIRSIZ);
+ fn[14] = '\0';
+ p = fname_to_utf8(fn);
+ while((*np++ = *p++) != '\0')
+ ;
+ }
+#else /* !defined(ct) */
+#ifdef _WINDOWS
+ strncpy(buf, dn, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ snprintf(buf, sizeof(buf), "%s%s%s*%s%s", dn,
+ (dn[strlen(dn)-1] == '\\') ? "" : "\\",
+ (pat && *pat) ? pat : "",
+ (pat && *pat && strchr(pat, '.')) ? "" : ".",
+ (pat && *pat && strchr(pat, '.')) ? "" : "*");
+ if((findrv = _findfirst(buf, &dbuf)) < 0){
+ if(e)
+ sprintf(e, "Can't find first file in \"%s\"", dn);
+
+ free((char *) names);
+ return(NULL);
+ }
+
+ do {
+ p = fname_to_utf8(dbuf.name);
+#else /* UNIX */
+ while((dp = readdir(dirp)) != NULL){
+ p = fname_to_utf8(dp->d_name);
+ if(!pat || !*pat || !strncmp(p, pat, strlen(pat))){
+#endif /* UNIX */
+ (*n)++;
+ l = strlen(p);
+ while(avail < l+1){
+ char *oldnames;
+
+ alloced += incr;
+ avail += incr;
+ oldnames = names;
+ if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
+ == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc enough space for file names");
+
+ return(NULL);
+ }
+
+ np = names + (np-oldnames);
+ }
+
+ avail -= (l+1);
+
+ while((*np++ = *p++) != '\0')
+ ;
+#ifdef _WINDOWS
+ }
+ while(_findnext(findrv, &dbuf) == 0);
+#else /* UNIX */
+ }
+ }
+#endif /* UNIX */
+#endif /* !defined(ct) */
+
+#ifdef _WINDOWS
+ _findclose(findrv);
+#else
+ closedir(dirp); /* shut down */
+#endif
+ return(names);
+}
+
+
+/*
+ * fioperr - given the error number and file name, display error
+ */
+void
+fioperr(int e, char *f)
+{
+ EML eml;
+
+ eml.s = f;
+
+ switch(e){
+ case FIOFNF: /* File not found */
+ emlwrite("\007File \"%s\" not found", &eml);
+ break;
+ case FIOEOF: /* end of file */
+ emlwrite("\007End of file \"%s\" reached", &eml);
+ break;
+ case FIOLNG: /* name too long */
+ emlwrite("\007File name \"%s\" too long", &eml);
+ break;
+ case FIODIR: /* file is a directory */
+ emlwrite("\007File \"%s\" is a directory", &eml);
+ break;
+ case FIONWT:
+ emlwrite("\007Write permission denied: %s", &eml);
+ break;
+ case FIONRD:
+ emlwrite("\007Read permission denied: %s", &eml);
+ break;
+ case FIOPER:
+ emlwrite("\007Permission denied: %s", &eml);
+ break;
+ case FIONEX:
+ emlwrite("\007Execute permission denied: %s", &eml);
+ break;
+ default:
+ emlwrite("\007File I/O error: %s", &eml);
+ }
+}
+
+
+
+/*
+ * pfnexpand - pico's function to expand the given file name if there is
+ * a leading '~'. Converts the homedir from user's locale to UTF-8.
+ * Fn is assumed to already be UTF-8.
+ */
+char *
+pfnexpand(char *fn, size_t fnlen)
+{
+ register char *x, *y, *z;
+ char *home = NULL;
+#ifndef _WINDOWS
+ struct passwd *pw;
+ char name[50];
+
+ if(*fn == '~'){
+ for(x = fn+1, y = name;
+ *x != '/' && *x != '\0' && y-name < sizeof(name)-1;
+ *y++ = *x++)
+ ;
+
+ *y = '\0';
+ if(x == fn + 1){ /* ~/ */
+ if (!(home = (char *) getenv("HOME")))
+ if ((pw = getpwuid(geteuid())) != NULL)
+ home = pw->pw_dir;
+ }
+ else if(*name){ /* ~username/ */
+ if((pw = getpwnam(name)) != NULL)
+ home = pw->pw_dir;
+ }
+
+ if(!home || (strlen(home) + strlen(fn) >= fnlen))
+ return(NULL);
+#else /* _WINDOWS */
+ char name[_MAX_PATH];
+
+ if(*fn == '~' && *(x = fn + 1) == '\\') {
+ if(!(home = (char *) getenv("HOME"))
+ && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
+ snprintf(home = name, sizeof(name), "%s%s",
+ (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));
+#endif /* _WINDOWS */
+ home = fname_to_utf8(home);
+
+ /* make room for expanded path */
+ for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
+ z >= x;
+ *y-- = *z--)
+ ;
+
+ /* and insert the expanded address */
+ for(x = fn, y = home; *y != '\0' && x-fn < fnlen; *x++ = *y++)
+ ;
+ }
+
+ fn[fnlen-1] = '\0';
+
+ return(fn);
+}
+
+
+/*
+ * fixpath - make the given pathname into an absolute path, incoming
+ * name is UTF-8.
+ */
+void
+fixpath(char *name, size_t namelen)
+{
+#ifdef _WINDOWS
+ char file[_MAX_PATH];
+ int dr;
+
+ if(!namelen)
+ return;
+
+ /* return the full path of given file, so drive spec? */
+ if(name[1] == ':' && isalpha((unsigned char) name[0])){
+ if(name[2] != '\\'){ /* including path? */
+ dr = toupper((unsigned char)name[0]) - 'A' + 1;
+ if((void *)_getdcwd(dr, file, _MAX_PATH) != NULL){
+ if(file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ /* add file name */
+ strncat(file, &name[2], sizeof(file)-1-strlen(file));
+ }
+ else
+ return;
+ }
+ else
+ return; /* fully qualified with drive and path! */
+ }
+ else if(name[0] == '\\' && name[1] != '\\') { /* no drive spec! */
+ sprintf(file, "%c:%.*s", _getdrive()+'A'-1, namelen-3, name);
+ }
+ else if(name[0] == '\\' && name[1] == '\\'){ /* Windows network drive */
+ return;
+ }
+ else{
+ if(Pmaster && !(gmode & MDCURDIR)){
+ strncpy(file, ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL), sizeof(file)-1);
+ file[sizeof(file)-1] = '\0';
+ }
+ else if(!_getcwd(file, sizeof(file))) /* no qualification */
+ return;
+
+ if(*name){ /* if name, append it */
+ if(*file && file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ strncat(file, name, sizeof(file)-1-strlen(file));
+ }
+ }
+
+ strncpy(name, file, namelen-1); /* copy back to real buffer */
+ name[namelen-1] = '\0'; /* tie off just in case */
+#else /* UNIX */
+ char *shft;
+
+ /* filenames relative to ~ */
+ if(!((name[0] == '/')
+ || (name[0] == '.'
+ && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
+ if(Pmaster && !(gmode&MDCURDIR)
+ && (*name != '~' && strlen(name)+2 < namelen)){
+
+ if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 < namelen){
+ int off = strlen(opertree);
+
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[off+1] = *shft;
+
+ strncpy(name, opertree, MIN(off,namelen-1));
+ name[MIN(off,namelen-1)] = '/';
+ }
+ else{
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[2] = *shft;
+
+ name[0] = '~';
+ name[1] = '/';
+ }
+ }
+
+ pfnexpand(name, namelen);
+ }
+#endif /* UNIX */
+}
+
+
+/*
+ * compresspath - given a base path and an additional directory, collapse
+ * ".." and "." elements and return absolute path (appending
+ * base if necessary).
+ *
+ * returns 1 if OK,
+ * 0 if there's a problem
+ * new path, by side effect, if things went OK
+ */
+int
+compresspath(char *base, char *path, size_t pathlen)
+{
+ register int i;
+ int depth = 0;
+ char *p;
+ char *stack[32];
+ char pathbuf[NLINE];
+
+#define PUSHD(X) (stack[depth++] = X)
+#define POPD() ((depth > 0) ? stack[--depth] : "")
+
+#ifndef _WINDOWS
+ if(*path == '~'){
+ fixpath(path, pathlen);
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+ else if(*path != C_FILESEP)
+ snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", base, C_FILESEP, path);
+ else{
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+#else /* _WINDOWS */
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ fixpath(pathbuf, sizeof(pathbuf));
+#endif /* _WINDOWS */
+
+ p = &pathbuf[0];
+ for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */
+ if(pathbuf[i] == C_FILESEP){
+ if(p != pathbuf)
+ PUSHD(p); /* push dir entry */
+
+ p = &pathbuf[i+1]; /* advance p */
+ pathbuf[i] = '\0'; /* cap old p off */
+ continue;
+ }
+
+ if(pathbuf[i] == '.'){ /* special cases! */
+ if(pathbuf[i+1] == '.' /* parent */
+ && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
+ if(!strcmp(POPD(), "")) /* bad news! */
+ return(0);
+
+ i += 2;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
+ i++;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ }
+ }
+
+ if(*p != '\0')
+ PUSHD(p); /* get last element */
+
+ path[0] = '\0';
+ for(i = 0; i < depth; i++){
+ strncat(path, S_FILESEP, pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ strncat(path, stack[i], pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ }
+
+ return(1); /* everything's ok */
+}
+
+
+
+/*
+ * tmpname - return a temporary file name in the given buffer, the filename
+ * is in the directory dir unless dir is NULL. The file did not exist at
+ * the time of the temp_nam call, but was created by temp_nam.
+ */
+void
+tmpname(char *dir, char *name)
+{
+ char *t;
+#ifndef _WINDOWS
+ if((t = temp_nam((dir && *dir) ? dir : NULL, "pico.")) != NULL){
+#else /* _WINDOWS */
+ char tmp[_MAX_PATH];
+
+ if(!((dir && *dir) ||
+ (dir = getenv("TMPDIR")) ||
+ (dir = getenv("TMP")) ||
+ (dir = getenv("TEMP"))))
+ if(!(getcwd(dir = tmp, _MAX_PATH)
+ && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
+ dir = "c:\\";
+
+ if((t = temp_nam_ext(dir, "ae", "txt")) != NULL){
+#endif /* _WINDOWS */
+ strncpy(name, t, NFILEN-1);
+ name[NFILEN-1] = '\0';
+ free((void *)t);
+ }
+ else {
+ emlwrite("Unable to construct temp file name", NULL);
+ name[0] = '\0';
+ }
+}
+
+
+/*
+ * Take a file name, and from it
+ * fabricate a buffer name. This routine knows
+ * about the syntax of file names on the target system.
+ * I suppose that this information could be put in
+ * a better place than a line of code.
+ */
+void
+makename(char bname[], char fname[])
+{
+ register char *cp1;
+ register char *cp2;
+
+ cp1 = &fname[0];
+ while (*cp1 != 0)
+ ++cp1;
+
+ while (cp1!=&fname[0] && cp1[-1]!=C_FILESEP)
+ --cp1;
+
+ cp2 = &bname[0];
+ while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
+ *cp2++ = *cp1++;
+
+ *cp2 = 0;
+}
+
+
+/*
+ * copy - copy contents of file 'a' into a file named 'b'. Return error
+ * if either isn't accessible or is a directory
+ */
+int
+copy(char *a, char *b)
+{
+ int in, out, n, rv = 0;
+ char *cb;
+ struct stat tsb, fsb;
+ EML eml;
+ extern int errno;
+
+ if(our_stat(a, &fsb) < 0){ /* get source file info */
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ return(-1);
+ }
+
+ if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */
+ eml.s = a;
+ emlwrite("\007Read permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
+ eml.s = a;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(our_stat(b, &tsb) < 0){ /* get dest file's mode */
+ switch(errno){
+ case ENOENT:
+ break; /* these are OK */
+ default:
+ eml.s = errstr(errno);
+ emlwrite("\007Can't Copy: %s", &eml);
+ return(-1);
+ }
+ }
+ else{
+ if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */
+ eml.s = b;
+ emlwrite("\007Write permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */
+ eml.s = b;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
+ emlwrite("\007Identical files. File not copied", NULL);
+ return(-1);
+ }
+ }
+
+ if((in = our_open(a, O_RDONLY|O_BINARY, 0600)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Copy Failed: %s", &eml);
+ return(-1);
+ }
+
+ if((out=our_creat(b, fsb.st_mode&0xfff)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ close(in);
+ return(-1);
+ }
+
+ if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
+ emlwrite("Can't allocate space for copy buffer!", NULL);
+ close(in);
+ close(out);
+ return(-1);
+ }
+
+ while(1){ /* do the copy */
+ if((n = read(in, cb, NLINE)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Read Copy: %s", &eml);
+ rv = -1;
+ break; /* get out now */
+ }
+
+ if(n == 0) /* done! */
+ break;
+
+ if(write(out, cb, n) != n){
+ eml.s = errstr(errno);
+ emlwrite("Can't Write Copy: %s", &eml);
+ rv = -1;
+ break;
+ }
+ }
+
+ free(cb);
+ close(in);
+ close(out);
+ return(rv);
+}
+
+
+/*
+ * Open a file for writing. Return TRUE if all is well, and FALSE on error
+ * (cannot create).
+ */
+int
+ffwopen(char *fn, int readonly)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ int fd;
+ EML eml;
+#endif
+#ifndef MODE_READONLY
+#define MODE_READONLY (0600)
+#endif
+
+ /*
+ * Call open() by hand since we don't want O_TRUNC -- it'll
+ * screw over-quota users. The strategy is to overwrite the
+ * existing file's data and call ftruncate before close to lop
+ * off bytes
+ */
+
+ g_pico_fio.flags = FIOINFO_WRITE;
+ g_pico_fio.name = fn;
+#ifndef _WINDOWS
+ if((fd = our_open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
+ && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
+ && fseek(g_pico_fio.fp, 0L, 0) == 0)
+ return (FIOSUC);
+
+
+ eml.s = errstr(errno);
+ emlwrite("Cannot open file for writing: %s", &eml);
+ return (FIOERR);
+#else /* _WINDOWS */
+ if ((g_pico_fio.fp = our_fopen(fn, "w")) == NULL) {
+ emlwrite("Cannot open file for writing", NULL);
+ return (FIOERR);
+ }
+
+#ifdef MODE_READONLY
+ if(readonly)
+ our_chmod(fn, MODE_READONLY); /* fix access rights */
+#endif
+
+ return (FIOSUC);
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * Close a file. Should look at the status in all systems.
+ */
+int
+ffclose(void)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ EML eml;
+
+ errno = 0;
+ if((g_pico_fio.flags & FIOINFO_WRITE)
+ && (fflush(g_pico_fio.fp) == EOF
+ || ftruncate(fileno(g_pico_fio.fp),
+ (off_t) ftell(g_pico_fio.fp)) < 0)){
+ eml.s = errstr(errno);
+ emlwrite("\007Error preparing to close file: %s", &eml);
+ sleep(5);
+ }
+
+ if (fclose(g_pico_fio.fp) == EOF) {
+ eml.s = errstr(errno);
+ emlwrite("\007Error closing file: %s", &eml);
+ return(FIOERR);
+ }
+#else /* _WINDOWS */
+ if (fclose(g_pico_fio.fp) != FALSE) {
+ emlwrite("Error closing file", NULL);
+ return(FIOERR);
+ }
+#endif /* _WINDOWS */
+
+ return(FIOSUC);
+}
+
+
+
+#define EXTEND_BLOCK 1024
+
+
+/*
+ * ffelbowroom - make sure the destination's got enough room to receive
+ * what we're about to write...
+ */
+int
+ffelbowroom(void)
+{
+#ifndef _WINDOWS
+ register LINE *lp;
+ register long n;
+ int x;
+ char s[EXTEND_BLOCK], *errstring = NULL;
+ struct stat fsbuf;
+ extern FIOINFO g_pico_fio;
+
+ /* Figure out how much room do we need */
+ /* first, what's total */
+ for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
+ n += (llength(lp) + 1);
+
+ errno = 0; /* make sure previous error are cleared */
+
+ if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
+ n -= fsbuf.st_size;
+
+ if(n > 0L){ /* must be growing file, extend it */
+ memset(s, 'U', EXTEND_BLOCK);
+ if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
+ for( ; n > 0L; n -= EXTEND_BLOCK){
+ x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
+ if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
+ errstring = errstr(errno);
+ break;
+ }
+ }
+
+ if(!errstring
+ && (fflush(g_pico_fio.fp) == EOF
+ || fsync(fileno(g_pico_fio.fp)) < 0))
+ errstring = errstr(errno);
+
+ if(errstring) /* clean up */
+ (void) ftruncate(fileno(g_pico_fio.fp), (off_t) fsbuf.st_size);
+ else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
+ errstring = errstr(errno);
+ }
+ else
+ errstring = errstr(errno);
+ }
+ }
+ else
+ errstring = errstr(errno);
+
+ if(errstring){
+ snprintf(s, sizeof(s), "Error writing to %s: %s", g_pico_fio.name, errstring);
+ emlwrite(s, NULL);
+ (void) fclose(g_pico_fio.fp);
+ return(FALSE);
+ }
+#endif /* UNIX */
+ return(TRUE);
+}
diff --git a/pico/osdep/filesys.h b/pico/osdep/filesys.h
new file mode 100644
index 00000000..eadbdc86
--- /dev/null
+++ b/pico/osdep/filesys.h
@@ -0,0 +1,43 @@
+/*
+ * $Id: filesys.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_FILESYS_INCLUDED
+#define PICO_OSDEP_FILESYS_INCLUDED
+
+
+#include "../../pith/osdep/canaccess.h" /* for *_ACCESS */
+
+
+/* exported prototypes */
+int fexist(char *, char *, off_t *);
+int isdir(char *, long *, time_t *);
+char *gethomedir(int *);
+int homeless(char *);
+char *getfnames(char *, char *, int *, char *, size_t);
+void fioperr(int, char *);
+char *pfnexpand(char *, size_t);
+void fixpath(char *, size_t);
+int compresspath(char *, char *, size_t);
+void tmpname(char *, char *);
+void makename(char *, char *);
+int copy(char *, char *);
+int ffwopen(char *, int);
+int ffclose(void);
+int ffelbowroom(void);
+
+
+#endif /* PICO_OSDEP_FILESYS_INCLUDED */
diff --git a/pico/osdep/fsync.c b/pico/osdep/fsync.c
new file mode 100644
index 00000000..14a15d29
--- /dev/null
+++ b/pico/osdep/fsync.c
@@ -0,0 +1,31 @@
+/*
+ * Program: File sync emulator
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+
+#ifndef HAVE_FSYNC
+/* Emulator for BSD fsync() call
+ * Accepts: file name
+ * Returns: 0 if successful, -1 if failure
+ */
+int
+our_fsync(int fd)
+{
+ sync();
+ return 0;
+}
+#endif /* !HAVE_FSYNC */
+
diff --git a/pico/osdep/fsync.h b/pico/osdep/fsync.h
new file mode 100644
index 00000000..e7df6f93
--- /dev/null
+++ b/pico/osdep/fsync.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: fsync.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_FSYNC_INCLUDED
+#define PICO_OSDEP_FSYNC_INCLUDED
+
+#ifndef HAVE_FSYNC
+
+#define fsync(F) our_fsync(F)
+
+/* exported prototypes */
+int our_fsync(int);
+
+
+#endif /* !HAVE_FSYNC */
+
+#endif /* PICO_OSDEP_FSYNC_INCLUDED */
diff --git a/pico/osdep/getkey.c b/pico/osdep/getkey.c
new file mode 100644
index 00000000..b55dd092
--- /dev/null
+++ b/pico/osdep/getkey.c
@@ -0,0 +1,565 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: getkey.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "tty.h"
+#include "getkey.h"
+#include "read.h"
+#include "mouse.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+static int MapMSKEYtoPK(int c);
+#endif /* _WINDOWS */
+
+
+/* internal declarations */
+static int timeo = 0;
+
+/* these next two are declared in pith/conf.h */
+int
+set_input_timeout(int t)
+{
+ int oldtimeo = timeo;
+
+ timeo = t;
+ return(oldtimeo);
+}
+
+int
+get_input_timeout(void)
+{
+ return(timeo);
+}
+
+
+#ifndef _WINDOWS
+
+
+/* internal prototypes */
+void bail(void);
+int ReadyForKey(int);
+
+
+
+void
+bail(void)
+{
+ sleep(30); /* see if os receives SIGHUP */
+ kill(getpid(), SIGHUP); /* eof or bad error */
+}
+
+
+#if TYPEAH
+/*
+ * typahead - Check to see if any characters are already in the
+ * keyboard buffer
+ */
+int
+typahead(void)
+{
+ int x; /* holds # of pending chars */
+
+ return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
+}
+#endif /* TYPEAH */
+
+
+
+/*
+ * ReadyForKey - return true if there's no timeout or we're told input
+ * is available...
+ */
+int
+ReadyForKey(int timeout)
+{
+ switch(input_ready(timeout)){
+ case READY_TO_READ:
+ return(1);
+ break;
+
+ case NO_OP_COMMAND:
+ case NO_OP_IDLE:
+ case READ_INTR:
+ return(0);
+
+ case BAIL_OUT:
+ case PANIC_NOW:
+ emlwrite("\007Problem reading from keyboard!", NULL);
+ kill(getpid(), SIGHUP); /* Bomb out (saving our work)! */
+ /* no return */
+ }
+
+ /* can't happen */
+ return(0);
+}
+
+
+
+/*
+ * GetKey - Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch, status, cc;
+
+ if(!ReadyForKey(FUDGE-5))
+ return(NODATA);
+
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case 0: /* regular character */
+ break;
+
+ case KEY_DOUBLE_ESC:
+ /*
+ * Special hack to get around comm devices eating control characters.
+ */
+ if(!ReadyForKey(5))
+ return(BADESC); /* user typed ESC ESC, then stopped */
+ else
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(CTRL | status);
+ break;
+
+ case 0: /* regular character */
+ break;
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ return(BADESC);
+ break;
+ }
+
+ ch &= 0x7f;
+ if(isdigit((unsigned char)ch)){
+ int n = 0, i = ch - '0';
+
+ if(i < 0 || i > 2)
+ return(BADESC); /* bogus literal char value */
+
+ while(n++ < 2){
+ if(!ReadyForKey(5)
+ || (!isdigit((unsigned char) (ch =
+ (*term.t_getchar)(NODATA, NULL, bail)))
+ || (n == 1 && i == 2 && ch > '5')
+ || (n == 2 && i == 25 && ch > '5'))){
+ return(BADESC);
+ }
+
+ i = (i * 10) + (ch - '0');
+ }
+
+ ch = i;
+ }
+ else{
+ if(islower((unsigned char)ch)) /* canonicalize if alpha */
+ ch = toupper((unsigned char)ch);
+
+ return((isalpha((unsigned char)ch) || ch == '@'
+ || (ch >= '[' && ch <= '_'))
+ ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
+ }
+
+ break;
+
+#ifdef MOUSE
+ case KEY_XTERM_MOUSE:
+ {
+ /*
+ * Special hack to get mouse events from an xterm.
+ * Get the details, then pass it past the keymenu event
+ * handler, and then to the installed handler if there
+ * is one...
+ */
+ static int down = 0;
+ int x, y, button;
+ unsigned long cmd;
+
+ button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
+
+ x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+ y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+
+ if(button == 0){
+ down = 1;
+ if(checkmouse(&cmd, 1, x, y))
+ return((UCS) cmd);
+ }
+ else if(down && button == 3){
+ down = 0;
+ if(checkmouse(&cmd, 0, x, y))
+ return((UCS) cmd);
+ }
+
+ return(NODATA);
+ }
+
+ break;
+#endif /* MOUSE */
+
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(status);
+
+ case CTRL_KEY_UP :
+ return(CTRL | KEY_UP);
+ case CTRL_KEY_DOWN :
+ return(CTRL | KEY_DOWN);
+ case CTRL_KEY_RIGHT :
+ return(CTRL | KEY_RIGHT);
+ case CTRL_KEY_LEFT :
+ return(CTRL | KEY_LEFT);
+
+ case KEY_SWALLOW_Z:
+ status = BADESC;
+ case KEY_SWAL_UP:
+ case KEY_SWAL_DOWN:
+ case KEY_SWAL_LEFT:
+ case KEY_SWAL_RIGHT:
+ do
+ if(!ReadyForKey(2)){
+ status = BADESC;
+ break;
+ }
+ while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
+
+ return((status == BADESC)
+ ? status
+ : status - (KEY_SWAL_UP - KEY_UP));
+ break;
+
+ case KEY_KERMIT:
+ do{
+ cc = ch;
+ if(!ReadyForKey(2))
+ return(BADESC);
+ else
+ ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
+ }while(cc != '\033' && ch != '\\');
+
+ ch = NODATA;
+ break;
+
+ case BADESC:
+ (*term.t_beep)();
+ return(status);
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ break;
+ }
+
+ if (ch >= 0x00 && ch <= 0x1F) /* C0 control -> C- */
+ ch = CTRL | (ch+'@');
+
+ return(ch);
+}
+
+
+/*
+ * kbseq - looks at an escape sequence coming from the keyboard and
+ * compares it to a trie of known keyboard escape sequences, and
+ * returns the function bound to the escape sequence.
+ *
+ * Args: getcfunc -- Function to get a single character from stdin,
+ * called with the next two arguments to this
+ * function as its arguments.
+ * recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ * c -- Pointer to returned character.
+ *
+ * Returns: BADESC
+ * The escaped function.
+ * 0 if a regular char with char stuffed in location c.
+ */
+UCS
+kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
+ int (*recorder)(int),
+ void (*bail_handler)(void),
+ void *data,
+ UCS *ch)
+{
+ unsigned char c;
+ int first = 1;
+ KBESC_T *current;
+
+ current = kbesc;
+ if(current == NULL) /* bag it */
+ return(BADESC);
+
+ while(1){
+ c = (*getcfunc)(recorder, bail_handler);
+
+ while(current->value != c){
+ if(current->left == NULL){ /* NO MATCH */
+ if(first){
+ unsigned long octets_so_far, remaining_octets;
+ unsigned char *inputp;
+ UCS ucs;
+ unsigned char inputbuf[20];
+
+ /*
+ * Regular character.
+ * Read enough bytes to make up a character and convert it to UCS-4.
+ */
+ memset(inputbuf, 0, sizeof(inputbuf));
+ inputbuf[0] = c;
+ octets_so_far = 1;
+ for(;;){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = mbtow(data, &inputp, &remaining_octets);
+ switch(ucs){
+ case CCONV_BADCHAR:
+ /*
+ * Not really a BADESC but that ought to
+ * be sufficient. We can add another type if
+ * we need to.
+ */
+ return(BADESC);
+
+ case CCONV_NEEDMORE:
+ if(octets_so_far >= sizeof(inputbuf))
+ return(BADESC);
+
+ c = (*getcfunc)(recorder, bail_handler);
+ inputbuf[octets_so_far++] = c;
+ break;
+
+ default:
+ /* got a good UCS-4 character */
+ *ch = ucs;
+ return(0);
+ }
+ }
+
+ /* NOTREACHED */
+ return(0);
+ }
+ else
+ return(BADESC);
+ }
+ current = current->left;
+ }
+
+ if(current->down == NULL) /* match!!!*/
+ return(current->func);
+ else
+ current = current->down;
+
+ first = 0;
+ }
+}
+
+
+#define newnode() (KBESC_T *)malloc(sizeof(KBESC_T))
+
+/*
+ * kpinsert - insert a keystroke escape sequence into the global search
+ * structure.
+ */
+void
+kpinsert(char *kstr, int kval, int termcap_wins)
+{
+ register char *buf;
+ register KBESC_T *temp;
+ register KBESC_T *trail;
+
+ if(kstr == NULL)
+ return;
+
+ /*
+ * Don't allow escape sequences that don't start with ESC unless
+ * termcap_wins. This is to protect against mistakes in termcap files.
+ */
+ if(!termcap_wins && *kstr != '\033')
+ return;
+
+ temp = trail = kbesc;
+ buf = kstr;
+
+ for(;;){
+ if(temp == NULL){
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ if(kbesc == NULL)
+ kbesc = temp;
+ else
+ trail->down = temp;
+ }
+ else{ /* first entry */
+ while((temp != NULL) && (temp->value != *buf)){
+ trail = temp;
+ temp = temp->left;
+ }
+
+ if(temp == NULL){ /* add new val */
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ trail->left = temp;
+ }
+ }
+
+ if(*(++buf) == '\0')
+ break;
+ else{
+ /*
+ * Ignore attempt to overwrite shorter existing escape sequence.
+ * That means that sequences with higher priority should be
+ * set up first, so if we want termcap sequences to override
+ * hardwired sequences, put the kpinsert calls for the
+ * termcap sequences first. (That's what you get if you define
+ * TERMCAP_WINS.)
+ */
+ if(temp->func != 0)
+ return;
+
+ trail = temp;
+ temp = temp->down;
+ }
+ }
+
+ /*
+ * Ignore attempt to overwrite longer sequences we are a prefix
+ * of (down != NULL) and exact same sequence (func != 0).
+ */
+ if(temp != NULL && temp->down == NULL && temp->func == 0)
+ temp->func = kval;
+}
+
+
+
+/*
+ * kbdestroy() - kills the key pad function key search tree
+ * and frees all lines associated with it
+ *
+ * Should be called with arg kbesc, the top of the tree.
+ */
+void
+kbdestroy(KBESC_T *kb)
+{
+ if(kb){
+ kbdestroy(kb->left);
+ kbdestroy(kb->down);
+ free((char *)kb);
+ kb = NULL;
+ }
+}
+
+#else /* _WINDOWS */
+
+/*
+ * Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch = 0;
+ long timein;
+
+
+ ch = NODATA;
+ timein = time(0L);
+
+ /*
+ * Main character processing loop.
+ */
+ while(!mswin_charavail()) {
+
+#ifdef MOUSE
+ /* Check Mouse. If we get a mouse event, convert to char
+ * event and return that. */
+ if (checkmouse (&ch,0,0,0)) {
+ curwp->w_flag |= WFHARD;
+ return (ch);
+ }
+#endif /* MOUSE */
+
+
+ /* Check Timeout. */
+ if(time(0L) >= timein+(FUDGE-10))
+ return(NODATA);
+ }
+
+
+ return (mswin_getc_fast());
+}
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/getkey.h b/pico/osdep/getkey.h
new file mode 100644
index 00000000..98d52aa7
--- /dev/null
+++ b/pico/osdep/getkey.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: getkey.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_GETKEY_INCLUDED
+#define PICO_OSDEP_GETKEY_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+UCS GetKey(void);
+void kpinsert(char *, int, int);
+#if TYPEAH
+int typahead(void);
+#endif /* TYPEAH */
+#ifndef _WINDOWS
+UCS kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
+ int (*recorder)(int), void (*bail_handler)(void), void *, UCS *);
+void kbdestroy(KBESC_T *);
+#endif
+
+#endif /* PICO_OSDEP_GETKEY_INCLUDED */
diff --git a/pico/osdep/makefile.wnt b/pico/osdep/makefile.wnt
new file mode 100644
index 00000000..0eb3d1e7
--- /dev/null
+++ b/pico/osdep/makefile.wnt
@@ -0,0 +1,69 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of libpicoosd.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\..\include -I..\..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS= #-D_UNICODE -DUNICODE
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../../include/system.h ../../include/general.h \
+ altedit.h chkpoint.h color.h filesys.h fsync.h \
+ getkey.h mswin.h mswin_tw.h mswin_aspell.h mswin_spell.h mouse.h \
+ newmail.h popen.h read.h \
+ shell.h signals.h spell.h terminal.h truncate.h \
+ tty.h
+
+OFILES= altedit.obj chkpoint.obj color.obj filesys.obj fsync.obj \
+ getkey.obj msdlg.obj mswin.obj mswin_tw.obj mswin_aspell.obj mswin_spell.obj \
+ mouse.obj newmail.obj popen.obj \
+ read.obj shell.obj signals.obj spell.obj terminal.obj truncate.obj \
+ tty.obj
+
+all: libpicoosd.lib mswin.res
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpicoosd.lib: $(OFILES)
+ $(RM) libpicoosd.lib || rem
+ $(LIBER) /out:libpicoosd.lib $(OFILES)
+
+mswin.res: mswin.rc pico.ico mswinhnd.cur resource.h
+ $(RC) $(RCFLAGS) /fo mswin.res mswin.rc
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
+ $(RM) mswin.res
diff --git a/pico/osdep/mouse.c b/pico/osdep/mouse.c
new file mode 100644
index 00000000..b671f8c1
--- /dev/null
+++ b/pico/osdep/mouse.c
@@ -0,0 +1,446 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mouse.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "getkey.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "mouse.h"
+
+#ifdef MOUSE
+
+
+#ifndef _WINDOWS
+
+/* useful definitions */
+#define XTERM_MOUSE_ON "\033[?1000h" /* DECSET with parm 1000 */
+#define XTERM_MOUSE_OFF "\033[?1000l" /* DECRST with parm 1000 */
+
+
+/* useful declarations */
+static int mexist = 0; /* is the mouse driver installed? */
+static unsigned mnoop;
+
+
+
+/* internal prototypes */
+void mouseon(void);
+void mouseoff(void);
+
+
+
+/*
+ * init_mouse - check for xterm and initialize mouse tracking if present...
+ */
+int
+init_mouse(void)
+{
+ if(mexist)
+ return(TRUE);
+
+ if(getenv("DISPLAY")){
+ mouseon();
+ kpinsert("\033[M", KEY_XTERM_MOUSE, 1);
+ return(mexist = TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+/*
+ * end_mouse - clear xterm mouse tracking if present...
+ */
+void
+end_mouse(void)
+{
+ if(mexist){
+ mexist = 0; /* just see if it exists here. */
+ mouseoff();
+ }
+}
+
+
+/*
+ * mouseexist - function to let outsiders know if mouse is turned on
+ * or not.
+ */
+int
+mouseexist(void)
+{
+ return(mexist);
+}
+
+
+/*
+ * mouseon - call made available for programs calling pico to turn ON the
+ * mouse cursor.
+ */
+void
+mouseon(void)
+{
+ fputs(XTERM_MOUSE_ON, stdout);
+}
+
+
+/*
+ * mouseon - call made available for programs calling pico to turn OFF the
+ * mouse cursor.
+ */
+void
+mouseoff(void)
+{
+ fputs(XTERM_MOUSE_OFF, stdout);
+}
+
+
+/*
+ * checkmouse - look for mouse events in key menu and return
+ * appropriate value.
+ */
+int
+checkmouse(unsigned long *ch, int down, int mcol, int mrow)
+{
+ static int oindex;
+ int i = 0, rv = 0;
+ MENUITEM *mp;
+
+ if(!mexist || mcol < 0 || mrow < 0)
+ return(FALSE);
+
+ if(down) /* button down */
+ oindex = -1;
+
+ for(mp = mfunc; mp; mp = mp->next)
+ if(mp->action && M_ACTIVE(mrow, mcol, mp))
+ break;
+
+ if(mp){
+ unsigned long r;
+
+ r = (*mp->action)(down ? M_EVENT_DOWN : M_EVENT_UP,
+ mrow, mcol, M_BUTTON_LEFT, 0);
+ if(r){
+ *ch = r;
+ rv = TRUE;
+ }
+ }
+ else{
+ while(1){ /* see if we understand event */
+ if(i >= 12){
+ i = -1;
+ break;
+ }
+
+ if(M_ACTIVE(mrow, mcol, &menuitems[i]))
+ break;
+
+ i++;
+ }
+
+ if(down){ /* button down */
+ oindex = i; /* remember where */
+ if(i != -1
+ && menuitems[i].label_hiliter != NULL
+ && menuitems[i].val != mnoop) /* invert label */
+ (*menuitems[i].label_hiliter)(1, &menuitems[i]);
+ }
+ else{ /* button up */
+ if(oindex != -1){
+ if(i == oindex){
+ *ch = menuitems[i].val;
+ rv = TRUE;
+ }
+ }
+ }
+ }
+
+ /* restore label */
+ if(!down
+ && oindex != -1
+ && menuitems[oindex].label_hiliter != NULL
+ && menuitems[oindex].val != mnoop)
+ (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
+
+ return(rv);
+}
+
+
+/*
+ * invert_label - highlight the label of the given menu item.
+ */
+void
+invert_label(int state, MENUITEM *m)
+{
+ unsigned i, j;
+ int col_offset, savettrow, savettcol;
+ char *lp;
+
+ get_cursor(&savettrow, &savettcol);
+
+ /*
+ * Leave the command name bold
+ */
+ col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
+ movecursor((int)m->tl.r, (int)m->tl.c + col_offset);
+ flip_inv(state);
+
+ for(i = m->tl.r; i <= m->br.r; i++)
+ for(j = m->tl.c + col_offset; j <= m->br.c; j++)
+ if(i == m->lbl.r && j == m->lbl.c + col_offset && m->label){
+ lp = m->label + col_offset; /* show label?? */
+ while(*lp && j++ < m->br.c)
+ putc(*lp++, stdout);
+
+ continue;
+ }
+ else
+ putc(' ', stdout);
+
+ if(state)
+ flip_inv(FALSE);
+
+ movecursor(savettrow, savettcol);
+}
+
+#else /* _WINDOWS */
+
+#define MOUSE_BUTTONS 3
+
+static int mexist = 0; /* is the mouse driver installed? */
+static int nbuttons; /* number of buttons on the mouse */
+static unsigned mnoop;
+
+/*
+ * init_mouse - check for and initialize mouse driver...
+ */
+
+int
+init_mouse(void)
+{
+ nbuttons = MOUSE_BUTTONS;
+ return (mexist = TRUE); /* Mouse always exists under windows */
+}
+
+/*
+ * end_mouse - a no-op on Windows
+ */
+void
+end_mouse(void)
+{
+}
+
+/*
+ * mouseexist - function to let outsiders know if mouse is turned on
+ * or not.
+ */
+int
+mouseexist(void)
+{
+ return(mexist);
+}
+
+/*
+ * checkmouse - Check mouse and return maped command.
+ *
+ * EXPORTED to pico.
+ * NOTE: "down", "xxx", and "yyy" aren't used under windows.
+ */
+int
+checkmouse (unsigned long *ch, int ddd, int xxx, int yyy)
+{
+ static int oindex; /* Index of previous mouse down. */
+ int mcol; /* current mouse column */
+ int mrow; /* current mouse row */
+ unsigned long r;
+ int rv = 0; /* TRUE when we have something to return. */
+ MEvent mouse;
+ int i = 0;
+ MENUITEM *mp;
+
+
+ *ch = 0;
+
+ /* Mouse installed? */
+ if (!mexist)
+ return (FALSE);
+
+ if (!mswin_getmouseevent (&mouse))
+ return (FALSE);
+
+
+ /* Location of mouse event. */
+ mcol = mouse.nColumn;
+ mrow = mouse.nRow;
+
+
+
+ /*
+ * If there is a tracking function it gets all the mouse events
+ * reguardless of where they occur.
+ */
+ if (mtrack != NULL) {
+ r = mtrack (mouse.event, mrow, mcol, mouse.button, mouse.keys);
+ if (r & 0xffff){
+ *ch = r;
+ rv = TRUE;
+ }
+ return (rv);
+ }
+
+
+
+
+ /* Mouse down or up? */
+ if (mouse.event == M_EVENT_DOWN) { /* button down */
+ oindex = -1; /* No Previous mouse down. */
+ }
+
+
+ /* In special screen region? */
+ for(mp = mfunc; mp; mp = mp->next)
+ if(mp->action && M_ACTIVE(mrow, mcol, mp))
+ break;
+
+ if(mp){
+
+ r = (*mp->action)(mouse.event, mrow, mcol, mouse.button, mouse.keys);
+ if (r){
+ *ch = r;
+ rv = TRUE;
+ }
+ }
+ else if(mouse.button == M_BUTTON_LEFT){
+
+ /* In any of the menuitems? */
+ while(1){ /* see if we understand event */
+ if(i >= 12){
+ i = -1; /* Not Found. */
+ break;
+ }
+
+ if(M_ACTIVE(mrow, mcol, &menuitems[i]))
+ break; /* Found. */
+
+ i++; /* Next. */
+ }
+
+ /* Now, was that a mouse down or mouse up? */
+ if (mouse.event == M_EVENT_DOWN) { /* button down */
+ oindex = i; /* remember where */
+ if(i != -1){ /* invert label */
+ if(menuitems[i].label_hiliter != NULL)
+ (*menuitems[i].label_hiliter)(1, &menuitems[i]);
+ else
+ invert_label(1, &menuitems[i]);
+ }
+ }
+ else if (mouse.event == M_EVENT_UP) {/* button up */
+ if (oindex != -1) { /* If up in menu item. */
+ if (i == oindex){ /* And same item down in. */
+ *ch = menuitems[i].val; /* Return menu character. */
+ rv = 1;
+ }
+ }
+ }
+ }
+ else if(mouse.button == M_BUTTON_RIGHT){
+ if (mouse.event == M_EVENT_UP) {
+ mswin_keymenu_popup();
+ }
+ }
+
+ /* If this is mouse up AND there was a mouse down in a menu item
+ * then uninvert that menu item */
+ if(mouse.event == M_EVENT_UP && oindex != -1){
+ if(menuitems[oindex].label_hiliter != NULL)
+ (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
+ else
+ invert_label(0, &menuitems[oindex]);
+ }
+
+ return(rv);
+}
+
+/*
+ * invert_label - highlight the label of the given menu item.
+ */
+void
+invert_label(int state, MENUITEM *m)
+{
+ int r, c;
+ unsigned i, j;
+ char *lp;
+ int old_state;
+ int wasShown;
+ int col_offset;
+ COLOR_PAIR *lastc = NULL;
+ if(m->val == mnoop)
+ return;
+
+
+ mswin_getpos (&r, &c); /* get cursor position */
+ wasShown = mswin_showcaret (0);
+ old_state = mswin_getrevstate ();
+ /*
+ * Leave the command name bold
+ */
+ col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
+ (*term.t_move)(m->tl.r, m->tl.c + col_offset);
+ if(state && m->kncp)
+ lastc = pico_set_colorp(m->kncp, PSC_REV|PSC_RET);
+ else if(!state && m->klcp)
+ lastc = pico_set_colorp(m->klcp, PSC_NORM|PSC_RET);
+ else
+ (*term.t_rev)(state);
+
+ for(i = m->tl.r; i <= m->br.r; i++) {
+ for(j = m->tl.c + col_offset; j <= m->br.c; j++) {
+ if(i == m->lbl.r && j == m->lbl.c + col_offset){ /* show label?? */
+ lp = m->label + col_offset;
+ while(*lp && j++ < m->br.c)
+ (*term.t_putchar)(*lp++);
+
+ continue;
+ }
+ else
+ (*term.t_putchar)(' ');
+ }
+ }
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(old_state);
+ mswin_move (r, c);
+ mswin_showcaret (wasShown);
+}
+
+
+#endif /* _WINDOWS */
+
+#endif /* MOUSE */
diff --git a/pico/osdep/mouse.h b/pico/osdep/mouse.h
new file mode 100644
index 00000000..db7946b3
--- /dev/null
+++ b/pico/osdep/mouse.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: mouse.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_MOUSE_INCLUDED
+#define PICO_OSDEP_MOUSE_INCLUDED
+
+#ifdef MOUSE
+
+
+/* exported prototypes */
+int init_mouse(void);
+void end_mouse(void);
+int mouseexist(void);
+int checkmouse(unsigned long *, int, int, int);
+void invert_label(int, MENUITEM *);
+
+
+#endif /* MOUSE */
+
+#endif /* PICO_OSDEP_MOUSE_INCLUDED */
diff --git a/pico/osdep/msdlg.c b/pico/osdep/msdlg.c
new file mode 100644
index 00000000..c4121dac
--- /dev/null
+++ b/pico/osdep/msdlg.c
@@ -0,0 +1,1050 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: msdlg.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * Module: msdlg.c
+ *
+ * Thomas Unger
+ * Networks and Distributed Computing
+ * Computing and Communications
+ * University of Washington
+ * Administration Builiding, AG-44
+ * Seattle, Washington, 98195, USA
+ * Internet: tunger@cac.washington.edu
+ *
+ *
+ * Pine and Pico are registered trademarks of the University of Washington.
+ * No commercial use of these trademarks may be made without prior written
+ * permission of the University of Washington.
+ *
+ * Pine, Pico, and Pilot software and its included text are Copyright
+ * 1989-1998 by the University of Washington.
+ *
+ * The full text of our legal notices is contained in the file called
+ * CPYRIGHT, included with this distribution.
+ *
+ *--------------------------------------------------------------------------*/
+
+#define WIN31
+#define STRICT
+
+#include <system.h>
+#include <general.h>
+
+#include <stdarg.h>
+#include <ddeml.h>
+
+#include "mswin.h"
+#include "msmenu.h"
+#include "resource.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../keydefs.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../utf8stub.h"
+#include "../../pith/charconv/filesys.h"
+
+
+extern HWND ghTTYWnd;
+extern HINSTANCE ghInstance;
+extern BOOL gfUseDialogs;
+extern FILE *mswin_debugfile;
+extern int mswin_debug;
+extern TCHAR gszAppName[45];
+
+
+
+#define BTN_FIRSTID 200
+#define BSPACE 4
+#define BWIDTH_MIN 120
+
+
+typedef struct {
+ LPTSTR prompt; /* Prompt. */
+ LPTSTR string; /* Resulting string. */
+ int strlen; /* Length of buffer. */
+ int append; /* Append to existing string. */
+ int passwd; /* Passwd, don't echo (1 use asterisk, 10 space). */
+ unsigned flags; /* other flags. */
+ int dflt;
+ int cancel;
+ int button_count; /* Number of additional buttons. */
+ MDlgButton *button_list; /* List of other buttons. */
+ char **helptext; /* Help text. */
+ char helpkey;
+ int result;
+} OEInfo;
+
+
+static OEInfo gOEInfo;
+static BOOL gDoHelpFirst;
+static WNDPROC gpOldEditProc; /* Old Edit window proc. */
+static WNDPROC gpEditProc;
+static WNDPROC gpOldBtnProc; /* Old Edit window proc. */
+static WNDPROC gpBtnProc;
+
+
+
+
+/*
+ * Forward function declaratins.
+ */
+LONG FAR PASCAL __export EditProc(HWND hBtn, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+LONG FAR PASCAL __export ButtonProc(HWND hBtn, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+BOOL CALLBACK __export mswin_dialog_proc (HWND hDlg, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK __export mswin_select_proc (HWND hDlg, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LONG FAR PASCAL __export KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+
+static void BuildButtonList (HWND, MDlgButton *, int, MDlgButton *);
+static BOOL ProcessChar (HWND, WPARAM, OEInfo *, long *);
+static void GetBtnPos (HWND, HWND, RECT *);
+
+int mswin_yesno_lptstr (LPTSTR);
+
+
+int
+mswin_usedialog (void)
+{
+ return (gfUseDialogs);
+}
+
+
+
+/*----------------------------------------------------------------------
+
+
+ Generic text entry. Prompt user for a string.
+
+
+ Args:
+ prompt -- The string to prompt with
+ string -- the buffer result is returned in, and original string (if
+ any) is passed in.
+ field_len -- Maximum length of string to accept
+ append_current -- flag indicating string should not be truncated before
+ accepting input
+ passwd -- a pass word is being fetch. Don't echo on screen
+ button_list -- pointer to array of mDlgButton's. input chars matching
+ those in list return value from list. Nearly identical
+ to Pine's ESCKEY structure.
+ help -- Arrary of strings for help text in bottom screen lines
+ flags -- flags
+
+ Result: editing input string
+ returns -1 unexpected errors
+ returns 0 typed CR or pressed "OK"
+ returns 1 typed ESC or pressed "Cancel"
+ returns 3 typed ^G or PF1 (help)
+ returns 4 typed ^L for a screen redraw
+ or one of the other return values defined in button_list.
+
+ WARNING: Care is required with regard to the escape_list processing.
+ The passed array is terminated with an entry that has ch = -1.
+ Function key labels and key strokes need to be setup externally!
+ Traditionally, a return value of 2 is used for ^T escapes.
+
+
+ Note About Help: We don't get the help text on the first call. If we
+ want help text we have to return 3. We'll get called again
+ with help text. To make it look good, we want to display
+ this the second time we're called. But we don't want to
+ display the help text every time we're called with help
+ text. To know the difference we set gDoHelpFirst when
+ exiting to request help text. If this is set then we
+ display help. If not, then no help.
+
+
+----------------------------------------------------------------------*/
+
+
+/*
+ * xxx implement flags.
+ * xxx display.d uses flags QFFILE, QDEFLT, and QBOBUF
+ */
+
+int
+mswin_dialog(UCS *prompt, UCS *buf, int nbuf, int append_current,
+ int passwd, MDlgButton *button_list, char **help, unsigned flags)
+{
+ DLGPROC dlgprc;
+ int c;
+ LPTSTR promptlpt, buflpt, b;
+ UCS *ucs;
+
+ mswin_flush();
+
+ promptlpt = ucs4_to_lptstr(prompt);
+ buflpt = (LPTSTR) fs_get(nbuf * sizeof(TCHAR));
+ b = ucs4_to_lptstr(buf);
+ if(b){
+ int i;
+
+ for(i = 0; i < nbuf && b[i]; i++)
+ buflpt[i] = b[i];
+
+ if(i < nbuf)
+ buflpt[i] = 0;
+
+ buflpt[nbuf-1] = 0;
+ fs_give((void **) &b);
+ }
+
+ gOEInfo.prompt = promptlpt;
+ gOEInfo.string = buflpt;
+ gOEInfo.strlen = nbuf;
+ gOEInfo.append = append_current;
+ gOEInfo.passwd = passwd;
+ gOEInfo.helptext = help;
+ gOEInfo.helpkey = 0x07; /* ^G */
+ gOEInfo.flags = flags;
+ gOEInfo.button_list = button_list;
+ gOEInfo.result = 0;
+
+ for (c = 0; button_list && button_list[c].ch != -1; ++c)
+ ;
+
+ gOEInfo.button_count = c;
+
+ gpEditProc = (WNDPROC) MakeProcInstance((FARPROC) EditProc, ghInstance);
+ gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
+ dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_dialog_proc, ghInstance);
+
+ DialogBox(ghInstance, MAKEINTRESOURCE (IDD_OPTIONALYENTER), ghTTYWnd, dlgprc);
+
+ FreeProcInstance((FARPROC) dlgprc);
+ FreeProcInstance((FARPROC) gpBtnProc);
+ FreeProcInstance((FARPROC) gpEditProc);
+
+ if(promptlpt)
+ fs_give((void **) &promptlpt);
+
+ ucs = lptstr_to_ucs4(buflpt);
+ if(ucs){
+ int i;
+
+ for(i = 0; i < nbuf && ucs[i]; i++)
+ buf[i] = ucs[i];
+
+ if(i < nbuf)
+ buf[i] = '\0';
+
+ buf[nbuf-1] = '\0';
+ fs_give((void **) &ucs);
+ }
+
+ return(gOEInfo.result);
+}
+
+
+
+
+/*
+ * Dialog procedure for the generic text entry dialog box.
+ */
+BOOL CALLBACK __export
+mswin_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = FALSE;
+ int i;
+ int biCount;
+ HWND hEdit;
+ MDlgButton built_in[3];
+ long l;
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ /*
+ * Set the prompt text.
+ */
+ SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
+
+ /*
+ * Set the initial edit text and configure the edit control
+ */
+ if (!gOEInfo.append)
+ *gOEInfo.string = '\0';
+
+ SetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string);
+
+ SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_SETPASSWORDCHAR,
+ (gOEInfo.passwd == 1) ? '*' : gOEInfo.passwd ? ' ' : 0, 0L);
+ SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_LIMITTEXT,
+ gOEInfo.strlen - 1, 0L);
+ l = (long) lstrlen(gOEInfo.string);
+ SendDlgItemMessage(hDlg, IDC_RESPONCE, EM_SETSEL, 0, l);
+ hEdit = GetDlgItem (hDlg, IDC_RESPONCE);
+ SetFocus (hEdit);
+ gpOldEditProc = (WNDPROC) GetWindowLong (hEdit, GWL_WNDPROC);
+ SetWindowLong (hEdit, GWL_WNDPROC, (DWORD)(FARPROC)gpEditProc);
+
+
+ /*
+ * Subclass the standard buttons and build buttons for each item
+ * in the button_list.
+ */
+ gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC);
+ SetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+ SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+
+ memset (&built_in, 0, sizeof (built_in));
+ built_in[0].id = IDOK;
+ built_in[1].id = IDCANCEL;
+ if (1) {
+ built_in[2].id = IDC_GETHELP;
+ biCount = 3;
+ }
+ else {
+ DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
+ biCount = 2;
+ }
+ BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
+
+
+ if (gOEInfo.helptext && gDoHelpFirst) {
+ mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
+ gDoHelpFirst = FALSE;
+ }
+ return (0);
+
+
+ case WM_ACTIVATE:
+ /*
+ * Keep focus on the edit item so key strokes are always
+ * received.
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ break;
+
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case IDOK:
+ /*
+ * Normal exit. Accept the new text.
+ */
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+
+ gOEInfo.result = 0;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ /*
+ * Cancel operation. Don't retreive new text.
+ */
+ gOEInfo.result = 1;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDC_GETHELP:
+ /*
+ * Get help. If we have help text display it. If not,
+ * return value 3, which tells the caller to provide us
+ * with help text.
+ */
+ if (gOEInfo.helptext != NULL) {
+ mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
+ }
+ else {
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+ gOEInfo.result = 3;
+ gDoHelpFirst = TRUE;
+ EndDialog (hDlg, gOEInfo.result);
+ }
+ ret = TRUE;
+ break;
+
+ default:
+ /*
+ * Search button list for button with this ID. If found
+ * return it's result code. Retreive text.
+ */
+ if ( wParam >= BTN_FIRSTID &&
+ wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+ i = wParam - BTN_FIRSTID;
+ gOEInfo.result = gOEInfo.button_list[i].rval;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ }
+ }
+
+ }
+ return (ret) ;
+}
+
+
+
+
+/*
+ * Subclassed window procedure for Edit control on generic text
+ * entry dialog box.
+ */
+LONG FAR PASCAL __export
+EditProc (HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hDlg;
+ LONG ret;
+
+ hDlg = GetParent (hEdit);
+
+ if (msg == WM_GETDLGCODE) {
+ /*
+ * Tell windows that we want to receive ALL keystrokes.
+ */
+ ret = CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam);
+ ret |= DLGC_WANTALLKEYS;
+ return (ret);
+ }
+
+
+ if (msg == WM_CHAR) {
+ /*
+ * See if the character typed is a shortcut for any of the
+ * buttons.
+ */
+ if (ProcessChar (hDlg, wParam, &gOEInfo, &ret))
+ return (ret);
+
+ /* No... Fall through for deault processing. */
+ }
+
+ return (CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam));
+}
+
+
+
+
+/*
+ * Subclass button windows.
+ *
+ * These buttons will automatically return the input focus to
+ * a control with id IDC_RESPONCE.
+ */
+LONG FAR PASCAL __export
+ButtonProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hDlg;
+ LONG ret;
+
+
+ if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
+ /*
+ * On mouse button up restore input focus to IDC_RESPONCE, which
+ * processes keyboard input.
+ */
+ ret = CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam);
+ hDlg = GetParent (hBtn);
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ return (ret);
+ }
+
+ return (CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam));
+}
+
+
+
+int
+mswin_yesno(UCS *prompt_ucs4)
+{
+ int ret;
+ LPTSTR prompt_lptstr;
+
+ prompt_lptstr = ucs4_to_lptstr(prompt_ucs4);
+ ret = mswin_yesno_lptstr(prompt_lptstr);
+ fs_give((void **) &prompt_lptstr);
+
+ return(ret);
+}
+
+
+int
+mswin_yesno_utf8(char *prompt_utf8)
+{
+ int ret;
+ LPTSTR prompt_lptstr;
+
+ prompt_lptstr = utf8_to_lptstr(prompt_utf8);
+ ret = mswin_yesno_lptstr(prompt_lptstr);
+ fs_give((void **) &prompt_lptstr);
+
+ return(ret);
+}
+
+/*
+ * Ask a yes/no question with the MessageBox procedure.
+ *
+ * Returns:
+ * 0 - Cancel operation.
+ * 1 - "Yes" was selected.
+ * 2 - "No" was selected.
+ */
+int
+mswin_yesno_lptstr(LPTSTR prompt)
+{
+ int ret;
+
+ mswin_flush ();
+
+ mswin_killsplash();
+
+ ret = MessageBox (ghTTYWnd, prompt, gszAppName,
+ MB_APPLMODAL | MB_ICONQUESTION | MB_YESNOCANCEL);
+
+ switch (ret) {
+ case IDYES: ret = 1; break;
+ case IDNO: ret = 2; break;
+ default:
+ case IDCANCEL: ret = 0; break;
+ }
+ return (ret);
+}
+
+
+/*----------------------------------------------------------------------
+
+
+ Generic selection routine. Display a prompt and a set of buttons for
+ each possible answer.
+
+
+
+ Args:
+ prompt -- The string to prompt with.
+ button_list -- pointer to array of mDlgButton's. input chars
+ matching those in list return value from
+ mlist. Nearly identical to Pine's ESCKEY
+ mstructure.
+ dflt -- Value returned when "Enter" is pressed.
+ on_ctrl_C -- Value returned to cancel dialog (ESC).
+ help -- Arrary of strings for help text in bottom screen
+ lines
+ flags -- flags
+
+ Result:
+ dflt -- Default option selected.
+ on_ctrl_C -- Calcel operation.
+ or one of the other return values defined in button_list.
+
+
+Note: To prcess keyboard in put we use a custom dialog control
+ which is invisible but always has the input focus.
+
+----------------------------------------------------------------------*/
+
+int
+mswin_select(char *utf8prompt, MDlgButton *button_list, int dflt, int on_ctrl_C,
+ char **help, unsigned flags)
+{
+ WNDCLASS wndclass;
+ DLGPROC dlgprc;
+ WNDPROC kbcProc;
+ int c;
+ LPTSTR promptlpt;
+
+ mswin_flush ();
+
+ promptlpt = utf8_to_lptstr(utf8prompt);
+
+ /*
+ * Setup dialog config structure.
+ */
+ gOEInfo.prompt = promptlpt;
+ gOEInfo.string = NULL;
+ gOEInfo.strlen = 0;
+ gOEInfo.append = 0;
+ gOEInfo.passwd = 0;
+ gOEInfo.helptext = help;
+ gOEInfo.helpkey = 'g';
+ gOEInfo.dflt = dflt;
+ gOEInfo.cancel = on_ctrl_C;
+ gOEInfo.flags = flags;
+ gOEInfo.button_list = button_list;
+ gOEInfo.result = 0;
+
+ /*
+ * Count the buttons.
+ */
+ for(c = 0; button_list && button_list[c].ch != -1; ++c)
+ ;
+
+ gOEInfo.button_count = c;
+
+ /*
+ * Register the class for the user control which will receive keyboard
+ * input events.
+ */
+ kbcProc = (WNDPROC) MakeProcInstance((FARPROC) KBCProc, ghInstance);
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = kbcProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = ghInstance;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = 0;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = TEXT("KeyboardCapture");
+
+ if(RegisterClass (&wndclass) == 0){
+ /*
+ * There is no good return value to indicate an error.
+ * Cancel the operation.
+ */
+ return(on_ctrl_C);
+ }
+
+ /*
+ * Make other procedure instances.
+ */
+ dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_select_proc, ghInstance);
+ gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
+
+ /*
+ * Bring up the dialog box.
+ */
+ DialogBox(ghInstance, MAKEINTRESOURCE(IDD_SELECT), ghTTYWnd, dlgprc);
+
+ /*
+ * Free the proc instances and window class.
+ */
+ FreeProcInstance((FARPROC) dlgprc);
+ FreeProcInstance((FARPROC) gpBtnProc);
+ UnregisterClass (TEXT("KeyboardCapture"), ghInstance);
+ FreeProcInstance((FARPROC) kbcProc);
+
+ if(promptlpt)
+ fs_give((void **) &promptlpt);
+
+ return(gOEInfo.result);
+}
+
+
+
+
+/*
+ * Dialog procedure for the button selection dialog box.
+ */
+BOOL CALLBACK __export
+mswin_select_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = FALSE;
+ int i;
+ int biCount;
+ MDlgButton built_in[3];
+
+ if (mswin_debug >= 1)
+ fprintf (mswin_debugfile, "Select: uMsg x%x (%d), wParam x%x, lParam x%lx\n",
+ uMsg, uMsg, wParam, lParam);
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ /*
+ * Show the prompt.
+ */
+ SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
+
+ /*
+ * Set focus to the invisible custom control
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+
+ /*
+ * Subclass the standard buttons and build buttons for each item
+ * in the button_list.
+ */
+ gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC);
+ SetWindowLong (GetDlgItem (hDlg, IDC_GETHELP), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+ SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+
+ memset (&built_in, 0, sizeof (built_in));
+ built_in[0].id = IDCANCEL;
+ if (gOEInfo.helptext != NULL) {
+ built_in[1].id = IDC_GETHELP;
+ biCount = 2;
+ }
+ else {
+ DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
+ gOEInfo.helpkey = 0;
+ biCount = 1;
+ }
+ BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
+ gDoHelpFirst = FALSE;
+ return (0);
+
+
+ case WM_ACTIVATE:
+ /*
+ * Keep focus on the custom control item so key strokes are always
+ * received.
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ break;
+
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case IDOK:
+ gOEInfo.result = gOEInfo.dflt;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ gOEInfo.result = gOEInfo.cancel;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDC_GETHELP:
+ /*
+ * Get help. If we have help text display it. If not,
+ * return value 3, which tells the caller to provide us
+ * with help text.
+ */
+ if (gOEInfo.helptext != NULL) {
+ mswin_showhelpmsg ((WINHAND) hDlg, gOEInfo.helptext);
+ }
+ ret = TRUE;
+ break;
+
+ default:
+ if ( wParam >= BTN_FIRSTID &&
+ wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
+ i = wParam - BTN_FIRSTID;
+ gOEInfo.result = gOEInfo.button_list[i].rval;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ }
+ }
+
+ }
+ return (ret) ;
+}
+
+
+
+/*
+ * Window procedure for the hidden custom control.
+ */
+LONG FAR PASCAL __export
+KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LONG ret;
+
+
+ if (uMsg == WM_GETDLGCODE) {
+ /*
+ * Tell windows that we want all the character messages.
+ */
+ ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ ret |= DLGC_WANTALLKEYS;
+ return (ret);
+ }
+
+
+ if (uMsg == WM_CHAR) {
+ /*
+ * See if the character typed is a shortcut for any of the
+ * buttons.
+ */
+ if (ProcessChar (GetParent (hWnd), wParam, &gOEInfo, &ret))
+ return (ret);
+
+ /* Fall through for default processing. */
+ }
+
+ return (DefWindowProc (hWnd, uMsg, wParam, lParam));
+}
+
+
+
+
+
+
+/*
+ * Check if character activates any button. If it does, send WM_COMMAND
+ * to target window.
+ *
+ * Args:
+ * hWnd - Window to send message to.
+ * wParam - character typed.
+ * oeinfo - dialog config structure. Contains button list.
+ * ret - value to return to windows.
+ *
+ * Returns:
+ * TRUE - Found a match. Exit from Window Procedure returning
+ * *ret to windows.
+ *
+ * FALSE - No match. Continue with default processing of character
+ * message.
+ *
+ */
+static BOOL
+ProcessChar (HWND hWnd, WPARAM wParam, OEInfo *oeinfo, long *ret)
+{
+ int i;
+ int id;
+
+
+ *ret = 0;
+ if (wParam == 0x0d) {
+ /*
+ * "Enter" is same as clicking on OK button.
+ */
+ PostMessage (hWnd, WM_COMMAND, IDOK,
+ MAKELONG (GetDlgItem (hWnd, IDOK), 1));
+ return (TRUE);
+ }
+ if (wParam == 0x1b || wParam == 0x03) {
+ /*
+ * Esc is same as clicking on Cancel button.
+ */
+ PostMessage (hWnd, WM_COMMAND, IDCANCEL,
+ MAKELONG (GetDlgItem (hWnd, IDCANCEL), 1));
+ return (TRUE);
+ }
+
+ /*
+ * Any of the custom buttons respond to this key?
+ */
+ for (i = 0; i < oeinfo->button_count; ++i) {
+ if (wParam == oeinfo->button_list[i].ch) {
+ id = oeinfo->button_list[i].id;
+ PostMessage (hWnd, WM_COMMAND, id,
+ MAKELONG (GetDlgItem (hWnd, id), 1));
+ return (TRUE);
+ }
+ }
+
+ /*
+ * Lastly, is it the help key?
+ */
+ if (oeinfo->helpkey != 0 && wParam == oeinfo->helpkey) {
+ id = IDC_GETHELP;
+ PostMessage (hWnd, WM_COMMAND, id,
+ MAKELONG (GetDlgItem (hWnd, id), 1));
+ return (TRUE);
+ }
+
+
+ /*
+ * Nothing we understand.
+ */
+ return (FALSE);
+}
+
+
+
+
+
+/*
+ * Get a button position in the parent's coordinate space.
+ */
+static void
+GetBtnPos (HWND hPrnt, HWND hWnd, RECT *r)
+{
+ GetWindowRect (hWnd, r);
+ ScreenToClient (hPrnt, (POINT *) r);
+ ScreenToClient (hPrnt, (POINT *) &r->right);
+}
+
+
+
+
+/*
+ * Add buttons to the dialog box.
+ */
+static void
+BuildButtonList(HWND hDlg, MDlgButton *built_in, int biCount, MDlgButton *button_list)
+{
+ RECT rDlg, rb1, rb2, r;
+ HDC hdc;
+ MDlgButton *pB; /* pointer to button struct*/
+ HWND hBtn, hBtn1, hBtn2; /* handle to buttons */
+ int btnCount; /* extra button count */
+ int rows, cols; /* button rows and columns */
+ int bpos; /* button position */
+ int i;
+ int maxstrIdx, maxstrLen; /* button w/ longest caption*/
+ DWORD textExtent; /* width of button caption */
+ int margine; /* left and right margines */
+ int btop, bleft; /* button position */
+ int bwidth, bheight, w; /* button size */
+ int bMinWidth; /* minimum buttonwidth */
+ int bvertSpace, bhorzSpace; /* button spacing */
+ int newWHeight, delta; /* window resizing */
+ TCHAR caption[128];
+ size_t ncaption;
+ HFONT btnFont;
+ SIZE size;
+
+ /*
+ * Are there any buttons to add?
+ */
+ if(button_list == NULL)
+ return;
+
+ maxstrIdx = 0;
+ for(btnCount = 0; button_list[btnCount].ch != -1; ++btnCount){
+ if(lstrlen(button_list[btnCount].label) >
+ lstrlen(button_list[maxstrIdx].label))
+ maxstrIdx = btnCount;
+ }
+
+ if(btnCount == 0)
+ return;
+
+
+ /*
+ * Get the size of the dialog box and the positions of the
+ * first and, if there is one, the second button. Calculate or
+ * default button offsets and positions.
+ */
+ GetClientRect(hDlg, &rDlg);
+ hBtn1 = GetDlgItem(hDlg, built_in[0].id);
+ GetBtnPos(hDlg, hBtn1, &rb1);
+ margine = rb1.left; /* left and right margine */
+ bheight = rb1.bottom - rb1.top; /* button width */
+ bwidth = rb1.right - rb1.left; /* button height. */
+ if(biCount > 1){
+ hBtn2 = GetDlgItem(hDlg, built_in[1].id);
+ GetBtnPos(hDlg, hBtn2, &rb2);
+ bvertSpace = rb2.top - rb1.top; /* vertical spacing */
+ }
+ else{
+ bvertSpace = bheight + BSPACE; /* vertical spacing */
+ }
+
+
+ /*
+ * Get the button font.
+ */
+ btnFont = (HFONT) SendMessage (hBtn1, WM_GETFONT, 0, 0);
+
+ /*
+ * Get the screen extent of the longest button label. min width
+ * is the extent, plus the average width of 5 extra characters for
+ * key stroke, plus margine.
+ */
+ hdc = GetDC (hBtn1);
+ ncaption = sizeof(caption)/sizeof(TCHAR);
+ _sntprintf(caption, ncaption, TEXT("%s '%s'"), button_list[maxstrIdx].label,
+ button_list[maxstrIdx].name);
+
+ maxstrLen = lstrlen(caption);
+ GetTextExtentPoint32(hdc, caption, maxstrLen, &size);
+
+ textExtent = size.cx;
+
+ ReleaseDC(hBtn1, hdc);
+ bMinWidth = LOWORD (textExtent) + (2 * (LOWORD(textExtent) / maxstrLen));
+ if(bwidth < bMinWidth)
+ bwidth = bMinWidth;
+
+ /*
+ * Calculate button positions. If the buttons are going to extend
+ * past the right edge of the dialog box, shrink their width. But
+ * if they get narrower than the min width calculated above then
+ * increase the number of rows.
+ */
+ rows = 1;
+ do {
+ ++rows; /* More rows. */
+ w = bwidth; /* Original button width. */
+ cols = 1 + ((biCount + btnCount) - 1) / rows; /* Calc num cols. */
+
+ /* Need to srink button width? */
+ if (2 * margine + bwidth * cols + BSPACE * (cols - 1) > rDlg.right) {
+ w = ((rDlg.right - (2 * margine)) - (BSPACE * (cols-1))) / cols;
+ }
+ /* Did buttons get too narrow? */
+ } while (w < bMinWidth && cols > 1);
+
+ bwidth = w;
+ bhorzSpace = bwidth + BSPACE; /* horizontal spacing */
+
+
+ /*
+ * Resize window.
+ */
+ newWHeight = rb1.top + (rows * bvertSpace);
+ delta = newWHeight - rDlg.bottom;
+ if (delta != 0) {
+ GetWindowRect (hDlg, &r);
+ MoveWindow (hDlg, r.left, r.top, r.right - r.left,
+ (r.bottom - r.top) + delta, FALSE);
+ }
+
+
+
+ /*
+ * Create new buttons.
+ */
+ for(bpos = 0; bpos < biCount + btnCount; ++bpos) {
+ /*
+ * Calculate position for this button.
+ */
+ btop = rb1.top + (bpos % rows) * bvertSpace;
+ bleft = rb1.left + (bpos / rows) * bhorzSpace;
+
+ if(bpos < biCount){
+ /*
+ * Resize existing buttons.
+ */
+ MoveWindow(GetDlgItem(hDlg, built_in[bpos].id),
+ bleft, btop, bwidth, bheight, FALSE);
+ }
+ else{
+ i = bpos - biCount;
+ pB = &button_list[i];
+
+ _sntprintf(caption, ncaption, TEXT("%s '%s'"), pB->label, pB->name);
+ hBtn = CreateWindow(TEXT("BUTTON"), caption,
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ bleft, btop, bwidth, bheight,
+ hDlg, NULL, ghInstance, NULL);
+
+ pB->id = BTN_FIRSTID + i;
+ SetWindowLong(hBtn, GWL_ID, pB->id);
+ SendMessage(hBtn, WM_SETFONT, (WPARAM)btnFont, MAKELPARAM (0, 0));
+
+ /* Subclass button. */
+ SetWindowLong(hBtn, GWL_WNDPROC, (DWORD)(FARPROC)gpBtnProc);
+ EnableWindow(hBtn, TRUE);
+ }
+ }
+}
diff --git a/pico/osdep/msmenu.h b/pico/osdep/msmenu.h
new file mode 100644
index 00000000..0bf63c53
--- /dev/null
+++ b/pico/osdep/msmenu.h
@@ -0,0 +1,97 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef MSMENU_H
+#define MSMENU_H
+
+#include "resource.h"
+
+
+/*
+ * var in pine's key structure we'll use
+ */
+#define KS_OSDATAVAR short menuitem;
+#define KS_OSDATAGET(X) ((X)->menuitem)
+#define KS_OSDATASET(X, Y) ((X)->menuitem = (Y))
+
+/*
+ * Menu key definitions.
+ * Should be same values as in resouce.h
+ * These are all in a range from KS_RANGESTART to KS_RANGEEND
+ * with no holes.
+ */
+#define KS_NONE 0
+#define KS_RANGESTART IDM_MI_VIEW
+
+#define KS_VIEW IDM_MI_VIEW
+#define KS_EXPUNGE IDM_MI_EXPUNGE
+#define KS_ZOOM IDM_MI_ZOOM
+#define KS_SORT IDM_MI_SORT
+#define KS_HDRMODE IDM_MI_HDRMODE
+#define KS_MAINMENU IDM_MI_MAINMENU
+#define KS_FLDRLIST IDM_MI_FLDRLIST
+#define KS_FLDRINDEX IDM_MI_FLDRINDEX
+#define KS_COMPOSER IDM_MI_COMPOSER
+#define KS_PREVPAGE IDM_MI_PREVPAGE
+#define KS_PREVMSG IDM_MI_PREVMSG
+#define KS_NEXTMSG IDM_MI_NEXTMSG
+#define KS_ADDRBOOK IDM_MI_ADDRBOOK
+#define KS_WHEREIS IDM_MI_WHEREIS
+#define KS_PRINT IDM_MI_PRINT
+#define KS_REPLY IDM_MI_REPLY
+#define KS_FORWARD IDM_MI_FORWARD
+#define KS_BOUNCE IDM_MI_BOUNCE
+#define KS_DELETE IDM_MI_DELETE
+#define KS_UNDELETE IDM_MI_UNDELETE
+#define KS_FLAG IDM_MI_FLAG
+#define KS_SAVE IDM_MI_SAVE
+#define KS_EXPORT IDM_MI_EXPORT
+#define KS_TAKEADDR IDM_MI_TAKEADDR
+#define KS_SELECT IDM_MI_SELECT
+#define KS_APPLY IDM_MI_APPLY
+#define KS_POSTPONE IDM_MI_POSTPONE
+#define KS_SEND IDM_MI_SEND
+#define KS_CANCEL IDM_MI_CANCEL
+#define KS_ATTACH IDM_MI_ATTACH
+#define KS_TOADDRBOOK IDM_MI_TOADDRBOOK
+#define KS_READFILE IDM_MI_READFILE
+#define KS_JUSTIFY IDM_MI_JUSTIFY
+#define KS_ALTEDITOR IDM_MI_ALTEDITOR
+#define KS_GENERALHELP IDM_MI_GENERALHELP
+#define KS_SCREENHELP IDM_MI_SCREENHELP
+#define KS_EXIT IDM_MI_EXIT
+#define KS_NEXTPAGE IDM_MI_NEXTPAGE
+#define KS_SAVEFILE IDM_MI_SAVEFILE
+#define KS_CURPOSITION IDM_MI_CURPOSITION
+#define KS_GOTOFLDR IDM_MI_GOTOFLDR
+#define KS_JUMPTOMSG IDM_MI_JUMPTOMSG
+#define KS_RICHHDR IDM_MI_RICHHDR
+#define KS_EXITMODE IDM_MI_EXITMODE
+#define KS_REVIEW IDM_MI_REVIEW
+#define KS_KEYMENU IDM_MI_KEYMENU
+#define KS_SELECTCUR IDM_MI_SELECTCUR
+#define KS_UNDO IDM_MI_UNDO
+#define KS_SPELLCHK IDM_MI_SPELLCHK
+
+#define KS_RANGEEND IDM_MI_SPELLCHK
+
+#define KS_COUNT ((KS_RANGEEND - KS_RANGESTART) + 1)
+
+
+
+#define MENU_DEFAULT 300 /* Default menu for application. */
+#define MENU_COMPOSER 301 /* Menu for pine's composer. */
+
+#endif /* MSMENU_H */
diff --git a/pico/osdep/mswin.bmp b/pico/osdep/mswin.bmp
new file mode 100644
index 00000000..e6882628
--- /dev/null
+++ b/pico/osdep/mswin.bmp
Binary files differ
diff --git a/pico/osdep/mswin.c b/pico/osdep/mswin.c
new file mode 100644
index 00000000..58d2ab3b
--- /dev/null
+++ b/pico/osdep/mswin.c
@@ -0,0 +1,12448 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#define WIN31
+#define STRICT
+
+#define termdef 1 /* don't define "term" external */
+
+#include "../headers.h"
+
+#include <stdarg.h>
+#include <ddeml.h>
+
+#include "mswin.h"
+#include "resource.h"
+
+#include "../../pith/osdep/pipe.h"
+#include "../../pith/osdep/canaccess.h"
+
+#include "../../pith/charconv/filesys.h"
+#include "../../pith/charconv/utf8.h"
+
+#include "../../pith/filttype.h"
+#include "../../pith/osdep/color.h"
+
+#include "mswin_tw.h"
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Defines
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+#define ICON
+
+
+/* For debugging, export locals so debugger can see them. */
+#ifdef DEBUG
+#define LOCAL
+#else
+#define LOCAL static
+#endif
+
+
+/*
+ * Define which debugging is desired. Generally only FDEBUG.
+ */
+#define FDEBUG /* Standard file debugging. */
+
+#undef SDEBUG /* Verbose debugging of startup and windows handling*/
+#undef CDEBUG /* Verbose debugging of character input timeing. */
+
+#undef OWN_DEBUG_FILE /* Define if we want to write to our own debug file,
+ * not pine's. */
+
+
+/* Max size permitted for the screen. */
+#define MAXNROW 200
+#define MAXNCOLUMN NLINE
+
+#define MINNROW 10 /* Minimum screen size */
+#define MINNCOLUMN 32
+
+#define MARGINE_LEFT 3
+#define MARGINE_TOP 1
+
+#define FRAME_3D_SIZE 1
+
+#define WIN_MIN_X_SIZE 190 /* Minimum window size. */
+#define WIN_MIN_Y_SIZE 180
+
+#define WIN_X_BORDER_SIZE 8 /* Space taked by window frame. */
+#define WIN_Y_BORDER_SIZE 65
+
+#define FONT_MIN_SIZE 5
+#define FONT_MAX_SIZE 21
+
+#define PRINT_TAB_SIZE 8 /* Tab size used by print code. */
+
+
+#define TB_HEIGHT 32 /* Tool Bar Height. */
+#define TB_BUTTONHEIGHT 16 /* Button Height. */
+#define TB_BUTTONSPACING 8 /* Space between buttons. */
+
+
+/* Some string lengths. */
+#define MAXLEN_TEMPSTR 256 /* Max size for temp storage. */
+
+#define WIN_POS_STR_MAX_LEN 21 /* Max length for window-position
+ * string. */
+
+#define MENU_ITEM_NAME_LEN 32 /* Menu item name lengths. */
+
+
+/* Length of keyboard input queue. */
+#define CHARACTER_QUEUE_LENGTH 32
+#define MOUSE_QUEUE_LENGTH 32
+
+/* Number of resize callback functions we can keep track of. */
+#define RESIZE_CALLBACK_ARRAY_SIZE 3
+
+/* Number of bytes held in the write accumulator. */
+#define WRITE_ACCUM_SIZE 200
+
+/* Max time that may pass between calls to GetMessage. See mswin_charavail()
+ */
+#define GM_MAX_TIME 3000 /* In milliseconds.*/
+
+/* My Timer Message */
+#define MY_TIMER_ID 33
+/* timeout period in miliseconds. */
+#define MY_TIMER_PERIOD (UINT)((IDLE_TIMEOUT + 1)*1000)
+/***** We use variable my_timer_period now instead so that we can set
+ it differently when in pine and when in regular old pico.
+ We're not entirely sure we need it in pico, but we will leave
+ it there because we don't understand.
+ *****/
+#define MY_TIMER_SHORT_PERIOD (UINT)5000 /* used when there is a task in
+ the OnTask list. */
+#define MY_TIMER_VERY_SHORT_PERIOD (UINT)500 /* used when SIGALRM and alarm()
+ is set. */
+#define MY_TIMER_EXCEEDINGLY_SHORT_PERIOD (UINT)80 /* used when
+ gAllowMouseTracking is set */
+
+#define TIMER_FAIL_MESSAGE TEXT("Failed to get all necessary Windows resources (timers). Alpine will run, but may not be able to keep the connection to the server alive. Quitting other applications and restarting Alpine may solve the problem.")
+
+/*
+ * Task bar notification Icon id and call back message for it.
+ */
+#define TASKBAR_ICON_NEWMAIL 1
+#define TASKBAR_ICON_MESSAGE WM_USER+1000
+
+
+/*
+ * Below here are fixed constancs that really should not be changed.
+ */
+
+/* Auto Wrap States. */
+#define WRAP_OFF 0 /* Never wrap to next line. */
+#define WRAP_ON 1 /* Wrap to next line. */
+#define WRAP_NO_SCROLL 2 /* Wrap to next line but DON'T scroll
+ screen to do it. */
+/* Speicial keys in the Character Queue. */
+#define CQ_FLAG_DOWN 0x01
+#define CQ_FLAG_EXTENDED 0x02
+#define CQ_FLAG_ALT 0x04
+
+/* Special ASCII characters. */
+#define ASCII_BEL 0x07
+#define ASCII_BS 0x08
+#define ASCII_TAB 0x09
+#define ASCII_LF 0x0A
+#define ASCII_CR 0x0D
+#define ASCII_XON 0x11
+#define ASCII_XOFF 0x13
+
+/* Character Attributes. */
+#define CHAR_ATTR_NORM 0x00 /* Normal. */
+#define CHAR_ATTR_REV 0x01 /* Reverse Video. */
+#define CHAR_ATTR_BOLD 0x02 /* Reverse Video. */
+#define CHAR_ATTR_ULINE 0x04 /* Reverse Video. */
+#define CHAR_ATTR_SEL 0x08 /* Selected text. */
+#define CHAR_ATTR_NOT 0x80 /* No attributes. */
+
+/*
+ * Different applications that we know about.
+ */
+#define APP_UNKNOWN 0
+#define APP_PICO 1
+#define APP_PICO_IDENT TEXT("pico")
+#define APP_PINE 2
+#define APP_PINE_IDENT TEXT("pine")
+
+/*
+ * Control values for call to AccelCtl.
+ */
+/*#undef ACCELERATORS*/
+#define ACCELERATORS
+#define ACCEL_UNLOAD 0 /* Unload the accelerators. */
+#define ACCEL_LOAD 1 /* Load the accelerators. */
+#define ACCEL_TOGGLE 2 /* Toggle the accelerators. */
+
+/*
+ * flag bits to control which edit menu options can get lit
+ */
+#define EM_NONE 0L
+#define EM_CP 0x0001
+#define EM_CP_APPEND 0x0002
+#define EM_FIND 0x0004
+#define EM_PST 0x0008
+#define EM_PST_ABORT 0x0010
+#define EM_CUT 0x0020
+#define EM_SEL_ALL 0x0040
+
+#define EM_MAX_ACCEL 6
+
+/* Offsets to objects in window extra storage. */
+#define GWL_PTTYINFO 0 /* Offset in Window extra storage. */
+
+/*
+ *
+ */
+#define FONT_CHARSET_FONT DEFAULT_CHARSET
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Typedefs
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/* Type that the screen array is made up of. */
+#define CHAR unsigned char
+
+/* define possible caret shapes */
+typedef enum {
+ CaretBlock = 0,
+ CaretSmallBlock,
+ CaretHorizBar,
+ CaretVertBar
+} CARETS;
+
+/* Type that the attribute array is made up of. */
+typedef struct _character_attribute {
+ unsigned style:8;
+ DWORD rgbFG, rgbBG;
+} CharAttrib;
+
+typedef int (*ResizeCallBackProc)();
+typedef int (*FileDropCallBackProc)();
+typedef short CORD;
+
+/* NOTE: There is currently code that assumes that CHAR and CharAttrib
+ * are one byte in size. All this code is flaged with a preceeding
+ * assert () */
+
+/* Struct that defines command menu entries. */
+typedef struct tagMenuItem {
+ BOOL miActive;
+ UCS miKey;
+ short miIndex;
+ char *miLabel;
+} MenuItem;
+
+
+/* List of child window IDs and previous window procedures. */
+typedef struct tagBtnList {
+ WORD wndID;
+ WNDPROC wndProc;
+} BtnList;
+
+
+/* General info. */
+typedef struct tagTTYINFO {
+ TCHAR *pScreen; /* Screen. */
+ int *pCellWidth; /* how wide to paint the char */
+ CharAttrib *pAttrib; /* Attributes. */
+ TCHAR writeAccum[WRITE_ACCUM_SIZE];
+ int writeAccumCount;
+ CARETS cCaretStyle; /* Current caret's style */
+ int scrollRange; /* Current scroll bar range. */
+ long scrollPos; /* Current scroll position. */
+ long scrollTo; /* Possition of last scroll to. */
+ HFONT hTTYFont;
+ LOGFONT lfTTYFont;
+ DWORD rgbFGColor; /* Normal forground color. */
+ DWORD rgbBGColor; /* Normal background color. */
+ DWORD rgbRFGColor; /* Reverse forground color. */
+ DWORD rgbRBGColor; /* Reverse background color */
+ unsigned screenDirty:1; /* TRUE if screen needs update. */
+ unsigned eraseScreen:1; /* TRUE if need to erase whole screen */
+ unsigned fMinimized:1; /* True when window is minimized. */
+ unsigned fMaximized:1; /* True when window is maximized. */
+ unsigned fFocused:1; /* True when we have focus. */
+ unsigned fNewLine:1; /* Auto LF on CR. */
+ unsigned fMassiveUpdate:1;/* True when in Massive screen update. */
+ unsigned fNewMailIcon:1; /* True when new mail has arrived. */
+ unsigned fMClosedIcon:1; /* True when no mailbox is open. */
+ unsigned fCursorOn:1; /* True when cursor's shown */
+ unsigned fCaretOn:1; /* True if caret's displayed */
+ unsigned fTrayIcon:1; /* True if tool tray icon's on */
+ ResizeCallBackProc resizer[RESIZE_CALLBACK_ARRAY_SIZE];
+ FileDropCallBackProc dndhandler;
+ int autoWrap; /* Auto wrap to next line. */
+ CharAttrib curAttrib; /* Current character attributes. */
+ int actNRow, actNColumn; /* Actual number of rows and comumns
+ * displayed. */
+ CORD xSize, ySize; /* Size of screen in pixels */
+ CORD xScroll, yScroll; /* ?? */
+ CORD xOffset, yOffset; /* Offset from the left and top of
+ * window contents. */
+ CORD nColumn, nRow; /* Current position of cursor in
+ * cells. */
+ CORD xChar, yChar; /* Width of a char in pixels. */
+ int yCurOffset; /* offset of cursor Y-size */
+ CORD fDesiredSize; /* TRUE when there is a specific size
+ * the window should be expanded to
+ * after being minimized. */
+ CORD xDesPos, yDesPos; /* Desired position. */
+ CORD xDesSize, yDesSize; /* Desired window position. */
+ int curWinMenu; /* Current window menu. */
+ HACCEL hAccel; /* Handle to accelorator keys. */
+ UINT fAccel; /* vector of bound accelerator keys. */
+ CORD toolBarSize; /* Size of toolbar. */
+ BOOL toolBarTop; /* Toolbar on top? */
+ int curToolBarID;
+ BtnList *toolBarBtns;
+ HWND hTBWnd;
+ HBRUSH hTBBrush;
+ BOOL menuItemsCurrent;
+ BOOL noScrollUpdate;
+ BOOL scrollRangeChanged;
+ long noSUpdatePage;
+ long noSUpdateRange;
+ short menuItemsIndex;
+ MenuItem menuItems[KS_COUNT];
+} TTYINFO, *PTTYINFO ;
+
+
+#define MAXCLEN (MAX(MAXCOLORLEN,RGBLEN+1))
+typedef struct MSWINColor {
+ char *colorName;
+ char *canonicalName;
+ COLORREF colorRef;
+} MSWINColor;
+
+
+typedef struct {
+ char *name;
+ CARETS style;
+} MSWinCaret_t;
+
+
+/*
+ * Entry in the OnTask list. This is a list of actions to perform
+ * when a task exits. Currently, the only thing we do is delete
+ * files. if that changes this structure will get more complex.
+ *
+ * hTask == NULL means "This program" and can be used to arrange for
+ * deletion of files when this program exits.
+ */
+typedef struct ontask {
+ struct ontask *next;
+ HTASK hTask;
+ char path[PATH_MAX+1];
+} OnTaskItem;
+
+typedef void (__cdecl *SignalType)(int);
+
+typedef struct _iconlist {
+ HICON hIcon;
+/* char path[PATH_MAX]; */
+/* WORD index; */
+ int id;
+ short row;
+ struct _iconlist *next;
+} IconList;
+
+/*
+ * char * array for printing registry settings.
+ */
+typedef struct MSWR_LINE_BUFFER {
+ char **linep; /* store these as utf8, since that's what we have to pass back */
+ unsigned long size;
+ unsigned long offset;
+} MSWR_LINE_BUFFER_S;
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Forward function declarations.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL void *
+MyGetWindowLongPtr(HWND hwnd, int nIndex)
+{
+ return (void *)(LONG_PTR)GetWindowLongPtr(hwnd, nIndex);
+}
+
+LOCAL LONG_PTR
+MySetWindowLongPtr(HWND hwnd, int nIndex, void *NewLongPtr)
+{
+// warning C4244: 'function': conversion from 'LONG_PTR' to 'LONG',
+// possible loss of data
+#pragma warning(push)
+#pragma warning(disable: 4244)
+ return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)NewLongPtr);
+#pragma warning(pop)
+}
+
+#ifdef WIN32
+#define GET_HINST( hWnd ) ((HINSTANCE) MyGetWindowLongPtr( hWnd, GWLP_HINSTANCE ))
+#define GET_ID( hWnd ) (LOWORD(MyGetWindowLongPtr( hWnd, GWLP_ID )))
+#else
+#define GET_HINST( hWnd ) ((HINSTANCE) GetWindowWord( hWnd, GWW_HINSTANCE ))
+#define GET_ID( hWnd ) ((WORD) GetWindowWord( hWnd, GWW_ID ))
+#endif
+
+#define CONSTRAIN(v,min,max) ((v) = (v) < (min) ? (min) : (v) > (max) ? (max) : (v))
+
+
+/* function prototypes (private) */
+int app_main (int argc, char *argv[]);
+
+void WinExit (void);
+
+LOCAL void mswin_invalidparameter(const wchar_t *, const wchar_t *,
+ const wchar_t *, unsigned int, uintptr_t);
+
+LOCAL BOOL InitApplication (HANDLE);
+LOCAL HWND InitInstance (HANDLE, int);
+LOCAL void MakeArgv (HINSTANCE hInstance, LPSTR cmdLine, int *pargc,
+ CHAR ***pargv);
+LOCAL LRESULT NEAR CreateTTYInfo (HWND hWnd);
+LOCAL BOOL NEAR DestroyTTYInfo (HWND hWnd);
+LOCAL int ResizeTTYScreen (HWND hWnd, PTTYINFO pTTYInfo,
+ int newNRow, int newNColumn);
+LOCAL BOOL ResetTTYFont (HWND, PTTYINFO, LOGFONT *);
+LOCAL BOOL EraseTTY (HWND, HDC);
+LOCAL BOOL PaintTTY (HWND);
+LOCAL BOOL GetMinMaxInfoTTY (HWND hWnd, MINMAXINFO __far *lpmmi);
+LOCAL BOOL AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos);
+LOCAL BOOL SizeTTY (HWND, int, CORD, CORD);
+LOCAL BOOL SizingTTY (HWND hWnd, int fwSide, LPRECT pRect);
+LOCAL void FrameRect3D(HDC hdc, RECT * pRC, int width, BOOL raised);
+LOCAL void FillRectColor(HDC hDC, RECT * pRC, COLORREF color);
+
+
+LOCAL BOOL MoveTTY (HWND hWnd, int xPos, int yPos);
+LOCAL void ScrollTTY (HWND hWnd, int wScrollCode, int nPos, HWND hScroll);
+LOCAL void CaretTTY (HWND hWnd, CARETS cStyle);
+LOCAL void CaretCreateTTY (HWND hWnd);
+#ifdef WIN32
+LOCAL void MouseWheelTTY (HWND hWnd, int xPos, int yPos,
+ int fwKeys, int zDelta);
+LOCAL void MouseWheelMultiplier ();
+#endif
+BOOL CALLBACK NoMsgsAreSent (void);
+LOCAL BOOL SetTTYFocus (HWND);
+LOCAL BOOL KillTTYFocus (HWND);
+LOCAL BOOL MoveTTYCursor (HWND);
+LOCAL BOOL ProcessTTYKeyDown (HWND hWnd, TCHAR bOut);
+LOCAL BOOL ProcessTTYCharacter (HWND hWnd, TCHAR bOut);
+LOCAL BOOL ProcessTTYMouse (HWND hWnd, int mevent, int button, CORD xPos,
+ CORD yPos, WPARAM keys);
+LOCAL BOOL ProcessTTYFileDrop (HANDLE wParam);
+LOCAL void ProcessTimer (void);
+LOCAL BOOL WriteTTYBlock (HWND, LPTSTR, int);
+LOCAL BOOL WriteTTYText (HWND, LPTSTR, int);
+LOCAL BOOL WriteTTYChar (HWND, TCHAR);
+
+LOCAL VOID GoModalDialogBoxParam (HINSTANCE, LPTSTR, HWND,
+ DLGPROC, LPARAM);
+LOCAL BOOL SelectTTYFont (HWND);
+LOCAL void SetColorAttribute (COLORREF *cf, char *colorName);
+LOCAL void SetReverseColor (void);
+LOCAL BOOL ConvertRGBString (char *colorName, COLORREF *cf);
+LOCAL char *ConvertStringRGB (char *colorName, size_t ncolorName, COLORREF colorRef);
+LOCAL BOOL ScanInt (char *str, int min, int max, int *val);
+
+LOCAL void TBToggle (HWND);
+LOCAL void TBPosToggle (HWND);
+LOCAL void TBShow (HWND);
+LOCAL void TBHide (HWND);
+LOCAL void TBSwap (HWND, int);
+LOCAL unsigned scrwidth(LPTSTR lpText, int nLength);
+LOCAL long pscreen_offset_from_cord(int row, int col, PTTYINFO pTTYInfo);
+
+LOCAL int tcsucmp(LPTSTR o, LPTSTR r);
+LOCAL int _print_send_page(void);
+
+/* defined in region.c */
+int copyregion(int f, int n);
+
+
+#ifdef ACCELERATORS
+#ifdef ACCELERATORS_OPT
+LOCAL void AccelCtl (HWND hWnd, int ctl, BOOL saveChange);
+#endif
+LOCAL void AccelManage (HWND hWnd, long accels);
+LOCAL void UpdateAccelerators (HWND hWnd);
+#endif
+
+LOCAL UINT UpdateEditAllowed(HWND hWnd);
+LOCAL void PopupConfig(HMENU hMenu, MPopup *members, int *n);
+LOCAL MPopup *PopupId(MPopup *members, int id);
+LOCAL BOOL CopyCutPopup (HWND hWnd, UINT fAllowed);
+
+LOCAL void SelStart (int nRow, int nColumn);
+LOCAL void SelFinish (int nRow, int nColumn);
+LOCAL void SelClear (void);
+LOCAL void SelTrackXYMouse (int xPos, int yPos);
+LOCAL void SelTrackMouse (int nRow, int nColumn);
+LOCAL BOOL SelAvailable (void);
+LOCAL void SelDoCopy (HANDLE hCB, DWORD lenCB);
+
+LOCAL void UpdateTrayIcon(DWORD dwMsg, HICON hIcon, LPTSTR tip);
+
+LOCAL void FlushWriteAccum (void);
+
+LOCAL int mswin_reg_lptstr(int, int, LPTSTR, size_t);
+
+LOCAL BOOL MSWRPoke(HKEY hKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data);
+LOCAL int MSWRClear(HKEY hKey, LPTSTR pSubKey);
+LOCAL void MSWRAlpineSet(HKEY hRootKey, LPTSTR subkey, LPTSTR val,
+ int update, LPTSTR data);
+LOCAL int MSWRProtocolSet(HKEY hKey, int type, LPTSTR path_lptstr);
+LOCAL void MSWRAlpineSetHandlers(int update, LPTSTR path);
+LOCAL int MSWRAlpineGet(HKEY hKey, LPTSTR subkey, LPTSTR val,
+ LPTSTR data_lptstr, size_t len);
+
+LOCAL void MSWIconAddList(int row, int id, HICON hIcon);
+LOCAL int MSWIconPaint(int row, HDC hDC);
+LOCAL void MSWIconFree(IconList **ppIcon);
+LOCAL void MSWRLineBufAdd(MSWR_LINE_BUFFER_S *lpLineBuf, LPTSTR line);
+LOCAL int MSWRDump(HKEY hKey, LPTSTR pSubKey, int keyDepth,
+ MSWR_LINE_BUFFER_S *lpLineBuf);
+
+
+ /* ... interface routines ... */
+
+
+void ProcessMenuItem (HWND hWnd, WPARAM wParam);
+
+void AlarmDeliver (void);
+void HUPDeliver (void);
+
+void PrintFontSameAs (HWND hWnd);
+void PrintFontSelect (HWND hWnd);
+void ExtractFontInfo(LOGFONT *pFont,
+ LPTSTR fontName, size_t nfontName,
+ int *fontSize,
+ LPTSTR fontStyle, size_t nfontStyle,
+ int ppi,
+ LPTSTR fontCharSet, size_t nfontCharSet);
+
+LOCAL void DidResize (PTTYINFO pTTYInfo);
+
+LOCAL void UpdateMenu (HWND hWnd);
+LOCAL void EditCut (void);
+LOCAL void EditCopy (void);
+LOCAL void EditCopyAppend (void);
+LOCAL void EditDoCopyData (HANDLE hCB, DWORD lenCB);
+LOCAL void EditPaste (void);
+LOCAL void EditCancelPaste (void);
+LOCAL UCS EditPasteGet (void);
+LOCAL BOOL EditPasteAvailable (void);
+LOCAL void EditSelectAll (void);
+
+LOCAL void MSWHelpShow (cbstr_t);
+LOCAL int MSWHelpSetMenu (HMENU hmenu);
+
+
+LOCAL void SortHandler (int order, int reverse);
+LOCAL void FlagHandler (int order, int reverse);
+
+LOCAL void MyTimerSet (void);
+
+LOCAL LRESULT ConfirmExit (void);
+
+LOCAL void CQInit (void);
+LOCAL BOOL CQAvailable (void);
+LOCAL BOOL CQAdd (UCS c, BOOL fKeyControlDown);
+LOCAL BOOL CQAddUniq (UCS c, BOOL fKeyControlDown);
+LOCAL UCS CQGet ();
+
+LOCAL void MQInit (void);
+LOCAL BOOL MQAvailable (void);
+LOCAL BOOL MQAdd (int mevent, int button, int nRow, int nColumn,
+ int keys, int flags);
+LOCAL BOOL MQGet (MEvent * pmouse);
+LOCAL BOOL MQClear (int flag);
+
+LOCAL UCS mswin_getc (void);
+
+LOCAL DWORD ExplainSystemErr(void);
+
+LOCAL void RestoreMouseCursor();
+
+LOCAL BYTE mswin_string2charsetid(LPTSTR);
+LOCAL int mswin_charsetid2string (LPTSTR fontCharSet,
+ size_t nfontCharSet, BYTE lfCharSet);
+
+LOCAL int mswin_tw_init(MSWIN_TEXTWINDOW *mswin_tw, int id,
+ LPCTSTR title);
+LOCAL MSWIN_TEXTWINDOW *mswin_tw_displaytext_lptstr (LPTSTR, LPTSTR, size_t,
+ LPTSTR *, MSWIN_TEXTWINDOW *mswin_tw,
+ int);
+LOCAL void mswin_tw_print_callback(MSWIN_TEXTWINDOW *mswin_tw);
+
+
+/* Functions exported to MS Windows. */
+
+LRESULT FAR PASCAL __export PWndProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export ToolBarProc (HWND, UINT, WPARAM, LPARAM);
+LRESULT FAR PASCAL __export TBBtnProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export SplashDlgProc (HWND, UINT, WPARAM, LPARAM);
+
+LOCAL HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hconv,
+ HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+ DWORD dwData1, DWORD dwData2);
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Module globals.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+PTTYINFO gpTTYInfo;
+HWND ghTTYWnd;
+HINSTANCE ghInstance;
+BOOL gfUseDialogs;
+FILE *mswin_debugfile = NULL;
+int mswin_debug = 0;
+TCHAR gszAppName[45];
+
+LOCAL TCHAR TempBuf [MAXLEN_TEMPSTR];
+
+LOCAL TCHAR gszTTYClass[] = TEXT("PineWnd");
+LOCAL int gAppIdent = APP_UNKNOWN;
+
+LOCAL int gNMW_width;
+
+LOCAL HCURSOR ghCursorArrow = NULL;
+LOCAL HCURSOR ghCursorBusy = NULL;
+LOCAL HCURSOR ghCursorIBeam = NULL;
+LOCAL HCURSOR ghCursorHand = NULL;
+LOCAL HCURSOR ghCursorCurrent = NULL;
+
+LOCAL HWND ghSplashWnd = NULL;
+
+LOCAL COLOR_PAIR *the_rev_color, *the_normal_color;
+
+/* Used for Pasting text. */
+LOCAL HANDLE ghPaste = NULL; /* Handle to Paste data. */
+LOCAL TCHAR *gpPasteNext = NULL; /* Pointer to current char. */
+LOCAL size_t gPasteBytesRemain = 0; /* Count of bytes left. */
+LOCAL BOOL gPasteWasCR = FALSE; /* Previous char was CR. */
+LOCAL int gPasteEnabled = MSWIN_PASTE_DISABLE;
+LOCAL getc_t gCopyCutFunction = NULL;
+LOCAL cbarg_t gScrollCallback = NULL;
+LOCAL BOOL gScrolling = FALSE; /* Keeps track of when we are
+ * in scroll routine. */
+LOCAL cbarg_t gSortCallback = NULL;
+LOCAL cbarg_t gFlagCallback = NULL;
+LOCAL cbarg_t gHdrCallback = NULL;
+LOCAL cbarg_t gZoomCallback = NULL;
+LOCAL cbarg_t gFkeyCallback = NULL;
+LOCAL cbarg_t gSelectedCallback = NULL;
+LOCAL BOOL gMouseTracking = FALSE; /* Keeps track of when we are
+ * tracking the mouse. */
+LOCAL FARPROC gWSBlockingProc = NULL;
+LOCAL DLGPROC gToolBarProc = NULL;
+LOCAL WNDPROC gTBBtnProc = NULL;
+
+LOCAL BOOL gAllowCopy = FALSE;
+LOCAL BOOL gAllowCut = FALSE;
+
+LOCAL BOOL gAllowMouseTrack = FALSE;/* Upper layer interested in
+ * mouse tracking. */
+LOCAL short gsMWMultiplier;
+LOCAL MEvent gMTEvent;
+
+LOCAL cbstr_t gHelpGenCallback = NULL;
+LOCAL BOOL gfHelpGenMenu = FALSE; /* TRUE when help menu
+ * installed. */
+LOCAL cbstr_t gHelpCallback = NULL;
+LOCAL BOOL gfHelpMenu = FALSE; /* TRUE when help menu
+ * installed. */
+LOCAL char *gpCloseText;
+
+LOCAL DWORD gGMLastCall = 0; /* Last time I called
+ * GetMessage. */
+LOCAL BOOL gConfirmExit = FALSE;
+LOCAL HICON ghNormalIcon = NULL;
+LOCAL HICON ghNewMailIcon = NULL;
+LOCAL HICON ghMClosedIcon = NULL;
+
+LOCAL int gPrintFontSize;
+LOCAL TCHAR gPrintFontName[LF_FACESIZE];
+LOCAL TCHAR gPrintFontStyle[64];
+LOCAL TCHAR gPrintFontCharSet[256];
+LOCAL BOOL gPrintFontSameAs = TRUE;
+
+LOCAL UINT gTimerCurrentPeriod = 0;
+
+LOCAL cbvoid_t gPeriodicCallback = NULL; /* Function to call. */
+LOCAL DWORD gPeriodicCBTimeout = 0; /* Time of next call. */
+LOCAL DWORD gPeriodicCBTime = 0; /* Delay between calls. */
+
+//=========================================================================
+// n
+//=========================================================================
+MSWIN_TEXTWINDOW gMswinAltWin = {0};
+MSWIN_TEXTWINDOW gMswinNewMailWin = {0};
+
+MSWIN_TEXTWINDOW gMswinIMAPTelem = {0};
+LOCAL cbvoid_t gIMAPDebugONCallback = NULL;
+LOCAL cbvoid_t gIMAPDebugOFFCallback = NULL;
+LOCAL cbvoid_t gEraseCredsCallback = NULL;
+LOCAL cbvoid_t gViewInWindCallback = NULL;
+
+LOCAL cbvoid_t gConfigScreenCallback = NULL;
+
+LOCAL cbarg_t gMouseTrackCallback = NULL;
+
+/* Currently only implement one SIGNAL so only need single variable. */
+LOCAL SignalType gSignalAlarm = SIG_DFL;
+LOCAL DWORD gAlarmTimeout = 0; /* Time alarm expires in
+ * seconds
+ * (GetTickCount()/1000) */
+LOCAL SignalType gSignalHUP = SIG_DFL;
+
+LOCAL IconList *gIconList = NULL;
+
+
+/*
+ * There is some coordination between these names and the similar names
+ * in osdep/unix. The names in the left column are mapped to the colors
+ * in the right column when displaying. Note that the last eight colors map
+ * to the same thing as the 1st eight colors. This is an attempt to inter-
+ * operate with 16-color xterms. When editing a color and writing the color
+ * into the config file, we use the canonical_name (middle column). So if
+ * the user chooses green from the menu we write color010 in the config
+ * file. [This has changed. Now we put the RGB value in the config file
+ * instead. We leave this so that we can interpret color010 in the config
+ * file from old versions of pine.]
+ * We display that as green later. The xterm also displays that as
+ * green, because the bright green (color010) on the xterm is what we want
+ * to consider to be green, and the other one (color002) is dark green.
+ * And dark green sucks.
+ * On the PC we only use the first 11 colors in this table when giving the
+ * user the set color display, since the last eight are repeats.
+ */
+LOCAL MSWINColor MSWINColorTable[] = {
+ "black", "000,000,000", RGB(0,0,0),
+ "red", "255,000,000", RGB(255,0,0),
+ "green", "000,255,000", RGB(0,255,0),
+ "yellow", "255,255,000", RGB(255,255,0),
+ "blue", "000,000,255", RGB(0,0,255),
+ "magenta", "255,000,255", RGB(255,0,255),
+ "cyan", "000,255,255", RGB(0,255,255),
+ "white", "255,255,255", RGB(255,255,255),
+ "colorlgr", "192,192,192", RGB(192,192,192), /* lite gray */
+ "colormgr", "128,128,128", RGB(128,128,128), /* med. gray */
+ "colordgr", "064,064,064", RGB(64,64,64), /* dark gray */
+ "color008", "000,000,000", RGB(0,0,0),
+ "color009", "255,000,000", RGB(255,0,0),
+ "color010", "000,255,000", RGB(0,255,0),
+ "color011", "255,255,000", RGB(255,255,0),
+ "color012", "000,000,255", RGB(0,0,255),
+ "color013", "255,000,255", RGB(255,0,255),
+ "color014", "000,255,255", RGB(0,255,255),
+ "color015", "255,255,255", RGB(255,255,255),
+ NULL, NULL, 0
+};
+
+#define fullColorTableSize ((sizeof(MSWINColorTable) / sizeof(MSWINColorTable[0])) - 1)
+#define visibleColorTableSize (fullColorTableSize - 8)
+
+
+LOCAL MSWinCaret_t MSWinCaretTable[] = {
+ "Block", CaretBlock,
+ "ShortBlock", CaretSmallBlock,
+ "Underline", CaretHorizBar,
+ "VertBar", CaretVertBar,
+ NULL, 0
+};
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Windows Functions.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*---------------------------------------------------------------------------
+ * int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
+ * LPSTR lpszCmdLine, int nCmdShow )
+ *
+ * Description:
+ * This is the main window loop!
+ *
+ * Parameters:
+ * As documented for all WinMain() functions.
+ *
+ *--------------------------------------------------------------------------*/
+int PASCAL
+wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPTSTR lpszCmdLine, int nCmdShow )
+{
+ CHAR **argv;
+ int argc, i, nosplash = 0;
+ char *cmdline_utf8;
+
+ ghInstance = hInstance;
+ if (!hPrevInstance)
+ if (!InitApplication( hInstance ))
+ return ( FALSE ) ;
+
+ /*
+ * Instead of crashing into Dr. Watson on invalid parameter calls
+ * into the C library, just return the error indication that these
+ * functions are normally expected to return.
+ */
+ _set_invalid_parameter_handler (mswin_invalidparameter);
+
+#ifdef OWN_DEBUG_FILE /* Want to write to seperate memdebug.txt file. */
+ mswin_debugfile = fopen ("memdebug.txt", "w");
+ fprintf (mswin_debugfile, "Beginning of mswin debug log\n");
+ if (mswin_debugfile != NULL) {
+ mswin_debug = 4;
+ MemDebug (mswin_debug, mswin_debugfile);
+ fprintf (mswin_debugfile, "Show window as: %d\n", nCmdShow);
+ fflush (mswin_debugfile);
+ }
+#endif
+
+ if (NULL == (ghTTYWnd = InitInstance (hInstance, nCmdShow)))
+ return (FALSE);
+
+ /* cmdline_utf8 memory is never freed */
+ cmdline_utf8 = lptstr_to_utf8(lpszCmdLine);
+
+ MakeArgv (hInstance, cmdline_utf8, &argc, &argv);
+
+ for(i = 0; i < argc; i++)
+ if(strcmp((const char *)argv[i], "-nosplash") == 0){
+ nosplash = 1;
+ break;
+ }
+ /* Create Splash window */
+ if(nosplash == 0){
+ ghSplashWnd = CreateDialog(hInstance,
+ MAKEINTRESOURCE( SPLASHDLGBOX ),
+ ghTTYWnd, (DLGPROC)SplashDlgProc);
+ ShowWindow (ghSplashWnd, SW_SHOWNORMAL);
+ }
+
+ atexit (WinExit);
+
+ app_main (argc, (char **)argv);
+
+ return (TRUE);
+}
+
+
+LOCAL void
+mswin_invalidparameter(const wchar_t *expression,
+ const wchar_t *function,
+ const wchar_t *file,
+ unsigned int line,
+ uintptr_t reserved)
+{
+ /* do nothing for now */
+}
+
+
+void
+WinExit (void)
+{
+ MSG msg;
+
+ if (ghTTYWnd == NULL)
+ return;
+
+ UpdateTrayIcon(NIM_DELETE, 0, NULL);
+
+ /* Destroy main window and process remaining events. */
+ DestroyWindow (ghTTYWnd);
+ while (GetMessage (&msg, NULL, 0, 0)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+#ifdef OWN_DEBUG_FILE
+ fclose (mswin_debugfile);
+#endif
+ if (gWSBlockingProc != NULL)
+ FreeProcInstance (gWSBlockingProc);
+
+ MemFreeAll ();
+ ghTTYWnd = NULL;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL InitApplication( HANDLE hInstance )
+ *
+ * Description:
+ * First time initialization stuff. This registers information
+ * such as window classes.
+ *
+ * Parameters:
+ * HANDLE hInstance
+ * Handle to this instance of the application.
+ *
+ *--------------------------------------------------------------------------*/
+LOCAL BOOL
+InitApplication (HANDLE hInstance)
+{
+ WNDCLASS wndclass;
+
+ /*
+ * Register tty window class.
+ */
+ wndclass.style = 0; /* CS_NOCLOSE; */
+ wndclass.lpfnWndProc = PWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof (LONG);
+ wndclass.hInstance = hInstance ;
+ /* In win16 we paint our own icon. */
+ wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (ALPINEICON));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wndclass.lpszMenuName = MAKEINTRESOURCE (ALPINEMENU);
+ wndclass.lpszClassName = gszTTYClass ;
+
+ return (RegisterClass (&wndclass));
+}
+
+
+/*---------------------------------------------------------------------------
+ * HWND InitInstance( HANDLE hInstance, int nCmdShow )
+ *
+ * Description:
+ * Initializes instance specific information.
+ *
+ * Parameters:
+ * HANDLE hInstance
+ * Handle to instance
+ *
+ * int nCmdShow
+ * How do we show the window?
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL HWND
+InitInstance (HANDLE hInstance, int nCmdShow)
+{
+ HWND hTTYWnd;
+ TCHAR appIdent[32];
+ SCROLLINFO scrollInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "InitInstance::: entered, nCmdShow %d\n",
+ nCmdShow);
+#endif
+
+ LoadString (hInstance, IDS_APPNAME, gszAppName, sizeof(gszAppName) / sizeof(TCHAR));
+
+ /* create the TTY window */
+ hTTYWnd = CreateWindow (gszTTYClass, gszAppName,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE | SIF_POS;
+ scrollInfo.nMin = 0;
+ scrollInfo.nMax = 1;
+ scrollInfo.nPage = 1;
+ scrollInfo.nPos = 0;
+ SetScrollInfo(hTTYWnd, SB_VERT, &scrollInfo, FALSE);
+ EnableScrollBar (hTTYWnd, SB_VERT, ESB_DISABLE_BOTH);
+
+ if (NULL == hTTYWnd)
+ return (NULL);
+
+ ghTTYWnd = hTTYWnd;
+
+ ghNormalIcon = LoadIcon (hInstance, MAKEINTRESOURCE (ALPINEICON));
+ ghNewMailIcon = LoadIcon (hInstance, MAKEINTRESOURCE (NEWMAILICON));
+ ghMClosedIcon = LoadIcon (hInstance, MAKEINTRESOURCE (MCLOSEDICON));
+
+ ghCursorArrow = LoadCursor (NULL, IDC_ARROW);
+ ghCursorBusy = LoadCursor (NULL, IDC_WAIT);
+ ghCursorIBeam = LoadCursor (NULL, IDC_IBEAM);
+#ifdef IDC_HAND
+ ghCursorHand = LoadCursor (NULL, IDC_HAND);
+#else
+ ghCursorHand = LoadCursor (hInstance, MAKEINTRESOURCE( PICOHAND ));
+#endif
+ ghCursorCurrent = ghCursorArrow;
+
+ MouseWheelMultiplier();
+
+ CQInit ();
+ MQInit ();
+
+
+ /*
+ * Load a resource with the name of the application. Compare to
+ * known applications to determine who we are running under.
+ * currently, only differentiation is the WINSOCK blocking hook.
+ */
+ LoadString (hInstance, IDS_APPIDENT, appIdent, sizeof(appIdent) / sizeof(TCHAR));
+ if (_tcscmp (appIdent, APP_PINE_IDENT) == 0) {
+ gAppIdent = APP_PINE;
+ gWSBlockingProc = MakeProcInstance ( (FARPROC) NoMsgsAreSent,
+ hInstance);
+ }
+ else if (_tcscmp (appIdent, APP_PICO_IDENT) == 0)
+ gAppIdent = APP_PICO;
+ else
+ gAppIdent = APP_UNKNOWN;
+
+
+ return (hTTYWnd);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void MakeArgv ()
+ *
+ * Description:
+ * Build a standard C argc, argv pointers into the command line string.
+ *
+ *
+ * Parameters:
+ * cmdLine - Command line.
+ * *argc - Count of words.
+ * ***argc - Pointer to Pointer to array of pointers to
+ * characters.
+ *
+ *--------------------------------------------------------------------------*/
+LOCAL void
+MakeArgv (HINSTANCE hInstance, char *cmdLine_utf8, int *pargc, CHAR ***pargv)
+{
+ CHAR **argv;
+ LPSTR c;
+ BOOL inWord, inQuote;
+ int wordCount;
+#define CMD_PATH_LEN 128
+ LPTSTR modPath_lptstr;
+ DWORD mpLen;
+
+
+ /* Count words in cmdLine. */
+ wordCount = 0;
+ inWord = FALSE;
+ inQuote = FALSE;
+ for (c = cmdLine_utf8; *c != '\0'; ++c) {
+ if (inQuote) {
+ if(*c == '"' && (*(c+1) == ' ' || *(c+1) == '\t' || *(c+1) == '\0')){
+ inQuote = inWord = FALSE;
+ }
+ }
+ else {
+ if(inWord && (*c == ' ' || *c == '\t' || *c == '\0')){
+ inWord = FALSE;
+ }
+ else if(!inWord && (*c != ' ' && *c != '\t')){
+ inWord = TRUE;
+ wordCount++;
+ if(*c == '"')
+ inQuote = TRUE;
+ }
+ }
+ }
+
+ ++wordCount; /* One for program name. */
+ argv = (CHAR **) MemAlloc (sizeof (CHAR *) * (wordCount + 1));
+ *pargv = argv;
+ *pargc = wordCount;
+
+ modPath_lptstr = (LPTSTR) MemAlloc (CMD_PATH_LEN*sizeof(TCHAR));
+ mpLen = GetModuleFileName (hInstance, modPath_lptstr, CMD_PATH_LEN);
+ if (mpLen > 0) {
+ *(modPath_lptstr + mpLen) = '\0';
+ *(argv++) = (unsigned char *)lptstr_to_utf8(modPath_lptstr);
+ }
+ else
+ *(argv++) = (unsigned char *)"Alpine/Pico";
+
+ MemFree((void *)modPath_lptstr);
+
+ /* Now break up command line. */
+ inWord = FALSE;
+ inQuote = FALSE;
+ for (c = cmdLine_utf8; *c != '\0'; ++c) {
+ if (inQuote) {
+ if(*c == '"' && (*(c+1) == ' ' || *(c+1) == '\t' || *(c+1) == '\0')){
+ inQuote = inWord = FALSE;
+ *c = '\0';
+ }
+ }
+ else {
+ if(inWord && (*c == ' ' || *c == '\t' || *c == '\0')){
+ *c = '\0';
+ inWord = FALSE;
+ }
+ else if(!inWord && (*c != ' ' && *c != '\t')){
+ inWord = TRUE;
+ if(*c == '"'){
+ inQuote = TRUE;
+ *(argv++) = (unsigned char *)c+1;
+ }
+ else
+ *(argv++) = (unsigned char *)c;
+ }
+ }
+ }
+
+ *argv = NULL; /* tie off argv */
+}
+
+
+/*---------------------------------------------------------------------------
+ * LRESULT FAR PASCAL __export PWndProc( HWND hWnd, UINT uMsg,
+ * WPARAM wParam, LPARAM lParam )
+ *
+ * Description:
+ * This is the TTY Window Proc. This handles ALL messages
+ * to the tty window.
+ *
+ * Parameters:
+ * As documented for Window procedures.
+ *
+/*--------------------------------------------------------------------------*/
+LRESULT FAR PASCAL __export
+PWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+#ifdef CDEBUG
+ if (mswin_debug > 12) {
+ fprintf (mswin_debugfile, "PWndProc:: uMsg = 0x%x\n", uMsg);
+ fflush (mswin_debugfile);
+ }
+#endif
+ switch (uMsg) {
+ case WM_CREATE:
+ MyTimerSet ();
+ return (CreateTTYInfo (hWnd));
+
+ case WM_COMMAND:
+ switch ((WORD) wParam) {
+
+ case IDM_OPT_SETFONT:
+ SelectTTYFont (hWnd);
+ break ;
+
+ case IDM_OPT_FONTSAMEAS:
+ PrintFontSameAs (hWnd);
+ break;
+
+ case IDM_OPT_TOOLBAR:
+ TBToggle (hWnd);
+ break;
+
+ case IDM_OPT_TOOLBARPOS:
+ TBPosToggle (hWnd);
+ break;
+
+ case IDM_OPT_USEDIALOGS: {
+ PTTYINFO pTTYInfo;
+
+ gfUseDialogs = !gfUseDialogs;
+ pTTYInfo = (PTTYINFO)(LONG_PTR)MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo)
+ DidResize (pTTYInfo);
+ break;
+ }
+
+ case IDM_OPT_ERASE_CREDENTIALS:
+ if(gEraseCredsCallback)
+ (*gEraseCredsCallback)();
+ break;
+
+ case IDM_MI_VIEWINWIND:
+ if(gViewInWindCallback)
+ (*gViewInWindCallback)();
+ break;
+
+ case IDM_OPT_IMAPTELEM:
+ mswin_tw_init(&gMswinIMAPTelem, (int)LOWORD(wParam),
+ TEXT("IMAP Telemetry"));
+ SetFocus (ghTTYWnd);
+ break;
+
+ case IDM_OPT_NEWMAILWIN:
+ mswin_tw_init(&gMswinNewMailWin, (int)LOWORD(wParam),
+ TEXT("New Mail View"));
+ SetFocus (ghTTYWnd);
+ break;
+
+#ifdef ACCELERATORS_OPT
+ case IDM_OPT_USEACCEL:
+ AccelCtl (hWnd, ACCEL_TOGGLE, TRUE);
+ break;
+#endif
+
+ case IDM_OPT_SETPRINTFONT:
+ PrintFontSelect (hWnd);
+ break;
+
+ case IDM_OPT_CARETBLOCK :
+ CaretTTY(hWnd, CaretBlock);
+ break;
+
+ case IDM_OPT_CARETSMALLBLOCK :
+ CaretTTY(hWnd, CaretSmallBlock);
+ break;
+
+ case IDM_OPT_CARETVBAR :
+ CaretTTY(hWnd, CaretVertBar);
+ break;
+
+ case IDM_OPT_CARETHBAR :
+ CaretTTY(hWnd, CaretHorizBar);
+ break;
+
+ case IDM_ABOUT:
+ GoModalDialogBoxParam ( GET_HINST( hWnd ),
+ MAKEINTRESOURCE( ABOUTDLGBOX ),
+ hWnd,
+ (DLGPROC)AboutDlgProc, (LPARAM) 0 ) ;
+ break;
+
+ case IDM_EDIT_CUT:
+ EditCut ();
+ break;
+
+ case IDM_EDIT_COPY:
+ EditCopy ();
+ break;
+
+ case IDM_EDIT_COPY_APPEND:
+ EditCopyAppend ();
+ break;
+
+ case IDM_EDIT_PASTE:
+ EditPaste ();
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+ break;
+
+ case IDM_EDIT_CANCEL_PASTE:
+ EditCancelPaste ();
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+ break;
+
+ case IDM_EDIT_SEL_ALL :
+ EditSelectAll();
+ break;
+
+ case IDM_HELP:
+ MSWHelpShow (gHelpCallback);
+ break;
+
+ case IDM_MI_GENERALHELP:
+ MSWHelpShow (gHelpGenCallback);
+ break;
+
+ case IDM_MI_WHEREIS :
+ CQAdd (gpTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miKey, 0);
+ break;
+
+ case IDM_MI_SORTSUBJECT :
+ case IDM_MI_SORTARRIVAL :
+ case IDM_MI_SORTSIZE :
+ case IDM_MI_SORTFROM :
+ case IDM_MI_SORTTO :
+ case IDM_MI_SORTCC :
+ case IDM_MI_SORTDATE :
+ case IDM_MI_SORTORDERSUB :
+ case IDM_MI_SORTSCORE :
+ case IDM_MI_SORTTHREAD :
+ SortHandler((int)(wParam - IDM_MI_SORTSUBJECT), 0);
+ break;
+
+ case IDM_MI_SORTREVERSE :
+ SortHandler(-1, 1);
+ break;
+
+ case IDM_MI_FLAGIMPORTANT :
+ case IDM_MI_FLAGNEW :
+ case IDM_MI_FLAGANSWERED :
+ case IDM_MI_FLAGDELETED :
+ FlagHandler((int)(wParam - IDM_MI_FLAGIMPORTANT), 0);
+ break;
+
+ default:
+ /* value falling within the menu item range are handled here. */
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND){
+ ProcessMenuItem (hWnd, wParam);
+ break;
+ }
+ break;
+ }
+ break ;
+
+ case WM_VSCROLL:
+ ScrollTTY (hWnd, LOWORD(wParam), HIWORD(wParam), (HWND) lParam);
+ break;
+
+ case WM_MOUSEWHEEL:
+ MouseWheelTTY (hWnd, LOWORD(lParam), HIWORD(lParam),
+ LOWORD(wParam), (short) HIWORD(wParam));
+ break;
+
+ case WM_ERASEBKGND:
+ if (IsIconic (hWnd))
+ return (DefWindowProc (hWnd, WM_ICONERASEBKGND, wParam, lParam));
+ else
+ EraseTTY (hWnd, (HDC) wParam);
+ break;
+
+ case WM_QUERYDRAGICON:
+ return ((LRESULT)ghNormalIcon);
+
+ case WM_PAINT:
+ PaintTTY (hWnd);
+ break ;
+
+ case WM_GETMINMAXINFO:
+ GetMinMaxInfoTTY (hWnd, (MINMAXINFO __far *)lParam);
+ break;
+
+ case WM_SIZE:
+ SizeTTY (hWnd, (int)wParam, HIWORD(lParam), LOWORD(lParam));
+ break ;
+
+ case WM_SIZING :
+ return(SizingTTY(hWnd, (int)wParam, (LPRECT) lParam));
+
+ case WM_MOVE:
+/* MoveTTY (hWnd, (int) LOWORD(lParam), (int) HIWORD(lParam)); */
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ /* Allows us to adjust new size of window. */
+ AboutToSizeTTY (hWnd, (WINDOWPOS FAR *) lParam);
+ break;
+
+
+ case TASKBAR_ICON_MESSAGE:
+ /* Notification of a mouse event in the task bar tray.
+ * If they are clicking on it we will restore the window. */
+ if (lParam == WM_LBUTTONDOWN){
+ if(gpTTYInfo->fMinimized)
+ ShowWindow (hWnd, SW_RESTORE);
+ else
+ SetForegroundWindow(hWnd);
+ }
+
+ break;
+
+
+ /*
+ * WM_KEYDOWN is sent for every "key press" and reports on the
+ * keyboard key, with out processing shift and control keys.
+ * WM_CHAR is a synthetic event, created from KEYDOWN and KEYUP
+ * events. It includes processing or control and shift characters.
+ * But does not get generated for extended keys suchs as arrow
+ * keys.
+ * I'm going to try to use KEYDOWN for processing just extended keys
+ * and let CHAR handle the the rest.
+ *
+ * The only key combo that is special is ^-space. For that, I'll use
+ * WM_KEYDOWN and WM_KEYUP to track the state of the control key.
+ */
+ case WM_CHAR:
+ /*
+ * If the windows cursor is visible and we have keyboard input
+ * then hide the windows cursor
+ */
+ mswin_showcursor(FALSE);
+ ProcessTTYCharacter (hWnd, (TCHAR)wParam);
+ break ;
+
+ case WM_KEYDOWN:
+ if (ProcessTTYKeyDown (hWnd, (TCHAR) wParam))
+ return (0);
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_SYSCHAR:
+ if (gFkeyCallback && (*gFkeyCallback)(0, 0)
+ && LOBYTE (wParam) == VK_F10){
+ ProcessTTYCharacter (hWnd, (TCHAR)wParam);
+ return (0);
+ }
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_SYSKEYDOWN:
+ /*
+ * lParam specifies the context code. Bit 29 is 1 if the ALT key is down
+ * while the key is pressed.
+ */
+ if (!(lParam & (1<<29))
+ && gFkeyCallback && (*gFkeyCallback)(0, 0)
+ && LOBYTE (wParam) == VK_F10
+ && ProcessTTYKeyDown (hWnd, (TCHAR) wParam))
+ return (0);
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+
+ case WM_LBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_LEFT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_LBUTTONUP:
+ if (ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_LEFT, LOWORD (lParam),
+ HIWORD (lParam), wParam))
+ goto callDef;
+ break;
+
+ case WM_MBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_MIDDLE, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_MBUTTONUP:
+ ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_MIDDLE, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_RBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_RIGHT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_RBUTTONUP:
+ ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_RIGHT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ ProcessTTYMouse (hWnd, M_EVENT_TRACK, 0, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_NCMOUSEMOVE:
+ mswin_showcursor(TRUE);
+ goto callDef; /* pretend it never happened */
+
+ case WM_SETFOCUS:
+ SetTTYFocus (hWnd);
+ break;
+
+ case WM_KILLFOCUS:
+ KillTTYFocus (hWnd);
+ break;
+
+ case WM_SETCURSOR:
+ /* Set cursor. If in client, leave as is. Otherwise, pass to
+ * DefWindow Proc */
+ if (LOWORD(lParam) == HTCLIENT) {
+ SetCursor (ghCursorCurrent);
+ return (TRUE);
+ }
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_INITMENU:
+ UpdateMenu (hWnd);
+ break;
+
+ case WM_TIMER:
+ /* Really just used so that we continue to receive messages even while
+ * in background. Causes mswin_getc() to process message and return
+ * to caller so that it can get some periodic processing in. */
+ ProcessTimer ();
+ break;
+
+ case WM_QUERYENDSESSION:
+ /* Returns non-zero if I can exit, otherwize zero, and the end
+ * session operation stops. */
+ return ((LRESULT)ConfirmExit ());
+
+ case WM_DESTROY:
+ KillTimer (hWnd, MY_TIMER_ID);
+ DestroyTTYInfo (hWnd);
+ PostQuitMessage (0);
+ break;
+
+
+ case WM_DROPFILES:
+ if(ProcessTTYFileDrop((HANDLE) wParam) == TRUE)
+ SetForegroundWindow(hWnd);
+
+ break;
+
+
+ case WM_CLOSE:
+ /* If the quit menu is active then insert the quit command
+ * Otherwise, abort. */
+ if (gpTTYInfo->menuItems[KS_EXIT - KS_RANGESTART].miActive) {
+ CQAdd (gpTTYInfo->menuItems[KS_EXIT - KS_RANGESTART].miKey, 0);
+ }
+ else if (gSignalHUP != SIG_DFL && gSignalHUP != SIG_IGN) {
+ if (MessageBox (hWnd,
+ TEXT("Abort PINE/PICO, possibly losing current work?"),
+ gszAppName, MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
+ HUPDeliver ();
+ }
+ break;
+
+
+ default:
+callDef:
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+ }
+ return 0L ;
+
+} /* end of PWndProc() */
+
+
+/*---------------------------------------------------------------------------
+ * LRESULT NEAR CreateTTYInfo( HWND hWnd )
+ *
+ * Description:
+ * Creates the tty information structure and sets
+ * menu option availability. Returns -1 if unsuccessful.
+ *
+ * Parameters:
+ * HWND hWnd
+ * Handle to main window.
+ *
+ *-------------------------------------------------------------------------*/
+LOCAL LRESULT NEAR
+CreateTTYInfo (HWND hWnd)
+{
+ HMENU hMenu;
+ PTTYINFO pTTYInfo;
+ LOGFONT newFont;
+ int i, ppi;
+ HDC hDC;
+ HFONT testFont;
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "CreateTTYInfo::: entered\n");
+#endif
+
+ hDC = GetDC (ghTTYWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+
+ pTTYInfo = (PTTYINFO) MemAlloc (sizeof (TTYINFO));
+ if (pTTYInfo == NULL)
+ return ((LRESULT) - 1);
+ gpTTYInfo = pTTYInfo;
+
+ /* initialize TTY info structure */
+ memset (pTTYInfo, 0, sizeof (TTYINFO));
+ /* Shown but not focused. */
+ pTTYInfo->cCaretStyle = CaretBlock;
+ pTTYInfo->fMinimized = FALSE;
+ pTTYInfo->fMaximized = FALSE;
+ pTTYInfo->fFocused = FALSE;
+ pTTYInfo->fNewLine = FALSE;
+ pTTYInfo->fMassiveUpdate = FALSE;
+ pTTYInfo->fNewMailIcon = FALSE;
+ pTTYInfo->fMClosedIcon = FALSE;
+ pTTYInfo->autoWrap = WRAP_NO_SCROLL;
+ pTTYInfo->xOffset = MARGINE_LEFT;
+ pTTYInfo->yOffset = MARGINE_TOP;
+ pTTYInfo->fDesiredSize = FALSE;
+ pTTYInfo->fCursorOn = TRUE;
+ pico_nfcolor(NULL);
+ pico_nbcolor(NULL);
+ pico_rfcolor(NULL);
+ pico_rbcolor(NULL);
+ pico_set_normal_color();
+ pTTYInfo->toolBarTop = TRUE;
+ pTTYInfo->curToolBarID = IDD_TOOLBAR;
+
+ /* Clear menu item array. */
+ pTTYInfo->curWinMenu = ALPINEMENU;
+ for (i = 0; i < KS_COUNT; ++i)
+ pTTYInfo->menuItems[i].miActive = FALSE;
+ pTTYInfo->menuItemsCurrent = FALSE;
+
+ /* Clear resize callback procs. */
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i)
+ pTTYInfo->resizer[i] = NULL;
+
+
+ /* clear screen space */
+ pTTYInfo->pScreen = NULL;
+ pTTYInfo->pCellWidth = NULL;
+ pTTYInfo->pAttrib = NULL;
+
+ /* setup default font information */
+
+ newFont.lfHeight = -MulDiv(12, ppi, 72);
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfWeight = 0;
+ newFont.lfItalic = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfCharSet = FONT_CHARSET_FONT;
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
+ _sntprintf(newFont.lfFaceName, LF_FACESIZE, TEXT("%s"), TEXT("Courier New"));
+ testFont = CreateFontIndirect(&newFont);
+ if(NULL == testFont)
+ newFont.lfFaceName[0] = '\0';
+ else
+ DeleteObject(testFont);
+
+ /* set TTYInfo handle before any further message processing. */
+
+ MySetWindowLongPtr (hWnd, GWL_PTTYINFO, pTTYInfo);
+
+ /* reset the character information, etc. */
+
+ ResetTTYFont (hWnd, pTTYInfo, &newFont);
+
+
+
+ hMenu = GetMenu (hWnd);
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND | MF_GRAYED);
+ return ((LRESULT) TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL NEAR DestroyTTYInfo( HWND hWnd )
+ *
+ * Description:
+ * Destroys block associated with TTY window handle.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ *-------------------------------------------------------------------------*/
+LOCAL BOOL NEAR
+DestroyTTYInfo (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+#ifdef ACCELERATORS
+ if(pTTYInfo->hAccel){
+ DestroyAcceleratorTable(pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ pTTYInfo->fAccel = EM_NONE;
+ }
+#endif
+
+ if(pTTYInfo->hTBWnd != NULL)
+ DestroyWindow (pTTYInfo->hTBWnd);
+
+ if(pTTYInfo->hTBBrush != NULL)
+ DeleteObject(pTTYInfo->hTBBrush);
+
+ DeleteObject (pTTYInfo->hTTYFont);
+
+ MemFree (pTTYInfo);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void ResizeTTYScreen( HWND hWnd, PTTYINFO pTTYInfo,
+ * int newNrow, int newNColumn);
+ *
+ * Description:
+ * Resize the screen to new size, copying data.
+ *
+ * Parameters:
+ * PTTYINFO pTTYInfo
+ * pointer to TTY info structure
+ * newNCo.umn, newNRow
+ * new size of screen.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ResizeTTYScreen (HWND hWnd, PTTYINFO pTTYInfo, int newNRow, int newNColumn)
+{
+ CharAttrib *pNewAttrib, tmpAttrib, *pSourceAtt, *pDestAtt;
+ TCHAR *pNewScreen, *pSource, *pDest;
+ int *pNewCW, *pSourceCW, *pDestCW;
+ size_t len;
+ int cells;
+ int r, i;
+ extern TERM term;
+
+
+ if (newNColumn < MINNCOLUMN)
+ newNColumn = MINNCOLUMN;
+ if (newNRow < MINNROW)
+ newNRow = MINNROW;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "ResizeTTYScreen::: entered, new row %d, col %d\n",
+ newNRow, newNColumn);
+#endif
+
+
+ SelClear ();
+ cells = newNColumn * newNRow;
+ pNewScreen = (TCHAR *)MemAlloc (cells * sizeof (TCHAR));
+ if (pNewScreen == NULL)
+ return (FALSE);
+
+ pNewCW = (int *)MemAlloc(cells * sizeof(int));
+ if(pNewCW == NULL){
+ MemFree((void *)pNewScreen);
+ return(FALSE);
+ }
+
+ pNewAttrib = (CharAttrib *)MemAlloc (cells * sizeof (CharAttrib));
+ if (pNewAttrib == NULL) {
+ MemFree ((void *)pNewScreen);
+ MemFree ((void *)pNewCW);
+ return (FALSE);
+ }
+
+
+ /*
+ * Clear new screen.
+ */
+
+ for(i = 0; i < cells; i++){
+ pNewScreen[i] = ' ';
+ pNewCW[i] = pTTYInfo->xChar; /* xChar set yet ? */
+ }
+
+ tmpAttrib.style = CHAR_ATTR_NORM;
+ tmpAttrib.rgbFG = pTTYInfo->rgbFGColor;
+ tmpAttrib.rgbBG = pTTYInfo->rgbBGColor;
+ for(r = 0; r < cells; r++)
+ pNewAttrib[r] = tmpAttrib;
+
+ /*
+ * Copy old screen onto new screen.
+ */
+ if (pTTYInfo->pScreen != NULL) {
+
+ for (r = 1; r <= newNRow && r <= pTTYInfo->actNRow; ++r) {
+ pSource = pTTYInfo->pScreen + ((pTTYInfo->actNRow - r) *
+ pTTYInfo->actNColumn);
+ pDest = pNewScreen + ((newNRow - r) * newNColumn);
+ len = MIN (newNColumn, pTTYInfo->actNColumn);
+ for(i = 0; i < len; i++)
+ pDest[i] = pSource[i];
+
+ pSourceCW = pTTYInfo->pCellWidth
+ + ((pTTYInfo->actNRow - r) * pTTYInfo->actNColumn);
+ pDestCW = pNewCW + ((newNRow - r) * newNColumn);
+ memcpy(pDestCW, pSourceCW, len * sizeof(int));
+
+ pSourceAtt = pTTYInfo->pAttrib
+ + ((pTTYInfo->actNRow - r) * pTTYInfo->actNColumn);
+ pDestAtt = pNewAttrib + ((newNRow - r) * newNColumn);
+ len = MIN (newNColumn, pTTYInfo->actNColumn);
+ memcpy (pDestAtt, pSourceAtt, len * sizeof(CharAttrib));
+ }
+
+ pTTYInfo->nColumn = (CORD)MIN (pTTYInfo->nColumn, newNColumn);
+ pTTYInfo->nRow = (CORD)MAX (0,
+ pTTYInfo->nRow + (newNRow - pTTYInfo->actNRow));
+ MemFree (pTTYInfo->pScreen);
+ MemFree (pTTYInfo->pCellWidth);
+ MemFree (pTTYInfo->pAttrib);
+ }
+ else {
+ pTTYInfo->nColumn = (CORD)MIN (pTTYInfo->nColumn, newNColumn);
+ pTTYInfo->nRow = (CORD)MIN (pTTYInfo->nRow, newNRow);
+ }
+
+ pTTYInfo->pScreen = pNewScreen;
+ pTTYInfo->pCellWidth = pNewCW;
+ pTTYInfo->pAttrib = pNewAttrib;
+ pTTYInfo->actNColumn = newNColumn;
+ pTTYInfo->actNRow = newNRow;
+
+
+ /* Repaint whole screen. */
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+
+
+ /* Pico specific. */
+ if (term.t_nrow == 0) {
+ term.t_nrow = (short)(newNRow - 1);
+ term.t_ncol = (short)newNColumn;
+ }
+
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ResetTTYFont( HWND hWnd, PTTYINFO pTTYInfo, LOGFONT *newFont)
+ *
+ * Description:
+ * Resets the TTY character information and causes the
+ * screen to resize to update the scroll information.
+ *
+ * Parameters:
+ * PTTYINFO pTTYInfo
+ * pointer to TTY info structure
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ResetTTYFont (HWND hWnd, PTTYINFO pTTYInfo, LOGFONT *newFont)
+{
+ HDC hDC;
+ HFONT hFont;
+ TEXTMETRIC tm;
+ int newNRow;
+ int newNColumn;
+ BOOL newsize;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "ResetTTYFont::: entered, curent window size X %d, Y %d\n",
+ pTTYInfo->xSize, pTTYInfo->ySize);
+#endif
+
+
+ if (NULL == pTTYInfo)
+ return (FALSE);
+
+ SelClear ();
+
+ /*
+ * Create new font.
+ */
+ hFont = CreateFontIndirect (newFont);
+ if (hFont == NULL)
+ return (FALSE);
+ hDC = GetDC (hWnd);
+ SelectObject (hDC, hFont);
+ GetTextMetrics (hDC, &tm);
+ ReleaseDC (hWnd, hDC);
+
+
+ /*
+ * Replace old font.
+ */
+ if (NULL != pTTYInfo->hTTYFont)
+ DeleteObject (pTTYInfo->hTTYFont);
+ pTTYInfo->hTTYFont = hFont;
+ memcpy (&pTTYInfo->lfTTYFont, newFont, sizeof (LOGFONT));
+
+
+ /* Update the char cell size. */
+ pTTYInfo->xChar = (CORD)tm.tmAveCharWidth;
+ pTTYInfo->yChar = (CORD)(tm.tmHeight + tm.tmExternalLeading);
+
+ /* Update the current number of rows and cols. Don't allow
+ * either to be less than zero. */
+ newNRow = MAX (MINNROW,
+ MIN (MAXNROW,
+ (pTTYInfo->ySize - pTTYInfo->toolBarSize - (2 * MARGINE_TOP))/
+ pTTYInfo->yChar));
+ newNColumn = MAX (MINNCOLUMN,
+ MIN (MAXNCOLUMN, (pTTYInfo->xSize - (2 * pTTYInfo->xOffset))/
+ pTTYInfo->xChar));
+
+ newsize = newNRow != pTTYInfo->actNRow ||
+ newNColumn != pTTYInfo->actNColumn;
+ if (newsize)
+ ResizeTTYScreen (hWnd, pTTYInfo, newNRow, newNColumn);
+
+ /* Resize the caret as well. */
+ if(pTTYInfo->fCaretOn)
+ HideCaret (hWnd);
+
+ DestroyCaret ();
+ CaretCreateTTY (hWnd);
+
+ /* Redraw screen and, if the "size" changed, tell the upper layers. */
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+ /* Always call the resize functions - even if the screen size
+ * has not changed, the font style may have. */
+ DidResize (pTTYInfo);
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL EraseTTY (HWND hWnd, HDC hDC)
+ *
+ * Description:
+ * Erase the tty background.
+ *
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window (as always)
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+EraseTTY (HWND hWnd, HDC hDC)
+{
+ RECT erect;
+ HBRUSH hBrush;
+
+
+ GetClientRect (hWnd, &erect);
+ hBrush = CreateSolidBrush (gpTTYInfo->rgbBGColor);
+ if (hBrush != NULL) {
+ FillRect (hDC, &erect, hBrush);
+ DeleteObject (hBrush);
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL PaintTTY( HWND hWnd )
+ *
+ * Description:
+ * Paints the rectangle determined by the paint struct of
+ * the DC.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window (as always)
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+PaintTTY (HWND hWnd)
+{
+ int nRow, nCol; /* Top left corner of update. */
+ int nEndRow, nEndCol; /* lower right corner of update. */
+ int nHorzPos, nVertPos; /* Position of each text write. */
+ int col; /* start col of run of similar attr */
+ int count; /* count of run of similar attrib. */
+ int endCount; /* How far to count. */
+ CharAttrib *pAttrib;
+ HDC hDC;
+ LOGFONT tmpFont;
+ HFONT hOrigFont, hOldFont = NULL, hTmpFont;
+ PTTYINFO pTTYInfo;
+ PAINTSTRUCT ps;
+ RECT rect;
+ RECT erect;
+ HBRUSH hBrush;
+ long offset; /* Offset into pScreen array */
+ long endoffset; /* Offset of nEndCol in each row array */
+ CharAttrib *pLastAttrib; /* Attributes of last text write. */
+ CharAttrib *pNewAttrib; /* Attributes of this text write. */
+
+
+#ifdef CDEBUG
+ if (mswin_debug >= 9)
+ fprintf (mswin_debugfile, "PaintTTY::: entered\n");
+#endif
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ if (IsIconic (hWnd))
+ return (TRUE);
+
+ hDC = BeginPaint (hWnd, &ps);
+ rect = ps.rcPaint;
+
+ hOrigFont = SelectObject (hDC, pTTYInfo->hTTYFont);
+ SetTextColor (hDC, pTTYInfo->rgbFGColor);
+ SetBkColor (hDC, pTTYInfo->rgbBGColor);
+ SetBkMode (hDC, OPAQUE);
+
+ nRow = (rect.top - pTTYInfo->yOffset) / pTTYInfo->yChar;
+ CONSTRAIN (nRow, 0, pTTYInfo->actNRow - 1);
+
+ nEndRow = MIN(pTTYInfo->actNRow - 1,
+ ((rect.bottom - pTTYInfo->yOffset - 1) / pTTYInfo->yChar));
+ nCol = MIN(pTTYInfo->actNColumn - 1,
+ MAX(0, (rect.left - pTTYInfo->xOffset) / pTTYInfo->xChar));
+ nEndCol = MIN(pTTYInfo->actNColumn - 1,
+ ((rect.right - pTTYInfo->xOffset - 1) / pTTYInfo->xChar));
+
+ pLastAttrib = NULL;
+
+ /* Erase screen if necessary. */
+ if (pTTYInfo->eraseScreen) {
+ erect.top = 0;
+ erect.left = 0;
+ erect.bottom = pTTYInfo->ySize;
+ erect.right = pTTYInfo->xSize;
+ hBrush = CreateSolidBrush (pTTYInfo->rgbBGColor);
+ if (hBrush != NULL) {
+ FillRect (hDC, &erect, hBrush);
+ DeleteObject (hBrush);
+ }
+ pTTYInfo->eraseScreen = FALSE;
+ }
+
+
+ /* Paint an inset frame around the text region. */
+ if (pTTYInfo->toolBarSize == 0) {
+ erect.top = 0;
+ erect.bottom = pTTYInfo->ySize;
+ }
+ else if (pTTYInfo->toolBarTop) {
+ erect.top = pTTYInfo->toolBarSize;
+ erect.bottom = pTTYInfo->ySize;
+ }
+ else {
+ erect.top = 0;
+ erect.bottom = pTTYInfo->ySize - pTTYInfo->toolBarSize;
+ }
+ erect.left = 0;
+ erect.right = pTTYInfo->xSize;
+ FrameRect3D (hDC, &erect, FRAME_3D_SIZE, FALSE);
+
+ /* Paint rows of text. */
+ for (; nRow <= nEndRow; nRow++) {
+ nVertPos = (nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.top = nVertPos;
+ rect.bottom = nVertPos + pTTYInfo->yChar;
+
+ /* Paint runs of similar attributes. */
+ col = nCol; /* Start at left. */
+
+ if(col == 0 && MSWIconPaint(nRow, hDC))
+ col += 2;
+
+ /*
+ * col is the column on the screen, not the index
+ * into the array.
+ */
+ while (col <= nEndCol) { /* While not past right. */
+
+ /* Starting with Character at nRow, col, what is its attribute? */
+
+ /* offset is an index into the array */
+ offset = pscreen_offset_from_cord(nRow, col, pTTYInfo);
+ pNewAttrib = pTTYInfo->pAttrib + offset;
+
+ hTmpFont = NULL;
+ if (!(pLastAttrib
+ && pNewAttrib->style == pLastAttrib->style
+ && pNewAttrib->rgbFG == pLastAttrib->rgbFG
+ && pNewAttrib->rgbBG == pLastAttrib->rgbBG)) {
+ /*
+ * Require new font?
+ */
+ if(!pLastAttrib
+ || (pNewAttrib->style & CHAR_ATTR_ULINE)
+ != (pLastAttrib->style & CHAR_ATTR_ULINE)){
+ if(pNewAttrib->style & CHAR_ATTR_ULINE){
+ /*
+ * Find suitable attribute font...
+ */
+ memcpy (&tmpFont, &pTTYInfo->lfTTYFont,
+ sizeof (LOGFONT));
+
+ tmpFont.lfHeight = - pTTYInfo->yChar;
+ tmpFont.lfWidth = - pTTYInfo->xChar;
+
+ tmpFont.lfUnderline = (BYTE)((pNewAttrib->style
+ & CHAR_ATTR_ULINE)
+ == CHAR_ATTR_ULINE);
+
+ hTmpFont = CreateFontIndirect (&tmpFont);
+
+ hOldFont = SelectObject (hDC, hTmpFont);
+ }
+ }
+
+ /*
+ * Set new color attributes. If Reverse or Selected, then
+ * show in reverse colors. But if neither, or both, then
+ * normal colors.
+ */
+ if(pNewAttrib->style & CHAR_ATTR_SEL){
+ SetTextColor (hDC, pNewAttrib->rgbBG);
+ SetBkColor (hDC, pNewAttrib->rgbFG);
+ }
+ else {
+ if(!(pLastAttrib
+ && pNewAttrib->rgbFG == pLastAttrib->rgbFG)
+ || (pLastAttrib->style & CHAR_ATTR_SEL))
+ SetTextColor (hDC, pNewAttrib->rgbFG);
+
+ if(!(pLastAttrib
+ && pNewAttrib->rgbBG == pLastAttrib->rgbBG)
+ || (pLastAttrib->style & CHAR_ATTR_SEL))
+ SetBkColor (hDC, pNewAttrib->rgbBG);
+ }
+ }
+
+ /* Find run of similar attributes. */
+ count = 1;
+ pAttrib = pTTYInfo->pAttrib + (offset + 1);
+ /* endoffset is an index into the pScreen array */
+ endoffset = pscreen_offset_from_cord(nRow, nEndCol, pTTYInfo);
+ endCount = endoffset - offset;
+ while (count <= endCount
+ && pAttrib->style == pNewAttrib->style
+ && pAttrib->rgbFG == pNewAttrib->rgbFG
+ && pAttrib->rgbBG == pNewAttrib->rgbBG){
+ ++pAttrib;
+ ++count;
+ }
+
+ if(hTmpFont != NULL){
+/* BUG: compute new offsets based on hTmpFont font if required */
+ nHorzPos = (col * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.left = nHorzPos;
+ rect.right = nHorzPos + pTTYInfo->xChar * scrwidth(pTTYInfo->pScreen+offset, count);
+ }
+ else{
+ /* Paint run of characters from nRow, col to nRow, col + count
+ * rect.top and rect.bottom have already been calculated. */
+ nHorzPos = (col * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.left = nHorzPos;
+ rect.right = nHorzPos + pTTYInfo->xChar * scrwidth(pTTYInfo->pScreen+offset, count);
+ }
+
+ ExtTextOut (hDC, nHorzPos, nVertPos, ETO_OPAQUE | ETO_CLIPPED,
+ &rect, (LPTSTR) (pTTYInfo->pScreen + offset),
+ count, (int *)(pTTYInfo->pCellWidth+offset));
+
+ /* Overstrike bold chars by hand to preserve char cell size */
+ if(pNewAttrib->style & CHAR_ATTR_BOLD){
+ int old_mode = GetBkMode(hDC);
+ SetBkMode (hDC, TRANSPARENT);
+ ExtTextOut (hDC, nHorzPos + 1, nVertPos, 0,
+ &rect, (LPTSTR) (pTTYInfo->pScreen + offset),
+ count, (int *)(pTTYInfo->pCellWidth+offset));
+ if(old_mode)
+ SetBkMode (hDC, old_mode);
+ }
+
+ /* Move pointer to end of this span of characters. */
+ col += MAX(scrwidth(pTTYInfo->pScreen+offset, count), 1);
+ pLastAttrib = pNewAttrib;
+
+ if(hTmpFont != NULL){
+ SelectObject(hDC, hOldFont);
+ DeleteObject(hTmpFont);
+ }
+ }
+ }
+
+ SelectObject (hDC, hOrigFont);
+ EndPaint (hWnd, &ps);
+ MoveTTYCursor (hWnd);
+ pTTYInfo->screenDirty = FALSE;
+ return (TRUE);
+}
+
+
+/* FillRectColor
+ *
+ *
+ * Description:
+ * FillRectColor is similar to PatB in toolbar.c
+ *
+ * Code based on MFC source code, so presumably efficient.
+ *
+ */
+LOCAL void
+FillRectColor(HDC hDC, RECT * pRC, COLORREF color)
+{
+ SetBkColor(hDC, color);
+ ExtTextOut(hDC, 0, 0, ETO_OPAQUE, pRC, NULL, 0, NULL);
+}
+
+
+/** FrameRect3D
+ *
+ *
+ * Inputs:
+ * hdc - HDC
+ * pRC - pointer to rectangle
+ * width - width for frame (usually one)
+ * raised - TRUE for raised effect, FALSE for sunken effect
+ *
+ * Outputs:
+ * none
+ *
+ * Returns:
+ * void
+ *
+ * Description
+ * Draws a frame with a 3D effect.
+ *
+ * If 'raised' is true, the rectangle will look raised (like
+ * a button); otherwise, the rectangle will look sunk.
+ *
+ */
+void
+FrameRect3D(HDC hdc, RECT * pRC, int width, BOOL raised)
+{
+ COLORREF hilite, shadow;
+ RECT rcTemp;
+
+ shadow = GetSysColor(COLOR_BTNSHADOW);
+ hilite = GetSysColor(COLOR_BTNHIGHLIGHT);
+
+ rcTemp = *pRC;
+
+ rcTemp.right = rcTemp.left + width;
+ FillRectColor(hdc, &rcTemp, raised ? hilite : shadow);
+ rcTemp.right = pRC->right;
+
+ rcTemp.bottom = rcTemp.top + width;
+ FillRectColor(hdc, &rcTemp, raised ? hilite : shadow);
+ rcTemp.bottom = pRC->bottom;
+
+ rcTemp.left = rcTemp.right - width;
+ FillRectColor(hdc, &rcTemp, raised ? shadow : hilite);
+ rcTemp.left = pRC->left;
+
+ rcTemp.top = rcTemp.bottom - width;
+ FillRectColor(hdc, &rcTemp, raised ? shadow : hilite);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL GetMinMaxInfoTTY (HWND hWnd, (MINMAXINFO __far *)lParam)
+ *
+ * Description:
+ * Return the min and max size that the window can be.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * MINMAXINFO
+ * Info structure that Windows would like us to fill.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+GetMinMaxInfoTTY (HWND hWnd, MINMAXINFO __far *lpmmi)
+{
+ PTTYINFO pTTYInfo;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "GetMinMaxInfoTTY::: entered\n");
+#endif
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ lpmmi->ptMaxTrackSize.x = lpmmi->ptMaxSize.x = MIN (lpmmi->ptMaxSize.x,
+ pTTYInfo->xChar * MAXNCOLUMN + WIN_X_BORDER_SIZE);
+ lpmmi->ptMaxTrackSize.y = lpmmi->ptMaxSize.y = MIN (lpmmi->ptMaxSize.y,
+ pTTYInfo->yChar * MAXNROW + WIN_Y_BORDER_SIZE);
+
+ lpmmi->ptMinTrackSize.x = MAX (WIN_MIN_X_SIZE,
+ pTTYInfo->xChar * MINNCOLUMN + WIN_X_BORDER_SIZE);
+ lpmmi->ptMinTrackSize.y = MAX (WIN_MIN_Y_SIZE,
+ pTTYInfo->yChar * MINNROW + WIN_Y_BORDER_SIZE);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos)
+ *
+ * Description:
+ * Called just before Windows resizes our window. We can change the
+ * values in 'winPos' to change the new size of the window.
+ *
+ * If mswin_setwindow() was called when the window was minimized we
+ * set the new size here.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD wVertSize
+ * new vertical size
+ *
+ * WORD wHorzSize
+ * new horizontal size
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "AboutToSizeTTY::: After x%lx, pos %d, %d, size %d, %d, flags x%x\n",
+ winPos->hwndInsertAfter, winPos->x, winPos->y, winPos->cx,
+ winPos->cy, winPos->flags);
+
+#endif
+
+ /*
+ * Was the window minimized AND is there a desired new size for it?
+ * AND is this a call that specifies a new size and position.
+ */
+ if (pTTYInfo->fMinimized && pTTYInfo->fDesiredSize &&
+ (winPos->flags & (SWP_NOSIZE | SWP_NOMOVE)) == 0) {
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "AboutToSizeTTY::: substitue pos (%d, %d), size (%d, %d)\n",
+ pTTYInfo->xDesPos, pTTYInfo->yDesPos,
+ pTTYInfo->xDesSize, pTTYInfo->yDesSize);
+#endif
+ pTTYInfo->fDesiredSize = FALSE;
+ winPos->x = pTTYInfo->xDesPos;
+ winPos->y = pTTYInfo->yDesPos;
+ winPos->cx = pTTYInfo->xDesSize;
+ winPos->cy = pTTYInfo->yDesSize;
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SizeTTY( HWND hWnd, int fwSizeType, CORD wVertSize,
+ * CORD wHorzSize)
+ *
+ * Description:
+ * Sizes TTY and sets up scrolling regions.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD wVertSize
+ * new vertical size
+ *
+ * WORD wHorzSize
+ * new horizontal size
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SizeTTY (HWND hWnd, int fwSizeType, CORD wVertSize, CORD wHorzSize)
+{
+ PTTYINFO pTTYInfo;
+ int newNColumn;
+ int newNRow;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "SizeTTY::: entered, sizeType %d, New screen size %d, %d pixels\n",
+ fwSizeType, wHorzSize, wVertSize);
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+
+ /*
+ * Is the window being minimized or maximized?
+ */
+ switch (fwSizeType) {
+ case SIZE_MINIMIZED:
+ pTTYInfo->fMinimized = TRUE;
+ pTTYInfo->fMaximized = FALSE;
+ return (TRUE);
+ case SIZE_MAXIMIZED:
+ pTTYInfo->fMinimized = FALSE;
+ pTTYInfo->fMaximized = TRUE;
+ break;
+ default:
+ pTTYInfo->fMinimized = pTTYInfo->fMaximized = FALSE;
+ break;
+ }
+
+ pTTYInfo->ySize = (CORD) wVertSize;
+ newNRow = MAX(MINNROW, MIN(MAXNROW,
+ (pTTYInfo->ySize - pTTYInfo->toolBarSize - (2 * MARGINE_TOP)) /
+ pTTYInfo->yChar));
+ if (pTTYInfo->toolBarTop)
+ pTTYInfo->yOffset = MARGINE_TOP + pTTYInfo->toolBarSize;
+ else
+ pTTYInfo->yOffset = MARGINE_TOP;
+
+
+ pTTYInfo->xSize = (CORD) wHorzSize;
+ newNColumn = MAX(MINNCOLUMN,
+ MIN(MAXNCOLUMN, (pTTYInfo->xSize - (2 * MARGINE_LEFT)) /
+ pTTYInfo->xChar));
+ pTTYInfo->xOffset = MARGINE_LEFT;
+
+ if(newNRow == pTTYInfo->actNRow && newNColumn == pTTYInfo->actNColumn)
+ return(FALSE);
+
+ ResizeTTYScreen (hWnd, pTTYInfo, newNRow, newNColumn);
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+ if (pTTYInfo->hTBWnd) {
+ if (pTTYInfo->toolBarTop)
+ /* Position at top of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, 0,
+ wHorzSize, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ else
+ /* Position at bottom of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, pTTYInfo->ySize - pTTYInfo->toolBarSize,
+ wHorzSize, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ }
+
+
+ DidResize (pTTYInfo);
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SizingTTY( HWND hWnd, int fwSide, LPRECT pRect)
+ *
+ * Description:
+ * Snaps the drag rectangle to char width/height boundaries
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD fwSide
+ * edge of window being sized
+ *
+ * LPRECT
+ * screen coords of drag rectangle in and desired size on return
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SizingTTY (HWND hWnd, int fwSide, LPRECT pRect)
+{
+ PTTYINFO pTTYInfo;
+ int newNRow, newNCol, xClient, yClient,
+ xSys, ySys, xDiff, yDiff;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ xSys = (2 * GetSystemMetrics(SM_CXSIZEFRAME))
+ + GetSystemMetrics(SM_CXVSCROLL);
+ ySys = (2 * GetSystemMetrics(SM_CYSIZEFRAME))
+ + GetSystemMetrics(SM_CYCAPTION)
+ + GetSystemMetrics(SM_CYMENU);
+
+ newNCol = (((pRect->right - pRect->left) - xSys)
+ - (2 * MARGINE_LEFT)) / pTTYInfo->xChar;
+ newNRow = (((pRect->bottom - pRect->top) - ySys) - (2 * MARGINE_TOP)
+ - pTTYInfo->toolBarSize) / pTTYInfo->yChar;
+
+ xClient = (newNCol * pTTYInfo->xChar) + (2 * MARGINE_LEFT);
+ yClient = (newNRow * pTTYInfo->yChar) + (2 * MARGINE_TOP)
+ + pTTYInfo->toolBarSize;
+
+ xDiff = (pRect->left + xClient + xSys) - pRect->right;
+ yDiff = (pRect->top + yClient + ySys) - pRect->bottom;
+
+ if(!(xDiff || yDiff))
+ return(FALSE);
+
+ switch(fwSide){
+ case WMSZ_BOTTOM : /* Bottom edge */
+ pRect->bottom += yDiff;
+ break;
+
+ case WMSZ_BOTTOMLEFT : /*Bottom-left corner */
+ pRect->bottom += yDiff;
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_BOTTOMRIGHT : /* Bottom-right corner */
+ pRect->bottom += yDiff;
+ pRect->right += xDiff;
+ break;
+
+ case WMSZ_LEFT : /* Left edge */
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_RIGHT : /* Right edge */
+ pRect->right += xDiff;
+ break;
+
+ case WMSZ_TOP : /* Top edge */
+ pRect->top -= yDiff;
+ break;
+
+ case WMSZ_TOPLEFT : /* Top-left corner */
+ pRect->top -= yDiff;
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_TOPRIGHT : /* Top-right corner */
+ pRect->top -= yDiff;
+ pRect->right += xDiff;
+ break;
+
+ default :
+ break;
+ }
+
+ if(!(newNRow == pTTYInfo->actNRow && newNCol == pTTYInfo->actNColumn))
+ SizeTTY(hWnd, SIZE_RESTORED, (CORD) yClient, (CORD) xClient);
+
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MoveTTY (HWND hWnd, int xPos, int yPos)
+ *
+ * Description:
+ * Notes the fact that the window has moved.
+ * Only real purpose is so we can tell pine which can the write the
+ * new window position to the 'pinerc' file.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * int xPos, yPos
+ * New position of the top left corner.
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+MoveTTY (HWND hWnd, int xPos, int yPos)
+{
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "MoveTTY::: entered\n");
+#endif
+
+ DidResize (gpTTYInfo);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void ScrollTTY ()
+ *
+ * Description:
+ * Respond to a scroll message by either calling the scroll
+ * callback or inserting a scroll character into the input
+ * stream.
+ *
+ * Scrolling in the TTY window is complicated by the way pine
+ * process events. Normal windows applications are entirly event
+ * driven. The top level does nothing but dispatch events. In
+ * pine, the top level implements the logic. Events are only
+ * dispatched by the lowest levels.
+ *
+ * In normal applications, mouse down in the scroll bar causes
+ * an internal scroll function to be entered. It tracks the
+ * mouse and issues scroll messages as needed. If the
+ * application redraws the screen the scroll function also
+ * dispatches the WM_PAINT message to the application. The
+ * important thing is that this internal scroll function does
+ * not exit until the mouse is released.
+ *
+ * We implement two methods for pine's screen managers to deal
+ * with scroll events. They can receive scroll events as
+ * characters in the normal input stream or they can register a
+ * callback function.
+ *
+ * In the "insert a character in the queue" mode, the scroll
+ * event never gets process until the mouse is release. Auto
+ * repeat scroll events (generated as the mouse is held down)
+ * will cause multiple chars to be inserted in the queue, none
+ * of which will get processed till the mouse is release. In a
+ * compromise, we allow only one scroll char in the queue,
+ * which prevents makes for a more friendly and controllable
+ * behavior.
+ *
+ * In the callback mode, the callback repaints the screen, and
+ * then it calls mswin_flush() which PROCESSES EVENTS! The
+ * Windows internal scroll function does NOT expect that. This
+ * behavior can confuses the scroll function, causing it to
+ * miss mouse up events. We avoid this by setting gScrolling TRUE
+ * when this routine is entered and FALSE when this routine exits
+ * All PeekMessage processors avoid processing any message when
+ * gScrolling is TRUE.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+ScrollTTY (HWND hWnd, int wScrollCode, int nPos, HWND hScroll)
+{
+ PTTYINFO pTTYInfo;
+ int cmd = 0;
+ long scroll_pos = 0;
+ BOOL noAction = FALSE;
+ BOOL didScroll;
+ FARPROC prevBlockingProc;
+
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+
+ if (pTTYInfo == NULL || gScrolling)
+ return;
+
+ gScrolling = TRUE;
+ if (gWSBlockingProc != NULL)
+ prevBlockingProc = WSASetBlockingHook (gWSBlockingProc);
+
+
+
+
+ switch (wScrollCode) {
+ case SB_BOTTOM:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = 0;
+ break;
+
+ case SB_TOP:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = pTTYInfo->scrollRange;
+ break;
+
+ case SB_LINEDOWN:
+ cmd = MSWIN_KEY_SCROLLDOWNLINE;
+ scroll_pos = 1;
+ break;
+
+ case SB_LINEUP:
+ cmd = MSWIN_KEY_SCROLLUPLINE;
+ scroll_pos = 1;
+ break;
+
+ case SB_PAGEDOWN:
+ cmd = MSWIN_KEY_SCROLLDOWNPAGE;
+ scroll_pos = 1;
+ break;
+
+ case SB_PAGEUP:
+ cmd = MSWIN_KEY_SCROLLUPPAGE;
+ scroll_pos = 1;
+ break;
+
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = (long) ((float)nPos);
+ break;
+
+ default:
+ noAction = TRUE;
+ break;
+ }
+
+
+ /*
+ * If there is a scroll callback call that. If there is no scroll
+ * callback or the callback says it did not handle the event (returned,
+ * FALSE) queue the scroll cmd.
+ */
+ if (!noAction) {
+ SelClear ();
+ didScroll = FALSE;
+ if (gScrollCallback != NULL) {
+ /* Call scrolling callback. Set blocking hook to our routine
+ * which prevents messages from being dispatched. */
+ if (gWSBlockingProc != NULL)
+ WSASetBlockingHook (gWSBlockingProc);
+ didScroll = gScrollCallback (cmd, scroll_pos);
+ if (gWSBlockingProc != NULL)
+ WSAUnhookBlockingHook ();
+ }
+ /*
+ * If no callback or callback did not do the scrolling operation,
+ * insert a scroll cmd in the input stream.
+ */
+ if (!didScroll)
+ CQAddUniq ((UCS)cmd, 0);
+ }
+
+
+ gScrolling = FALSE;
+ return;
+}
+
+
+#ifdef WIN32
+/*---------------------------------------------------------------------------
+ * void MouseWheelTTY ()
+ *
+ * Description:
+ * Respond to a WM_MOUSEWHEEL event by calling scroll callback
+ * ala ScrollTTY.
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+MouseWheelTTY (HWND hWnd, int xPos, int yPos, int fwKeys, int zDelta)
+{
+ PTTYINFO pTTYInfo;
+ int cmd;
+ long scroll_pos;
+ FARPROC prevBlockingProc;
+ SCROLLINFO scrollInfo;
+ static int zDelta_accumulated;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+
+ if (pTTYInfo == NULL || gScrolling)
+ return;
+
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_POS | SIF_RANGE;
+ GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
+ if((zDelta < 0 && scrollInfo.nPos < scrollInfo.nMin)
+ || (zDelta > 0 && scrollInfo.nPos >= scrollInfo.nMax))
+ return;
+
+ gScrolling = TRUE;
+ if (gWSBlockingProc != NULL)
+ prevBlockingProc = WSASetBlockingHook (gWSBlockingProc);
+
+ if(fwKeys == MK_MBUTTON)
+ zDelta *= 2; /* double the effect! */
+
+ if(abs(zDelta += zDelta_accumulated) < WHEEL_DELTA){
+ zDelta_accumulated = zDelta;
+ }
+ else{
+ /* Remember any partial increments */
+ zDelta_accumulated = zDelta % WHEEL_DELTA;
+
+ scroll_pos = (long)(gsMWMultiplier * abs((zDelta / WHEEL_DELTA)));
+
+ cmd = (zDelta < 0) ? MSWIN_KEY_SCROLLDOWNLINE : MSWIN_KEY_SCROLLUPLINE;
+
+ SelClear ();
+ if (gScrollCallback != NULL) {
+ /* Call scrolling callback. Set blocking hook to our routine
+ * which prevents messages from being dispatched. */
+ if (gWSBlockingProc != NULL)
+ WSASetBlockingHook (gWSBlockingProc);
+ (void) gScrollCallback (cmd, scroll_pos);
+ if (gWSBlockingProc != NULL)
+ WSAUnhookBlockingHook ();
+ }
+ }
+
+ gScrolling = FALSE;
+ return;
+}
+
+
+LOCAL void
+MouseWheelMultiplier()
+{
+ TCHAR lines[8];
+ DWORD llen = sizeof(lines)/sizeof(TCHAR);
+
+ /* HKEY_CURRENT_USER\Control Panel\Desktop holds the key */
+ gsMWMultiplier = (MSWRPeek(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"),
+ TEXT("WheelScrollLines"), lines, &llen) == TRUE)
+ ? (short)_ttoi(lines) : 1;
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * void CaretTTY (HWND hWnd, CARETS cStyle)
+ *
+ * Description:
+ * Adjusts the Caret to the user supplied style
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * int wStyle
+ * New style to take on
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CaretTTY (HWND hWnd, CARETS cStyle)
+{
+ PTTYINFO pTTYInfo;
+
+ if(pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)){
+ pTTYInfo->cCaretStyle = cStyle;
+ CaretCreateTTY (hWnd);
+ DidResize (gpTTYInfo);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ * void CaretCreateTTY (HWND hWnd, BOOL wPosition)
+ *
+ * Description:
+ * Adjusts the Caret to the user supplied style
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BOOL wPosition
+ * whether or not to position it too
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CaretCreateTTY (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ if(pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)){
+ int n = 0, x, y;
+
+ switch(pTTYInfo->cCaretStyle){
+ case CaretHorizBar :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar / 5;
+ n = pTTYInfo->yChar - y;
+ break;
+
+ case CaretVertBar :
+ x = pTTYInfo->xChar / 4;
+ y = pTTYInfo->yChar;
+ break;
+
+ case CaretSmallBlock :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar / 2;
+ n = pTTYInfo->yChar - y;
+ break;
+
+ default :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar;
+ break;
+ }
+
+ CreateCaret (hWnd, NULL, x, y);
+ pTTYInfo->yCurOffset = n;
+
+ if(pTTYInfo->fCaretOn){
+ ShowCaret(hWnd);
+ MoveTTYCursor(hWnd);
+ }
+ }
+}
+
+
+/*
+ * This routine is inserted as the winsock blocking hook. It's main perpos
+ * is to NOT dispatch messages.
+ */
+BOOL CALLBACK __export
+NoMsgsAreSent (void)
+{
+ return (FALSE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SetTTYFocus( HWND hWnd )
+ *
+ * Description:
+ * Sets the focus to the TTY window also creates caret.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SetTTYFocus (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "SetTTYFocus::: entered\n");
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ mswin_showcursor(TRUE);
+
+ pTTYInfo->fFocused = TRUE;
+
+ CaretCreateTTY (hWnd);
+
+ MoveTTYCursor (hWnd);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL KillTTYFocus( HWND hWnd )
+ *
+ * Description:
+ * Kills TTY focus and destroys the caret.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+KillTTYFocus (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "KillTTYFocus::: entered\n");
+#endif
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ mswin_showcursor(TRUE);
+
+ if(pTTYInfo->fCaretOn)
+ HideCaret (hWnd);
+
+ DestroyCaret();
+
+ pTTYInfo->fFocused = FALSE;
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MoveTTYCursor( HWND hWnd )
+ *
+ * Description:
+ * Moves caret to current position.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+MoveTTYCursor (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ if(pTTYInfo->fCaretOn && !pTTYInfo->fMassiveUpdate) {
+ HideCaret (hWnd);
+ SetCaretPos ((pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset,
+ (pTTYInfo->nRow * pTTYInfo->yChar)
+ + pTTYInfo->yCurOffset + pTTYInfo->yOffset);
+ ShowCaret (hWnd);
+ }
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYKeyDown ( HWND hWnd, WORD bOut )
+ *
+ * Description:
+ * Called to process MW_KEYDOWN message. We are only interested in
+ * virtual keys that pico/pine use. All others get passed on to
+ * the default message handler. Regular key presses will return
+ * latter as a WM_CHAR message, with SHIFT and CONTROL processing
+ * already done.
+ *
+ * We do watch for VK_CONTROL to keep track of it's state such
+ * that we can implement ^_space.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE key
+ * Virtual key code.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYKeyDown (HWND hWnd, TCHAR key)
+{
+ UCS myKey;
+ BOOL fKeyControlDown = GetKeyState(VK_CONTROL) < 0;
+ BOOL fKeyAltDown = GetKeyState(VK_MENU) < 0;
+
+ // If the alt key is down, let Windows handle the message. This will
+ // allow the Ctrl+Alt (AltGr) processing to work.
+ if(fKeyAltDown)
+ return FALSE;
+
+ switch (key) {
+ case VK_MENU:
+ case VK_CONTROL:
+ case VK_SHIFT:
+ return FALSE;
+ case VK_UP: myKey = KEY_UP; break;
+ case VK_DOWN: myKey = KEY_DOWN; break;
+ case VK_RIGHT:
+ /* Ctrl-@ is used to advance to the next word. */
+ myKey = fKeyControlDown ? '@': KEY_RIGHT;
+ break;
+ case VK_LEFT:
+ /* Ctrl-left is used to advance to the previous word. */
+ myKey = KEY_LEFT;
+ break;
+ case VK_HOME:
+ /* Ctrl-home is used to advance to the beginning of buffer. */
+ myKey = KEY_HOME;
+ break;
+ case VK_END:
+ /* Ctrl-end is used to advance to the end of buffer. */
+ myKey = KEY_END;
+ break;
+ case VK_PRIOR: myKey = KEY_PGUP; break;
+ case VK_NEXT: myKey = KEY_PGDN; break;
+ case VK_DELETE: myKey = KEY_DEL; break;
+ case VK_F1: myKey = F1; break;
+ case VK_F2: myKey = F2; break;
+ case VK_F3: myKey = F3; break;
+ case VK_F4: myKey = F4; break;
+ case VK_F5: myKey = F5; break;
+ case VK_F6: myKey = F6; break;
+ case VK_F7: myKey = F7; break;
+ case VK_F8: myKey = F8; break;
+ case VK_F9: myKey = F9; break;
+ case VK_F10: myKey = F10; break;
+ case VK_F11: myKey = F11; break;
+ case VK_F12: myKey = F12; break;
+
+ default:
+ if(fKeyControlDown && !(GetKeyState(VK_SHIFT) < 0)) {
+ if(key == '6') {
+ /*
+ * Ctrl-^ is used to set and clear the mark in the
+ * composer (pico) On most other systems Ctrl-6 does the
+ * same thing. Allow that on windows too.
+ */
+ myKey = '^';
+ break;
+ } else if(key == '2') {
+ /* Ctrl-@ is used to advance to the next word. */
+ myKey = '@';
+ break;
+ }
+ }
+
+ return (FALSE); /* Message NOT handled.*/
+ }
+
+ CQAdd (myKey, fKeyControlDown);
+
+ set_time_of_last_input();
+
+ return (TRUE); /* Message handled .*/
+}
+
+
+#ifdef CDEBUG
+char *
+dtime()
+{
+ static char timestring[23];
+ time_t t;
+ struct _timeb timebuffer;
+
+ timestring[0] = '\0';
+ t = time((time_t *) 0);
+ _ftime(&timebuffer);
+ snprintf(timestring, sizeof(timestring), "%.8s.%03ld", ctime(&t)+11, timebuffer.millitm);
+
+ return(timestring);
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYCharacter( HWND hWnd, WORD bOut )
+ *
+ * Description:
+ * Place the character into a queue.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE bOut
+ * byte from keyboard
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYCharacter (HWND hWnd, TCHAR bOut)
+{
+ // Only check for control key being down if the alt key isn't also down.
+ // Windows uses Ctrl+Alt as AltGr.
+ BOOL fKeyAltDown = GetKeyState(VK_MENU) < 0;
+ BOOL fKeyControlDown = fKeyAltDown ?
+ FALSE : (GetKeyState(VK_CONTROL) < 0);
+
+ if(fKeyControlDown) {
+ if(bOut == ' ')
+ bOut = '@';
+ else
+ bOut += '@';
+ }
+
+ CQAdd ((UCS)bOut, fKeyControlDown);
+
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+
+ set_time_of_last_input();
+
+ return (TRUE); /* Message handled. */
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYMouse(HWND hWnd, int mevent, int button,
+ * int xPos, int yPos, WPARAM keys)
+ *
+ * Description:
+ * This is the central control for all mouse events. Every event
+ * gets put into a queue to wait for the upper layer.
+ *
+ * The upper's input routine calls checkmouse() which pulls the
+ * mouse event off the input queue. checkmouse() has a list of
+ * of screen regions. Some regions correspond to a "menu" item
+ * (text button at bottom of screen). There is generally one
+ * region for the central region of the screen.
+ *
+ * Because pine/pico do not interpret mouse drags, we do that here.
+ * When the user presses the button and drags the mouse across the
+ * screen this select the text in the region defined by the drag.
+ * The operation is local to mswin.c, and can only get what text
+ * is on the screen.
+ *
+ * The one exception is that now pico interprets mouse drag events
+ * in the body. pico signals that it wants to track the mouse
+ * by calling mswin_allowmousetrack(). This will 1) turn off
+ * our mouse tracking and 2) cause mouse movement events to
+ * be put on the mouse queue.
+ *
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE bOut
+ * byte from keyboard
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYMouse (HWND hWnd, int mevent, int button,
+ CORD xPos, CORD yPos, WPARAM winkeys)
+{
+ int nRow;
+ int nColumn;
+ int keys;
+
+ /*
+ * Convert to cell position.
+ */
+ nColumn = (xPos - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ if (xPos < gpTTYInfo->xOffset)
+ --nColumn;
+ nRow = (yPos - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+ if (yPos < gpTTYInfo->yOffset)
+ --nRow;
+
+ /*
+ * Convert window's keys.
+ */
+ keys = 0;
+ if (winkeys & MK_CONTROL)
+ keys |= M_KEY_CONTROL;
+ if (winkeys & MK_SHIFT)
+ keys |= M_KEY_SHIFT;
+
+ /* Adjust the cursor */
+ if((unsigned long) mevent != M_EVENT_UP){
+ if(gMouseTracking)
+ mswin_setcursor(MSWIN_CURSOR_IBEAM);
+ else if(ghCursorCurrent == ghCursorBusy)
+ mswin_setcursor(MSWIN_CURSOR_BUSY);
+ else if(mouse_on_key(nRow, nColumn))
+ mswin_setcursor(MSWIN_CURSOR_HAND);
+ else if(gMouseTrackCallback)
+ mswin_setcursor((*gMouseTrackCallback)(nColumn, (long) nRow));
+ else
+ mswin_setcursor(MSWIN_CURSOR_ARROW);
+ }
+
+ /*
+ * Tracking event or mouse up/down?
+ */
+ if ((unsigned long) mevent == M_EVENT_TRACK) {
+ /*
+ * Who is doing the tracking?
+ */
+ if (gAllowMouseTrack) {
+ /* For tracking, Button info is different. */
+ if (keys & MK_LBUTTON)
+ button = M_BUTTON_LEFT;
+ else if (keys & MK_MBUTTON)
+ button = M_BUTTON_MIDDLE;
+ else if (keys & MK_RBUTTON)
+ button = M_BUTTON_RIGHT;
+ MQAdd (mevent, button, nRow, nColumn, keys,
+ MSWIN_MF_REPLACING);
+ }
+ else
+ SelTrackMouse (nRow, nColumn);
+ }
+ else{
+ /*
+ * Tracking. Only start tracking mouse down in the text region
+ * But allow mouse up anywhere.
+ */
+ if ( (nRow >= 0 && nRow < gpTTYInfo->actNRow &&
+ nColumn >= 0 && nColumn < gpTTYInfo->actNColumn)
+ || (unsigned long) mevent == M_EVENT_UP) {
+
+ /*
+ * Mouse tracking. When the mouse goes down we start
+ * capturing all mouse movement events. If no one else wants
+ * them we will start defining a text selection.
+ */
+ if ((unsigned long) mevent == M_EVENT_DOWN) {
+ gMouseTracking = TRUE;
+ SetCapture (ghTTYWnd);
+ if (!gAllowMouseTrack && button == M_BUTTON_LEFT)
+ SelStart (nRow, nColumn);
+ }
+ else {
+ ReleaseCapture ();
+ if (!gAllowMouseTrack && button == M_BUTTON_LEFT)
+ SelFinish (nRow, nColumn);
+ gMouseTracking = FALSE;
+
+ /*
+ * If right mouse button, toss pop-up menu offering
+ * cut/copy/paste
+ */
+ if(button == M_BUTTON_RIGHT && SelAvailable()){
+ UINT fAllowed = (EM_CP | EM_CP_APPEND);
+
+ if(gAllowCut)
+ fAllowed |= EM_CUT;
+
+ if(CopyCutPopup(hWnd, fAllowed) == TRUE)
+ mevent = M_EVENT_TRACK; /* don't add to input queue! */
+ }
+ }
+
+ /*
+ * Insert event into queue.
+ */
+ if((unsigned long) mevent != M_EVENT_TRACK)
+ MQAdd (mevent, button, nRow, nColumn, keys, 0);
+ }
+ }
+
+ mswin_showcursor(TRUE); /* make sure it's visible */
+
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+
+ set_time_of_last_input();
+
+ return (0); /* Message handled. */
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTimer ()
+ *
+ * Description:
+ * Process the periodic timer calls.
+ *
+ *
+ * Parameters:
+ * None.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+ProcessTimer (void)
+{
+ /* Time to deliver an alarm signal? */
+ if (gAlarmTimeout != 0 && GetTickCount () / 1000 > gAlarmTimeout)
+ AlarmDeliver ();
+
+ /* Time to make the periodic callback. */
+ if (gPeriodicCallback != NULL &&
+ GetTickCount() / 1000 > gPeriodicCBTimeout) {
+ gPeriodicCBTimeout = GetTickCount() / 1000 +
+ gPeriodicCBTime;
+ gPeriodicCallback ();
+ }
+
+ /*
+ * If tracking the mouse, insert a fake mouse tracking message
+ * At the last know location of the mouse.
+ */
+ if (gAllowMouseTrack) {
+ gMTEvent.event = M_EVENT_TRACK;
+ MQAdd (gMTEvent.event, gMTEvent.button, gMTEvent.nRow,
+ gMTEvent.nColumn, gMTEvent.keys, MSWIN_MF_REPLACING);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYBlock( HWND hWnd, LPSTR lpBlock, int nLength )
+ *
+ * Description:
+ * Writes block to TTY screen. Nothing fancy - just
+ * straight TTY.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * LPSTR lpBlock
+ * far pointer to block of data
+ *
+ * int nLength
+ * length of block
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYBlock (HWND hWnd, LPTSTR lpBlock, int nLength)
+{
+ int i, j, width;
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ BOOL fNewLine;
+ long offset;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ for (i = 0 ; i < nLength; i++) {
+ switch (lpBlock[i]) {
+ case ASCII_BEL:
+ /* Bell */
+ MessageBeep (0) ;
+ break ;
+
+ case ASCII_BS:
+ /* Backspace over a whole character */
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+ width = (offset > pTTYInfo->nRow * pTTYInfo->actNColumn)
+ ? scrwidth(pTTYInfo->pScreen+offset-1, 1) : 0;
+
+ if(pTTYInfo->nColumn > 0)
+ pTTYInfo->nColumn = (CORD)(pTTYInfo->nColumn - width);
+
+ MoveTTYCursor (hWnd);
+ break;
+
+ case ASCII_CR:
+ /* Carriage return */
+ pTTYInfo->nColumn = 0 ;
+ MoveTTYCursor (hWnd);
+ if (!pTTYInfo->fNewLine)
+ break;
+
+ /* fall through */
+
+ case ASCII_LF:
+ /* Line feed */
+ if (++pTTYInfo->nRow == pTTYInfo->actNRow) {
+
+ /* Scroll the Screen. */
+
+ /* slide rows 1 - n-1 up to row 0 */
+ memmove ((LPTSTR)pTTYInfo->pScreen,
+ (LPTSTR) (pTTYInfo->pScreen + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof (TCHAR));
+
+ /* initialize new row n-1 */
+ for(j = (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn;
+ j < pTTYInfo->actNColumn; j++)
+ pTTYInfo->pScreen[j] = (TCHAR) ' ';
+
+
+ /* Scroll the Cell Widths */
+ memmove ((int *)pTTYInfo->pCellWidth,
+ (int *) (pTTYInfo->pCellWidth + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof (int));
+ for(j = (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn;
+ j < pTTYInfo->actNColumn; j++)
+ pTTYInfo->pCellWidth[j] = pTTYInfo->xChar; /* xChar set yet ? */
+
+ /* Scroll the Attributes. */
+
+ /* slide rows 1 - n-1 up to row 0 */
+ memmove ((CharAttrib *) pTTYInfo->pAttrib,
+ (CharAttrib *) (pTTYInfo->pAttrib + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof(CharAttrib));
+
+ /* initialize new row n-1 to zero */
+ memset ((CharAttrib *) (pTTYInfo->pAttrib +
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn),
+ 0, pTTYInfo->actNColumn*sizeof(CharAttrib));
+
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+ --pTTYInfo->nRow;
+ }
+
+ MoveTTYCursor (hWnd);
+ break;
+
+ default:
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+ pTTYInfo->pScreen[offset] = lpBlock[i];
+ pTTYInfo->pCellWidth[offset] = wcellwidth((UCS)lpBlock[i]) * pTTYInfo->xChar;
+ pTTYInfo->pAttrib[offset] = pTTYInfo->curAttrib;
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) +
+ pTTYInfo->xOffset;
+ rect.right = rect.left + pTTYInfo->xChar;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) +
+ pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+
+ /* Line Wrap. */
+ if (pTTYInfo->nColumn < pTTYInfo->actNColumn - 1)
+ pTTYInfo->nColumn++ ;
+ else if (pTTYInfo->autoWrap == WRAP_ON ||
+ (pTTYInfo->autoWrap == WRAP_NO_SCROLL &&
+ pTTYInfo->nRow < pTTYInfo->actNRow - 1)) {
+ fNewLine = pTTYInfo->fNewLine;
+ pTTYInfo->fNewLine = FALSE;
+ WriteTTYBlock (hWnd, TEXT("\r\n"), 2);
+ pTTYInfo->fNewLine = fNewLine;
+ }
+ break;
+ }
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYText ( HWND hWnd, LPSTR lpBlock, int nLength )
+ *
+ * Description:
+ * Like WriteTTYBlock but optimized for strings that are text only,
+ * no carrage control characters.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * LPSTR lpBlock
+ * far pointer to block of data
+ *
+ * int nLength
+ * length of block
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYText (HWND hWnd, LPTSTR lpText, int nLength)
+{
+ int i;
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ long offset, endOffset;
+ long colEnd;
+ long screenEnd;
+ int screenwidth;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+
+ /* Calculate offset of cursor, end of current column, and end of screen */
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+
+ colEnd = (pTTYInfo->nRow + 1) * pTTYInfo->actNColumn;
+ screenEnd = pTTYInfo->actNRow * pTTYInfo->actNColumn;
+
+
+ /* Text is allowed to wrap around to subsequent lines, but not past end
+ * of screen */
+ endOffset = offset + nLength;
+ if (endOffset >= screenEnd) {
+ nLength = screenEnd - offset;
+ endOffset = offset + nLength - 1; /* Last cell, not one past last */
+ }
+
+
+ /* Calculate bounding rectangle. */
+ if (endOffset <= colEnd) {
+ /* Single line. */
+
+ screenwidth = scrwidth(lpText, nLength);
+
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.right = rect.left + (pTTYInfo->xChar * screenwidth);
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ /* Advance cursor on cur line but not past end. */
+ pTTYInfo->nColumn = (CORD)MIN(pTTYInfo->nColumn + screenwidth,
+ pTTYInfo->actNColumn - 1);
+ }
+ else {
+ /* Wraps across multiple lines. Calculate one rect to cover all
+ * lines. */
+ rect.left = 0;
+ rect.right = pTTYInfo->xSize;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = ((((offset + nLength) / pTTYInfo->actNColumn) + 1) *
+ pTTYInfo->yChar) + pTTYInfo->yOffset;
+ pTTYInfo->nRow = (CORD)(endOffset / pTTYInfo->actNColumn);
+ pTTYInfo->nColumn = (CORD)(endOffset % pTTYInfo->actNColumn);
+ }
+
+
+ /* Apply text and attributes to screen in one smooth motion. */
+ for(i = 0; i < nLength; i++)
+ (pTTYInfo->pScreen+offset)[i] = lpText[i];
+ for(i = 0; i < nLength; i++)
+ (pTTYInfo->pCellWidth+offset)[i] = wcellwidth((UCS)lpText[i]) * pTTYInfo->xChar;
+ for(i = 0; i < nLength; i++)
+ pTTYInfo->pAttrib[offset+i] = pTTYInfo->curAttrib;
+
+ /* Invalidate rectangle */
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYChar (HWND hWnd, char ch)
+ *
+ * Description:
+ * Write a single character to the cursor position and advance the
+ * cursor. Does not handle carage control.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * char ch
+ * character being written.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYChar (HWND hWnd, TCHAR ch)
+{
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ long offset;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ offset = (pTTYInfo->nRow * pTTYInfo->actNColumn) +
+ pTTYInfo->nColumn;
+
+ *(pTTYInfo->pScreen + offset) = ch;
+ pTTYInfo->pCellWidth[offset] = wcellwidth((UCS)ch) * pTTYInfo->xChar;
+ pTTYInfo->pAttrib[offset] = pTTYInfo->curAttrib;
+
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.right = rect.left + pTTYInfo->xChar;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+
+
+
+ /* Line Wrap. */
+ if (pTTYInfo->nColumn < pTTYInfo->actNColumn - 1)
+ pTTYInfo->nColumn++ ;
+ else if ((pTTYInfo->autoWrap == WRAP_ON ||
+ pTTYInfo->autoWrap == WRAP_NO_SCROLL) &&
+ pTTYInfo->nRow < pTTYInfo->actNRow - 1) {
+ pTTYInfo->nRow++;
+ pTTYInfo->nColumn = 0;
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * VOID GoModalDialogBoxParam( HINSTANCE hInstance,
+ * LPTSTR lpszTemplate, HWND hWnd,
+ * DLGPROC lpDlgProc, LPARAM lParam )
+ *
+ * Description:
+ * It is a simple utility function that simply performs the
+ * MPI and invokes the dialog box with a DWORD paramter.
+ *
+ * Parameters:
+ * similar to that of DialogBoxParam() with the exception
+ * that the lpDlgProc is not a procedure instance
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL VOID
+GoModalDialogBoxParam( HINSTANCE hInstance, LPTSTR lpszTemplate,
+ HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
+{
+ DLGPROC lpProcInstance ;
+
+ lpProcInstance = (DLGPROC) MakeProcInstance( (FARPROC) lpDlgProc,
+ hInstance ) ;
+ DialogBoxParam( hInstance, lpszTemplate, hWnd, lpProcInstance, lParam ) ;
+ FreeProcInstance( (FARPROC) lpProcInstance ) ;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL FAR PASCAL __export AboutDlgProc( HWND hDlg, UINT uMsg,
+ * WPARAM wParam, LPARAM lParam )
+ *
+ * Description:
+ * Simulates the Windows System Dialog Box.
+ *
+ * Parameters:
+ * Same as standard dialog procedures.
+ *
+/*--------------------------------------------------------------------------*/
+BOOL FAR PASCAL __export
+AboutDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ TCHAR szTemp [81];
+
+ /* sets up version number for PINE */
+ GetDlgItemText (hDlg, IDD_VERSION, szTemp, sizeof(szTemp)/sizeof(TCHAR));
+ /* szTemp is unicode, mswin_compilation_date etc are cast as %S in mswin.rc */
+ _sntprintf (TempBuf, sizeof(TempBuf)/sizeof(TCHAR), szTemp, mswin_specific_winver(),
+ mswin_majorver(), mswin_minorver(),
+ mswin_compilation_remarks(),
+ mswin_compilation_date());
+ SetDlgItemText (hDlg, IDD_VERSION, (LPTSTR) TempBuf);
+
+ /* get by-line */
+ LoadString (GET_HINST (hDlg), IDS_BYLINE, TempBuf,
+ sizeof(TempBuf) / sizeof(TCHAR));
+ SetDlgItemText (hDlg, IDD_BYLINE, TempBuf);
+
+ }
+ return ( TRUE ) ;
+
+ case WM_CLOSE:
+ EndDialog( hDlg, TRUE ) ;
+ return ( TRUE ) ;
+
+ case WM_COMMAND:
+ switch((WORD) wParam){
+ case IDD_OK :
+ EndDialog( hDlg, TRUE ) ;
+ return ( TRUE ) ;
+
+ default :
+ break;
+ }
+
+ break;
+ }
+ return ( FALSE ) ;
+
+} /* end of AboutDlgProc() */
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Description:
+ * Simulates the Windows System Dialog Box.
+ *
+ * Parameters:
+ * Same as standard dialog procedures.
+ *
+/*--------------------------------------------------------------------------*/
+BOOL FAR PASCAL __export
+SplashDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static HBITMAP hbmpSplash;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ if(hbmpSplash = LoadBitmap(GET_HINST(hDlg),
+ MAKEINTRESOURCE(ALPINESPLASH))){
+ BITMAP stBitmap;
+ int cx, cy;
+
+ cx = GetSystemMetrics(SM_CXSCREEN);
+ cy = GetSystemMetrics(SM_CYSCREEN);
+ GetObject(hbmpSplash, sizeof(BITMAP), &stBitmap);
+
+ SetWindowPos(hDlg, HWND_TOPMOST,
+ (cx - stBitmap.bmWidth) / 2,
+ (cy - stBitmap.bmHeight) / 2,
+ stBitmap.bmWidth, stBitmap.bmHeight,
+ SWP_NOCOPYBITS /* | SWP_SHOWWINDOW */);
+ }
+
+ return(TRUE);
+
+ case WM_CTLCOLORDLG :
+ return(TRUE);
+
+ case WM_ERASEBKGND :
+ {
+ HDC hMemDC;
+ POINT stPoint;
+ BITMAP stBitmap;
+ HGDIOBJ hObject;
+
+ if((hMemDC = CreateCompatibleDC((HDC) wParam)) != NULL){
+ hObject = SelectObject(hMemDC, hbmpSplash);
+ SetMapMode(hMemDC, GetMapMode((HDC) wParam));
+
+ GetObject(hbmpSplash, sizeof(BITMAP), &stBitmap);
+ stPoint.x = stBitmap.bmWidth;
+ stPoint.y = stBitmap.bmHeight;
+ DPtoLP((HDC) wParam, &stPoint, 1);
+
+ BitBlt((HDC) wParam,
+ 0, 0, stPoint.x, stPoint.y,
+ hMemDC, 0, 0, SRCCOPY);
+ SelectObject(hMemDC, hObject);
+ DeleteDC(hMemDC) ;
+ }
+
+ return(TRUE);
+ }
+
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hDlg);
+ return(TRUE);
+
+ case WM_DESTROY :
+ if(hbmpSplash)
+ DeleteObject(hbmpSplash);
+
+ break;
+
+ case WM_COMMAND: /* No commands! */
+ DestroyWindow(hDlg);
+ return(TRUE);
+ }
+
+ return(FALSE);
+
+}
+
+
+#if 0
+UINT APIENTRY
+SelectTTYFontHook(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg){
+ case WM_INITDIALOG :
+ {
+ /*
+ * Deactivate the Style combo box...
+ */
+ HWND hWnd = GetDlgItem(hDlg, cmb2);
+ EnableWindow(hWnd, FALSE);
+ SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW);
+ return(TRUE);
+ }
+
+ break;
+
+ case WM_COMMAND :
+ switch ((WORD) wParam) {
+ case psh3 :
+ {
+ LOGFONT curfont;
+
+ SendMessage(hDlg, WM_CHOOSEFONT_GETLOGFONT,
+ 0, (LPARAM) &curfont);
+ ResetTTYFont (ghTTYWnd, gpTTYInfo, &curfont);
+ return (TRUE);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ return(FALSE);
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SelectTTYFont( HWND hDlg )
+ *
+ * Description:
+ * Selects the current font for the TTY screen.
+ * Uses the Common Dialog ChooseFont() API.
+ *
+ * Parameters:
+ * HWND hDlg
+ * handle to settings dialog
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SelectTTYFont (HWND hWnd)
+{
+ CHOOSEFONT cfTTYFont;
+ LOGFONT newFont, origFont;
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ memcpy (&newFont, &gpTTYInfo->lfTTYFont, sizeof (LOGFONT));
+ memcpy (&origFont, &gpTTYInfo->lfTTYFont, sizeof (LOGFONT));
+
+ cfTTYFont.lStructSize = sizeof (CHOOSEFONT);
+ cfTTYFont.hwndOwner = hWnd ;
+ cfTTYFont.hDC = NULL ;
+ cfTTYFont.lpLogFont = &newFont;
+ cfTTYFont.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY |
+ CF_INITTOLOGFONTSTRUCT |
+#if 0
+ CF_FORCEFONTEXIST | CF_LIMITSIZE |
+ CF_ENABLEHOOK | CF_APPLY;
+#else
+ CF_FORCEFONTEXIST | CF_LIMITSIZE;
+#endif
+ cfTTYFont.nSizeMin = FONT_MIN_SIZE;
+ cfTTYFont.nSizeMax = FONT_MAX_SIZE;
+ cfTTYFont.lCustData = (long) 0 ;
+#if 0
+ cfTTYFont.lpfnHook = SelectTTYFontHook ;
+#else
+ cfTTYFont.lpfnHook = NULL;
+#endif
+ cfTTYFont.lpTemplateName = NULL ;
+ cfTTYFont.hInstance = GET_HINST (hWnd);
+
+
+
+ if (ChooseFont (&cfTTYFont)) {
+ ResetTTYFont (hWnd, pTTYInfo, &newFont);
+ }
+#if 0
+ else{
+ ResetTTYFont (hWnd, pTTYInfo, &origFont);
+ }
+#endif
+
+ return (TRUE);
+}
+
+
+/*
+ * Set a specific color (forground, background, reverse, normal) to
+ * the color specified by name.
+ */
+LOCAL void
+SetColorAttribute (COLORREF *cf, char *colorName)
+{
+ /* color name not in table. Try converting RGB string. */
+ ConvertRGBString (colorName, cf);
+
+ /* Redraw screen. */
+ gpTTYInfo->screenDirty = TRUE;
+ gpTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (ghTTYWnd, NULL, FALSE);
+}
+
+
+/*
+ * Set current color attribute to reverse color
+ */
+void
+SetReverseColor()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbFG = gpTTYInfo->rgbRFGColor;
+ gpTTYInfo->curAttrib.rgbBG = gpTTYInfo->rgbRBGColor;
+}
+
+
+/*
+ * Convert a string to an integer.
+ */
+LOCAL BOOL
+ScanInt (char *str, int min, int max, int *val)
+{
+ char *c;
+ int v;
+ int neg = 1;
+
+
+ if (str == NULL) return (FALSE);
+ if (*str == '\0' || strlen (str) > 9) return (FALSE);
+
+ /* Check for a negative sign. */
+ if (*str == '-') {
+ neg = -1;
+ ++str;
+ }
+
+ /* Check for all digits. */
+ for (c = str; *c != '\0'; ++c) {
+ if (!isdigit((unsigned char)*c))
+ return (FALSE);
+ }
+
+ /* Convert from ascii to int. */
+ v = atoi (str) * neg;
+
+ /* Check constraints. */
+ if (v < min || v > max)
+ return (FALSE);
+ *val = v;
+ return (TRUE);
+}
+
+
+/*
+ * Convert a RGB string to a color ref. The string should look like:
+ * rrr,ggg,bbb
+ * where rrr, ggg, and bbb are numbers between 0 and 255 that represent
+ * red, gree, and blue values. Must be comma seperated.
+ * Returns:
+ * TRUE - Successfully converted string.
+ * FALSE - Bad format, 'cf' unchanged.
+ */
+LOCAL BOOL
+ConvertRGBString (char *colorName, COLORREF *cf)
+{
+ int i, j, n, rgb[3];
+ MSWINColor *ct;
+
+ if(!colorName)
+ return(FALSE);
+
+ /* Is the name in the global color table? */
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(!struncmp(ct->colorName, colorName, (int)strlen(ct->colorName))){
+ *cf = ct->colorRef;
+ return(TRUE);
+ }
+
+ /* Not a named color, try RRR,GGG,BBB */
+ for(i = 0; i < 3; i++){
+ if(i && *colorName++ != ',')
+ return(FALSE);
+
+ for(rgb[i] = 0, j = 0; j < 3; j++)
+ if((n = *colorName++ - '0') < 0 || n > 9)
+ return(FALSE);
+ else
+ rgb[i] = (rgb[i] * 10) + n;
+ }
+
+ *cf = RGB (rgb[0], rgb[1], rgb[2]);
+ return (TRUE);
+}
+
+
+LOCAL char *
+ConvertStringRGB(char *colorName, size_t ncolorName, COLORREF colorRef)
+{
+ MSWINColor *cf;
+
+ for(cf = MSWINColorTable;
+ cf->colorName && cf->colorRef != colorRef;
+ cf++)
+ ;
+
+ if(cf->colorName){
+ strncpy(colorName, cf->colorName, ncolorName);
+ colorName[ncolorName-1] = '\0';
+ }
+ else
+ snprintf(colorName, ncolorName, "%.3d,%.3d,%.3d",
+ GetRValue(colorRef), GetGValue(colorRef), GetBValue(colorRef));
+
+ return(colorName);
+}
+
+
+/*
+ * Map from integer color value to canonical color name.
+ */
+char *
+colorx(int color)
+{
+ MSWINColor *ct;
+ static char cbuf[RGBLEN+1];
+
+ if(color < fullColorTableSize){
+ ct = &MSWINColorTable[color];
+ if(ct->canonicalName)
+ return(ct->canonicalName);
+ }
+
+ /* not supposed to get here */
+ snprintf(cbuf, sizeof(cbuf), "color%03.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Argument is a color name which could be an RGB string, a name like "blue",
+ * or a name like "color011".
+ *
+ * Returns a pointer to the canonical name of the color.
+ */
+char *
+color_to_canonical_name(char *s)
+{
+ int i, j, n, rgb[3];
+ MSWINColor *ct;
+ COLORREF cr;
+ static char cn[RGBLEN+1];
+
+ if(!s)
+ return(NULL);
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(!struncmp(ct->colorName, s, (int)strlen(ct->colorName)))
+ break;
+
+ if(ct->colorName)
+ return(ct->canonicalName);
+
+ /* maybe it is RGB? */
+ for(i = 0; i < 3; i++){
+ if(i && *s++ != ',')
+ return("");
+
+ for(rgb[i] = 0, j = 0; j < 3; j++)
+ if((n = *s++ - '0') < 0 || n > 9)
+ return("");
+ else
+ rgb[i] = (rgb[i] * 10) + n;
+ }
+
+ cr = RGB(rgb[0], rgb[1], rgb[2]);
+
+ /*
+ * Now compare that RGB against the color table RGBs. If it is
+ * in the table, return the canonical name, else return the RGB string.
+ */
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(ct->colorRef == cr)
+ break;
+
+ if(ct->colorName)
+ return(ct->canonicalName);
+ else{
+ snprintf(cn, sizeof(cn), "%.3d,%.3d,%.3d",
+ GetRValue(cr), GetGValue(cr), GetBValue(cr));
+ return(cn);
+ }
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Toolbar setup routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+TBToggle (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (pTTYInfo->toolBarSize > 0)
+ TBHide (hWnd);
+ else
+ TBShow (hWnd);
+}
+
+
+LOCAL void
+TBPosToggle (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ pTTYInfo->toolBarTop = !pTTYInfo->toolBarTop;
+ if(pTTYInfo->hTBWnd){
+ TBHide (hWnd);
+ TBShow (hWnd);
+ }
+}
+
+
+LOCAL void
+TBShow (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ /*
+ * Make sure the tool bar not already shown.
+ */
+ if (pTTYInfo->toolBarSize > 0)
+ return;
+
+
+
+ /*
+ * Make procinstance for dialog funciton.
+ */
+ HideCaret (hWnd);
+ if (gToolBarProc == NULL)
+ gToolBarProc = (DLGPROC) MakeProcInstance( (FARPROC) ToolBarProc,
+ ghInstance ) ;
+ if (gTBBtnProc == NULL)
+ gTBBtnProc = (WNDPROC) MakeProcInstance( (FARPROC) TBBtnProc,
+ ghInstance ) ;
+
+
+ /*
+ * Create the dialog box.
+ */
+ pTTYInfo->hTBWnd = CreateDialog (ghInstance,
+ MAKEINTRESOURCE (pTTYInfo->curToolBarID),
+ hWnd,
+ gToolBarProc);
+ if (pTTYInfo->hTBWnd == NULL) {
+ ShowCaret (hWnd);
+ return;
+ }
+
+ SetFocus (hWnd);
+
+
+ /*
+ * Adjust the window size.
+ */
+ GetWindowRect (pTTYInfo->hTBWnd, &rc); /* Get Toolbar size. */
+ pTTYInfo->toolBarSize = (CORD)(rc.bottom - rc.top);
+
+ GetClientRect (hWnd, &rc); /* Get TTY window size. */
+ SizeTTY (hWnd, 0, (CORD)rc.bottom, (CORD)rc.right);
+ ShowCaret (hWnd);
+}
+
+
+LOCAL void
+TBHide (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ if (pTTYInfo->toolBarSize == 0)
+ return;
+
+ DestroyWindow (pTTYInfo->hTBWnd);
+ pTTYInfo->hTBWnd = NULL;
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = NULL;
+
+
+ /*
+ * Adjust the window size.
+ */
+ pTTYInfo->toolBarSize = 0;
+ GetClientRect (hWnd, &rc);
+ SizeTTY (hWnd, 0, (CORD)rc.bottom, (CORD)rc.right);
+}
+
+
+LOCAL void
+TBSwap (HWND hWnd, int newID)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (pTTYInfo->toolBarSize == 0 || pTTYInfo->curToolBarID == newID)
+ return;
+
+ /*
+ * Dispose of old tool bar window.
+ */
+ HideCaret (hWnd);
+
+ DestroyWindow (pTTYInfo->hTBWnd);
+ pTTYInfo->hTBWnd = NULL;
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = NULL;
+
+
+
+ /*
+ * Create the new dialog box.
+ */
+ pTTYInfo->hTBWnd = CreateDialog (ghInstance,
+ MAKEINTRESOURCE (newID),
+ hWnd,
+ gToolBarProc);
+ if (pTTYInfo->hTBWnd == NULL) {
+ ShowCaret (hWnd);
+ return;
+ }
+ pTTYInfo->curToolBarID = newID;
+ SetFocus (hWnd); /* Return focus to parent. */
+
+
+ /*
+ * Fit new tool bar into old tool bars position. This assumes that
+ * all tool bars are about the same height.
+ */
+ GetClientRect (hWnd, &rc); /* Get TTY window size. */
+ if (pTTYInfo->toolBarTop)
+ /* Position at top of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, 0,
+ rc.right, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ else
+ /* Position at bottom of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, pTTYInfo->ySize - pTTYInfo->toolBarSize,
+ rc.right, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+
+ ShowCaret (hWnd);
+}
+
+
+BOOL FAR PASCAL __export
+ToolBarProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ BOOL ret;
+ int height;
+ HBRUSH hBrush;
+ HWND hCld;
+ int btnCount;
+ int i;
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = gpTTYInfo;
+
+ ret = FALSE;
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ /* Fit dialog to window. */
+ GetWindowRect (hWnd, &rc);
+ height = rc.bottom - rc.top;
+ GetClientRect (GetParent (hWnd), &rc);
+ SetWindowPos (hWnd, HWND_TOP, 0, 0, rc.right, height,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ /* Count child windows.*/
+ btnCount = 0;
+ for (hCld = GetWindow (hWnd, GW_CHILD);
+ hCld;
+ hCld = GetWindow (hCld, GW_HWNDNEXT))
+ ++btnCount;
+
+ /* Allocate a list of previous child procs. */
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = MemAlloc (sizeof (BtnList) * (btnCount + 1));
+
+ /* Subclass all child windows. */
+ for (i = 0, hCld = GetWindow (hWnd, GW_CHILD);
+ hCld;
+ ++i, hCld = GetWindow (hCld, GW_HWNDNEXT)) {
+ pTTYInfo->toolBarBtns[i].wndID = GET_ID (hCld);
+ pTTYInfo->toolBarBtns[i].wndProc =
+ (WNDPROC)(LONG_PTR)MyGetWindowLongPtr (hCld,
+ GWLP_WNDPROC);
+ MySetWindowLongPtr (hCld, GWLP_WNDPROC, (void *)(LONG_PTR)TBBtnProc);
+ }
+ pTTYInfo->toolBarBtns[i].wndID = 0;
+ pTTYInfo->toolBarBtns[i].wndProc = NULL;
+
+ ret = FALSE;
+ break;
+
+
+ case WM_COMMAND:
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND){
+ ProcessMenuItem (GetParent (hWnd), wParam);
+ /* Set input focus back to parent. */
+ SetFocus (GetParent (hWnd));
+ ret = TRUE;
+ break;
+ }
+ break;
+
+#ifdef WIN32
+ case WM_CTLCOLORBTN:
+#else
+ case WM_CTLCOLOR:
+#endif
+ if (HIWORD (lParam) == CTLCOLOR_DLG) {
+ if(pTTYInfo->hTBBrush != NULL){
+ DeleteObject(pTTYInfo->hTBBrush);
+ pTTYInfo->hTBBrush = NULL;
+ }
+
+ hBrush = CreateSolidBrush (GetSysColor (COLOR_ACTIVEBORDER));
+ return ((BOOL)!!(pTTYInfo->hTBBrush = hBrush));
+ }
+ }
+ return (ret);
+}
+
+
+/*
+ * Subclass toolbar button windows.
+ *
+ * These buttons will automatically return the input focus to
+ * the toolbar's parent
+ */
+LRESULT FAR PASCAL __export
+TBBtnProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ PTTYINFO pTTYInfo;
+ HWND hPrnt;
+ int i;
+ WORD id;
+ LRESULT ret;
+ WNDPROC wndProc;
+
+
+ /*
+ * Find previous window proc.
+ */
+ pTTYInfo = gpTTYInfo;
+ id = GET_ID (hBtn);
+ for (i = 0; pTTYInfo->toolBarBtns[i].wndID != 0; ++i)
+ if (pTTYInfo->toolBarBtns[i].wndID == id)
+ goto FoundWindow;
+ /* Whoops! Didn't find window, don't know how to pass message. */
+ return (0);
+
+
+FoundWindow:
+ wndProc = pTTYInfo->toolBarBtns[i].wndProc;
+
+
+
+ if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
+ /*
+ * On mouse button up restore input focus to IDC_RESPONCE, which
+ * processes keyboard input.
+ */
+ ret = CallWindowProc (wndProc, hBtn, uMsg, wParam, lParam);
+ hPrnt = GetParent (GetParent (hBtn));
+ if (hPrnt)
+ SetFocus (hPrnt);
+ return (ret);
+ }
+
+ return (CallWindowProc (wndProc, hBtn, uMsg, wParam, lParam));
+}
+
+
+/*
+ * return bitmap of allowed Edit Menu items
+ */
+LOCAL UINT
+UpdateEditAllowed(HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ UINT fAccel = EM_NONE;
+
+ if((pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)) != NULL){
+ if(EditPasteAvailable())
+ fAccel |= (EM_PST | EM_PST_ABORT);
+ else if(IsClipboardFormatAvailable (CF_UNICODETEXT) && gPasteEnabled)
+ fAccel |= EM_PST;
+
+ if(SelAvailable()){
+ fAccel |= (EM_CP | EM_CP_APPEND);
+ }
+ else{
+ if(gAllowCut)
+ fAccel |= EM_CUT;
+
+ if(gAllowCopy)
+ fAccel |= (EM_CP | EM_CP_APPEND);
+ }
+
+ if (pTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miActive)
+ fAccel |= EM_FIND;
+ }
+ return(fAccel);
+}
+
+
+#ifdef ACCELERATORS
+#ifdef ACCELERATORS_OPT
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Accelorator key routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+AccelCtl (HWND hWnd, int ctl, BOOL saveChange)
+{
+ PTTYINFO pTTYInfo;
+ BOOL load, changed;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ switch (ctl) {
+ case ACCEL_LOAD :
+ load = TRUE;
+ break;
+ case ACCEL_UNLOAD :
+ load = FALSE;
+ break;
+ case ACCEL_TOGGLE :
+ load = pTTYInfo->hAccel == NULL;
+ break;
+ default :
+ load = FALSE;
+ break;
+ }
+
+ changed = FALSE;
+ if (load && pTTYInfo->hAccel == NULL) {
+ /* Load em up. */
+ pTTYInfo->hAccel = LoadAccelerators (ghInstance,
+ MAKEINTRESOURCE (IDR_ACCEL_PINE));
+ changed = TRUE;
+ }
+ else if(!load && pTTYInfo->hAccel) {
+ /* unload em. */
+ FreeResource (pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ changed = TRUE;
+ }
+
+ if (changed && saveChange)
+ DidResize (pTTYInfo);
+}
+#endif
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Accelorator key routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+AccelManage (HWND hWnd, long accels)
+{
+ PTTYINFO pTTYInfo;
+ ACCEL accelarray[EM_MAX_ACCEL];
+ int n;
+ static ACCEL am_cp = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'C', IDM_EDIT_COPY
+ };
+ static ACCEL am_cp_append = {
+ FVIRTKEY | FCONTROL | FALT | FNOINVERT, 'C', IDM_EDIT_COPY_APPEND
+ };
+ static ACCEL am_find = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'F', IDM_MI_WHEREIS
+ };
+ static ACCEL am_pst = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'V', IDM_EDIT_PASTE
+ };
+ static ACCEL am_pst_abort = {
+ FVIRTKEY | FCONTROL | FALT | FNOINVERT, 'V', IDM_EDIT_CANCEL_PASTE
+ };
+ static ACCEL am_cut = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'X', IDM_EDIT_CUT
+ };
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL || pTTYInfo->fAccel == (UINT)accels)
+ return;
+
+ if(pTTYInfo->hAccel){
+ DestroyAcceleratorTable(pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ pTTYInfo->fAccel = EM_NONE;
+ }
+
+ n = 0;
+
+ if(accels & EM_CP)
+ accelarray[n++] = am_cp;
+
+ if(accels & EM_CP_APPEND)
+ accelarray[n++] = am_cp_append;
+
+ if(accels & EM_FIND)
+ accelarray[n++] = am_find;
+
+ if(accels & EM_PST)
+ accelarray[n++] = am_pst;
+
+ if(accels & EM_PST_ABORT)
+ accelarray[n++] = am_pst_abort;
+
+ if(accels & EM_CUT)
+ accelarray[n++] = am_cut;
+
+ /* Load em up. */
+ if(n && (pTTYInfo->hAccel = CreateAcceleratorTable(accelarray, n)))
+ pTTYInfo->fAccel = accels;
+ else
+ pTTYInfo->fAccel = EM_NONE;
+}
+
+
+LOCAL void
+UpdateAccelerators (HWND hWnd)
+{
+ AccelManage (hWnd, UpdateEditAllowed(hWnd));
+}
+#endif
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Mouse Selection routines
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL BOOL SelSelected = FALSE;
+LOCAL BOOL SelTracking = FALSE;
+LOCAL int SelAnchorRow;
+LOCAL int SelAnchorCol;
+LOCAL int SelPointerRow;
+LOCAL int SelPointerCol;
+typedef struct {
+ TCHAR *pRow;
+ int len;
+} CopyRow;
+
+
+LOCAL void
+SelRSet (int oStart, int oEnd)
+{
+ CharAttrib *pca;
+
+ for (pca = gpTTYInfo->pAttrib + oStart; oStart < oEnd; ++pca, ++oStart)
+ pca->style |= CHAR_ATTR_SEL;
+}
+
+
+LOCAL void
+SelRClear (int oStart, int oEnd)
+{
+ CharAttrib *pca;
+
+ for (pca = gpTTYInfo->pAttrib + oStart; oStart < oEnd; ++pca, ++oStart)
+ pca->style &= ~CHAR_ATTR_SEL;
+}
+
+
+LOCAL void
+SelRInvalidate (int oStart, int oEnd)
+{
+ RECT rect;
+ int sRow, sCol;
+ int eRow, eCol;
+
+ sRow = oStart / gpTTYInfo->actNColumn;
+ sCol = oStart % gpTTYInfo->actNColumn;
+ eRow = oEnd / gpTTYInfo->actNColumn;
+ eCol = oEnd % gpTTYInfo->actNColumn;
+
+ rect.top = (sRow * gpTTYInfo->yChar) + gpTTYInfo->yOffset;
+ rect.bottom = ((eRow+1) * gpTTYInfo->yChar) + gpTTYInfo->yOffset;
+ if (sRow == eRow) {
+ rect.left = (sCol * gpTTYInfo->xChar) + gpTTYInfo->xOffset;
+ rect.right = ((eCol+1) * gpTTYInfo->xChar) + gpTTYInfo->xOffset;
+ } else {
+ rect.left = gpTTYInfo->xOffset;
+ rect.right = (gpTTYInfo->actNColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ }
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+}
+
+
+/*
+ * Start a mouse selection.
+ */
+LOCAL void
+SelStart (int nRow, int nColumn)
+{
+ SelClear ();
+ SelTracking = TRUE;
+ SelSelected = TRUE;
+ SelPointerRow = SelAnchorRow = nRow;
+ SelPointerCol = SelAnchorCol = nColumn;
+ return;
+}
+
+
+/*
+ * Finish a mouse selection.
+ */
+LOCAL void
+SelFinish (int nRow, int nColumn)
+{
+ if (nRow == SelAnchorRow && nColumn == SelAnchorCol) {
+ /* Mouse up in same place it went down - no selection. */
+ SelClear ();
+ }
+ else {
+ /* Update screen selection and set final position of mouse
+ * then turn of mouse tracking. Selection remains in effect
+ * until SelClear is called. */
+ SelTrackMouse (nRow, nColumn);
+ SelTracking = FALSE;
+ }
+}
+
+
+LOCAL void
+SelClear (void)
+{
+ int a, p;
+ int s, e;
+
+ if (!SelSelected)
+ return;
+
+ /* Convert the anchor and point coordinates to offsets then
+ * order the offsets. */
+ a = (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol;
+ p = (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol;
+ if (a < p) {
+ s = a;
+ e = p;
+ } else {
+ s = p;
+ e = a;
+ }
+
+ /* Clear selected attribute of those cells in range. */
+ SelRClear (s, e);
+ SelRInvalidate (s, e);
+ SelSelected = FALSE;
+ SelTracking = FALSE;
+}
+
+
+/*
+ * Update the position of the mouse point.
+ */
+LOCAL void
+SelTrackXYMouse (int xPos, int yPos)
+{
+ int nRow;
+ int nColumn;
+
+ nColumn = (xPos - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ nRow = (yPos - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+
+ SelTrackMouse (nRow, nColumn);
+}
+
+
+/*
+ * Update the position of the mouse point.
+ */
+LOCAL void
+SelTrackMouse (int nRow, int nColumn)
+{
+ int a, p, n;
+
+ if (!SelTracking)
+ return;
+
+ /* Constrain the cel position to be on the screen. But allow
+ * for the Column to be one past the right edge of the screen so
+ * the user can select the right most cel of a row. */
+ nColumn = MAX(0, nColumn);
+ nColumn = MIN(gpTTYInfo->actNColumn, nColumn);
+ nRow = MAX(0, nRow);
+ nRow = MIN(gpTTYInfo->actNRow-1, nRow);
+
+
+ /* Convert the anchor, previous mouse position, and new mouse
+ * position to offsets. */
+ a = (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol;
+ p = (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol;
+ n = (nRow * gpTTYInfo->actNColumn) + nColumn;
+
+ /* If previous position same as current position, do nothing. */
+ if (p == n)
+ return;
+
+ /* there are six possible orderings of the points, each with
+ * a different action:
+ * order clear set redraw
+ * n p a n - p n - p
+ * p n a p - n p - n
+ * p a n p - a a - n p - n
+ * a p n p - n p - n
+ * a n p n - p n - p
+ * n a p a - p n - a n - p
+ */
+ if (p < a) {
+ if (n < a) {
+ if (n < p) {
+ SelRSet (n, p);
+ SelRInvalidate (n, p);
+ } else {
+ SelRClear (p, n);
+ SelRInvalidate (p, n);
+ }
+ } else {
+ SelRClear (p, a);
+ SelRSet (a, n);
+ SelRInvalidate (p, n);
+ }
+ } else {
+ if (n > a) {
+ if (n > p) {
+ SelRSet (p, n);
+ SelRInvalidate (p, n);
+ } else {
+ SelRClear (n, p);
+ SelRInvalidate (n, p);
+ }
+ } else {
+ SelRClear (a, p);
+ SelRSet (n, a);
+ SelRInvalidate (n, p);
+ }
+ }
+
+ /* Set new pointer. */
+ SelPointerRow = nRow;
+ SelPointerCol = nColumn;
+}
+
+
+LOCAL BOOL
+SelAvailable (void)
+{
+ return (SelSelected);
+}
+
+
+/*
+ * Copy screen data to clipboard. Actually appends data from screen to
+ * existing handle so as we can implement a "Copy Append" to append to
+ * existing clipboard data.
+ *
+ * The screen does not have real line terminators. We decide where the
+ * actual screen data ends by scanning the line (row) from end backwards
+ * to find the first non-space.
+ *
+ * I don't know how many bytes of data I'll be appending to the clipboard.
+ * So I implemented in two passes. The first finds all the row starts
+ * and length while the second copies the data.
+ */
+LOCAL void
+SelDoCopy (HANDLE hCB, DWORD lenCB)
+{
+ HANDLE newCB; /* Used in reallocation. */
+ TCHAR *pCB; /* Points to CB data. */
+ TCHAR *p2; /* Temp pointer to screen data. */
+ int sRow, eRow; /* Start and End Rows. */
+ int sCol, eCol; /* Start and End columns. */
+ int row, c1, c2; /* temp row and column indexes. */
+ int totalLen; /* total len of new data. */
+ CopyRow *rowTable, *rp; /* pointers to table of rows. */
+ BOOL noLastCRLF = FALSE;
+
+
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()) { /* ...and clear previous CB.*/
+
+
+ /* Find the start and end row and column. */
+ if ( (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol <
+ (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol) {
+ sRow = SelAnchorRow;
+ sCol = SelAnchorCol;
+ eRow = SelPointerRow;
+ eCol = SelPointerCol;
+ }
+ else {
+ sRow = SelPointerRow;
+ sCol = SelPointerCol;
+ eRow = SelAnchorRow;
+ eCol = SelAnchorCol;
+ }
+
+ /* Allocate a table in which we store info on rows. */
+ rowTable = (CopyRow *) MemAlloc (sizeof (CopyRow) * (eRow-sRow+1));
+ if (rowTable == NULL)
+ goto Fail1;
+
+ /* Find the start and length of each row. */
+ totalLen = 0;
+ for (row = sRow, rp = rowTable; row <= eRow; ++row, ++rp) {
+ /* Find beginning and end columns, which depends on if
+ * this is the first or last row in the selection. */
+ c1 = (row == sRow ? sCol : 0);
+ c2 = (row == eRow ? eCol : gpTTYInfo->actNColumn);
+
+ /* Calculate pointer to beginning of this line. */
+ rp->pRow = gpTTYInfo->pScreen +
+ ((row * gpTTYInfo->actNColumn) + c1);
+
+ /* Back down from end column to find first non space.
+ * noLastCRLF indicates if it looks like the selection
+ * should include a CRLF on the end of the line. It
+ * gets set for each line, but only the setting for the
+ * last line in the selection is remembered (which is all
+ * we're interested in). */
+ p2 = gpTTYInfo->pScreen +
+ ((row * gpTTYInfo->actNColumn) + c2);
+ noLastCRLF = TRUE;
+ while (c2 > c1) {
+ if (*(p2-1) != (TCHAR)' ')
+ break;
+ noLastCRLF = FALSE;
+ --c2;
+ --p2;
+ }
+
+ /* Calculate size of line, then increment totalLen plus 2 for
+ * the CRLF which will terminate each line. */
+ rp->len = c2 - c1;
+ totalLen += rp->len + 2;
+ }
+
+ /* Reallocate the memory block. Add one byte for null terminator. */
+ newCB = GlobalReAlloc (hCB, (lenCB + totalLen + 1)*sizeof(TCHAR), 0);
+ if (newCB == NULL)
+ goto Fail2;
+ hCB = newCB;
+
+ pCB = GlobalLock (hCB);
+ if (pCB == NULL)
+ goto Fail2;
+
+ /* Append each of the rows, deliminated by a CRLF. */
+ pCB += lenCB;
+ for (row = sRow, rp = rowTable; row <= eRow; ++row, ++rp) {
+ if (rp->len > 0) {
+ memcpy (pCB, rp->pRow, rp->len * sizeof(TCHAR));
+ pCB += rp->len;
+ }
+ if (row < eRow || !noLastCRLF) {
+ *pCB++ = (TCHAR)ASCII_CR;
+ *pCB++ = (TCHAR)ASCII_LF;
+ }
+ }
+ *pCB = (TCHAR)'\0'; /* Null terminator. */
+ MemFree (rowTable);
+ GlobalUnlock (hCB);
+
+ /* Attempt to pass the data to the clipboard, then release clipboard
+ * and exit function. */
+ if (SetClipboardData (CF_UNICODETEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ CloseClipboard ();
+ }
+ }
+ return;
+
+
+ /* Error exit. */
+Fail2: MemFree (rowTable);
+Fail1: GlobalFree (hCB);
+ CloseClipboard ();
+ return;
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Upper Layer Screen routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Flush the write accumulator buffer.
+ */
+LOCAL void
+FlushWriteAccum (void)
+{
+ if (gpTTYInfo->writeAccumCount > 0) {
+ WriteTTYText (ghTTYWnd, gpTTYInfo->writeAccum,
+ gpTTYInfo->writeAccumCount);
+ gpTTYInfo->writeAccumCount = 0;
+ }
+}
+
+
+/*
+ * Set window's title
+ */
+void
+mswin_settitle(char *utf8_title)
+{
+ TCHAR buf[256];
+ LPTSTR lptstr_title;
+
+ lptstr_title = utf8_to_lptstr(utf8_title);
+ _sntprintf(buf, 256, TEXT("%.*s - Alpine"), 80, lptstr_title);
+ fs_give((void **) &lptstr_title);
+
+ SetWindowText(ghTTYWnd, buf);
+}
+
+
+/*
+ * Return the application instance.
+ */
+WINHAND
+mswin_gethinstance ()
+{
+ return ((WINHAND)ghInstance);
+}
+
+
+WINHAND
+mswin_gethwnd ()
+{
+ return ((WINHAND)ghTTYWnd);
+}
+
+
+/*
+ * destroy splash screen
+ */
+void
+mswin_killsplash()
+{
+ if(ghSplashWnd != NULL){
+ DestroyWindow(ghSplashWnd);
+ ghSplashWnd = NULL;
+ }
+}
+
+
+/*
+ * Called to get mouse event.
+ */
+int
+mswin_getmouseevent (MEvent * pMouse)
+{
+ return (MQGet (pMouse));
+}
+
+
+/*
+ * Make a pop-up menu and track it
+ */
+int
+mswin_popup(MPopup *members)
+{
+ HMENU hMenu;
+ POINT point;
+ MPopup *mp;
+ int n = 1;
+
+ if(members && (hMenu = CreatePopupMenu())){
+
+ PopupConfig(hMenu, members, &n);
+ if(GetCursorPos(&point)
+ && (n = TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON
+ | TPM_NONOTIFY | TPM_RETURNCMD,
+ point.x, point.y, 0, ghTTYWnd, NULL))
+ && (mp = PopupId(members, n))){
+ /* Find the member with the internal.id of n */
+ n = mp->internal.id - 1; /* item's "selectable" index */
+ switch(mp->type){
+ case tQueue :
+ CQAdd (mp->data.val, 0);
+ break;
+
+ case tMessage :
+ SendMessage(ghTTYWnd, WM_COMMAND, mp->data.msg, 0);
+ break;
+
+ default :
+ break;
+ }
+ }
+ else
+ n = -1; /* error or nothing selected; same diff */
+
+ DestroyMenu(hMenu);
+ }
+ else
+ n = -1; /* error */
+
+ return(n);
+}
+
+
+LOCAL void
+PopupConfig(HMENU hMenu, MPopup *members, int *n)
+{
+ MENUITEMINFO mitem;
+ int index;
+ TCHAR tcbuf[256];
+
+ for(index = 0; members->type != tTail; members++, index++){
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE;
+
+ if(members->type == tSubMenu){
+ mitem.fMask |= MIIM_SUBMENU;
+ mitem.fType = MFT_STRING;
+ if(mitem.hSubMenu = CreatePopupMenu()){
+ /* members->label.string is still just a char * */
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), members->label.string);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, index, TRUE, &mitem);
+
+ PopupConfig(mitem.hSubMenu, members->data.submenu, n);
+ }
+ }
+ else{
+ switch(members->type){
+ case tSeparator :
+ mitem.fType = MFT_SEPARATOR;
+ break;
+
+ default :
+ mitem.fMask |= MIIM_ID;
+ mitem.wID = members->internal.id = (*n)++;
+ switch(members->label.style){
+ case lChecked :
+ mitem.fMask |= MIIM_STATE;
+ mitem.fState = MFS_CHECKED;
+ break;
+
+ case lDisabled :
+ mitem.fMask |= MIIM_STATE;
+ mitem.fState = MFS_GRAYED;
+ break;
+
+ case lNormal :
+ default :
+ break;
+ }
+
+ mitem.fType = MFT_STRING;
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), members->label.string);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ break;
+ }
+
+ InsertMenuItem(hMenu, index, TRUE, &mitem);
+ }
+ }
+}
+
+
+LOCAL MPopup *
+PopupId(MPopup *members, int id)
+{
+ MPopup *mp;
+
+ for(; members->type != tTail; members++)
+ switch(members->type){
+ case tSubMenu :
+ if(mp = PopupId(members->data.submenu, id))
+ return(mp);
+
+ break;
+
+ case tSeparator :
+ break;
+
+ default :
+ if(members->internal.id == id)
+ return(members);
+
+ break;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Make a pop-up offering Copy/Cut/Paste menu and track it
+ */
+LOCAL BOOL
+CopyCutPopup(HWND hWnd, UINT fAllowed)
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ BOOL rv = FALSE;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(fAllowed != EM_NONE && (hMenu = CreatePopupMenu())){
+ if(fAllowed & EM_CUT){
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_CUT;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Cut");
+ mitem.cch = 3;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_CP){
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_COPY;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy");
+ mitem.cch = 4;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_CP_APPEND){
+ /* Insert a "Copy Append" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_COPY_APPEND;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy Append");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_PST){
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if((fAllowed & EM_SEL_ALL) && !(fAllowed & (EM_CP | EM_CP_APPEND))){
+ /* Insert a "Select All" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_SEL_ALL;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Select &All");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(n >= 0 && GetCursorPos(&point)){
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, hWnd, NULL);
+ rv = TRUE;
+ }
+
+ DestroyMenu(hMenu);
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ *
+ */
+void
+pico_popup()
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ UINT fAllow;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(hMenu = CreatePopupMenu()){
+
+ if((fAllow = UpdateEditAllowed(ghTTYWnd)) != EM_NONE){
+ /* Insert a "Cut" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_CUT;
+ mitem.fState = (fAllow & EM_CUT) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Cut");
+ mitem.cch = 3;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_COPY;
+ mitem.fState = (fAllow & EM_CP) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy");
+ mitem.cch = 4;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Copy Append" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_COPY_APPEND;
+ mitem.fState = (fAllow & EM_CP_APPEND) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy Append");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.fState = (fAllow & EM_PST) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ /* Insert a "Select All" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_SEL_ALL;
+ mitem.fType = MFT_STRING;
+ mitem.fState = (fAllow & (EM_CP | EM_CP_APPEND))
+ ? MFS_GRAYED : MFS_ENABLED;
+ mitem.dwTypeData = TEXT("Select &All");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ if(n >= 0 && GetCursorPos(&point))
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+/*
+ *
+ */
+void
+mswin_paste_popup()
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ UINT fAllow;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(hMenu = CreatePopupMenu()){
+
+ if((fAllow = UpdateEditAllowed(ghTTYWnd)) != EM_NONE){
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.fState = (fAllow & EM_PST) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ if(n >= 0 && GetCursorPos(&point))
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+ }
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+void
+mswin_keymenu_popup()
+{
+ HMENU hBarMenu, hMenu;
+ MENUITEMINFO mitem;
+ POINT point;
+ int i, j, n;
+ TCHAR tcbuf[256];
+
+ /*
+ * run thru menubar from left to right and down each list building
+ * a popup of all active members. we run down the menu's rather
+ * than thru the menuItems array so we can preserve order...
+ */
+ if((hMenu = CreatePopupMenu())){
+ if(hBarMenu = GetMenu(ghTTYWnd)){
+ /* For each possible index, look for matching registered key */
+ for(n = 0, i = 1; i <= KS_COUNT; i++)
+ for(j = 0; j < KS_COUNT; j++)
+ if(gpTTYInfo->menuItems[j].miIndex == i){
+ if(gpTTYInfo->menuItems[j].miActive == TRUE
+ && gpTTYInfo->menuItems[j].miLabel){
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
+ mitem.wID = j + KS_RANGESTART;
+ mitem.fState = MFS_ENABLED;
+ mitem.fType = MFT_STRING;
+ /* miLabel is still plain old char *, not utf8 */
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), gpTTYInfo->menuItems[j].miLabel);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, n++, TRUE, &mitem);
+
+ if(j + KS_RANGESTART == IDM_MI_SCREENHELP
+ && gHelpCallback){
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
+ mitem.wID = IDM_HELP;
+ mitem.fState = MFS_ENABLED;
+ mitem.fType = MFT_STRING;
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), "Help in New Window");
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, n++, TRUE, &mitem);
+ }
+ }
+
+ break;
+ }
+
+ if(n > 0 && GetCursorPos(&point)){
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+ }
+ }
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+/*
+ *
+ */
+void
+mswin_registericon(int row, int id, char *utf8_file)
+{
+ LPTSTR sPathCopy;
+ HICON hIcon;
+ WORD iIcon;
+ IconList *pIcon;
+
+ /* Turn this off until it can get tuned */
+ return;
+
+ /* Already registered? */
+ for(pIcon = gIconList; pIcon; pIcon = pIcon->next)
+ if(pIcon->id == id){
+ pIcon->row = (short)row;
+ return;
+ }
+
+ sPathCopy = utf8_to_lptstr(utf8_file);
+
+ if(hIcon = ExtractAssociatedIcon(ghInstance, sPathCopy, &iIcon))
+ MSWIconAddList(row, id, hIcon);
+
+ fs_give((void **) &sPathCopy);
+}
+
+
+void
+mswin_destroyicons()
+{
+ MSWIconFree(&gIconList);
+}
+
+
+void
+MSWIconAddList(int row, int id, HICON hIcon)
+{
+ IconList **ppIcon;
+
+ for(ppIcon = &gIconList; *ppIcon; ppIcon = &(*ppIcon)->next)
+ ;
+
+ *ppIcon = (IconList *) MemAlloc (sizeof (IconList));
+ memset(*ppIcon, 0, sizeof(IconList));
+ (*ppIcon)->hIcon = hIcon;
+ (*ppIcon)->id = id;
+ (*ppIcon)->row = (short)row;
+}
+
+
+int
+MSWIconPaint(int row, HDC hDC)
+{
+ IconList *pIcon;
+ int rv = 0;
+
+ for(pIcon = gIconList; pIcon && pIcon->row != row; pIcon = pIcon->next)
+ ;
+
+ if(pIcon){
+ /* Invalidate rectange covering singel character. */
+ DrawIconEx(hDC, 0, (row * gpTTYInfo->yChar) + gpTTYInfo->yOffset,
+ pIcon->hIcon, 2 * gpTTYInfo->xChar, gpTTYInfo->yChar,
+ 0, NULL, DI_NORMAL);
+
+ rv = 1;
+ }
+
+ return(rv);
+}
+
+
+void
+MSWIconFree(IconList **ppIcon)
+{
+ if(ppIcon && *ppIcon){
+ if((*ppIcon)->next)
+ MSWIconFree(&(*ppIcon)->next);
+
+ DestroyIcon((*ppIcon)->hIcon);
+ MemFree (*ppIcon);
+ *ppIcon = NULL;
+ }
+}
+
+
+/*
+ * Set up debugging stuff.
+ */
+void
+mswin_setdebug (int debug, FILE *debugfile)
+{
+ /* Accept new file only if we don't already have a file. */
+ if (mswin_debugfile == 0) {
+ mswin_debug = debug;
+ mswin_debugfile = debugfile;
+ MemDebug (debug, mswin_debugfile);
+ }
+}
+
+
+void
+mswin_setnewmailwidth (int width)
+{
+ gNMW_width = width;
+ gNMW_width = gNMW_width < 20 ? 20 : gNMW_width;
+ gNMW_width = gNMW_width > 170 ? 170 : gNMW_width;
+}
+
+
+/*
+ * Event handler to deal with File Drop events
+ */
+LOCAL BOOL
+ProcessTTYFileDrop (HANDLE wDrop)
+{
+ HDROP hDrop = wDrop;
+ POINT pos;
+ int col, row, i, n;
+ TCHAR fname[1024];
+ char *utf8_fname;
+
+ if(!gpTTYInfo->dndhandler)
+ return(FALSE);
+
+ /* translate drop point to character cell */
+ DragQueryPoint(hDrop, &pos);
+ col = (pos.x - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ if (pos.x < gpTTYInfo->xOffset)
+ --col;
+ row = (pos.y - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+ if (pos.y < gpTTYInfo->yOffset)
+ --row;
+
+ for(n = DragQueryFile(hDrop, (UINT)-1, NULL, 0), i = 0; i < n; i++){
+ DragQueryFile(hDrop, i, fname, 1024);
+ utf8_fname = lptstr_to_utf8(fname);
+ gpTTYInfo->dndhandler (row, col, utf8_fname);
+ fs_give((void **) &utf8_fname);
+ }
+
+ DragFinish(hDrop);
+
+ set_time_of_last_input();
+
+ return(TRUE);
+}
+
+
+/*
+ * Set a callback to deal with Drag 'N Drop events
+ */
+int
+mswin_setdndcallback (int (*cb)())
+{
+ if(gpTTYInfo->dndhandler)
+ gpTTYInfo->dndhandler = NULL;
+
+ if(cb){
+ gpTTYInfo->dndhandler = cb;
+ DragAcceptFiles(ghTTYWnd, TRUE);
+ }
+
+ return(1);
+}
+
+
+/*
+ * Clear previously installed callback to handle Drag 'N Drop
+ * events
+ */
+int
+mswin_cleardndcallback ()
+{
+ gpTTYInfo->dndhandler = NULL;
+ DragAcceptFiles(ghTTYWnd, FALSE);
+ return(1);
+}
+
+
+/*
+ * Set a callback for function 'ch'
+ */
+int
+mswin_setresizecallback (int (*cb)())
+{
+ int i;
+ int e;
+
+
+ /*
+ * Look through whole array for this call back function. Don't
+ * insert duplicate. Also look for empty slot.
+ */
+ e = -1;
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (gpTTYInfo->resizer[i] == cb)
+ return (0);
+ if (e == -1 && gpTTYInfo->resizer[i] == NULL)
+ e = i;
+ }
+
+ /*
+ * Insert in empty slot or return an error.
+ */
+ if (e != -1) {
+ gpTTYInfo->resizer[e] = cb;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/*
+ * Clear all instances of the callback function 'cb'
+ */
+int
+mswin_clearresizecallback (int (*cb)())
+{
+ int i;
+ int status;
+
+ status = -1;
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (gpTTYInfo->resizer[i] == cb) {
+ gpTTYInfo->resizer[i] = NULL;
+ status = 0;
+ }
+ }
+ return (status);
+}
+
+
+void
+mswin_beginupdate (void)
+{
+ gpTTYInfo->fMassiveUpdate = TRUE;
+}
+
+
+void
+mswin_endupdate (void)
+{
+ gpTTYInfo->fMassiveUpdate = FALSE;
+ MoveTTYCursor (ghTTYWnd);
+}
+
+
+int
+mswin_charsetid2string(LPTSTR fontCharSet, size_t nfontCharSet, BYTE lfCharSet)
+{
+ TCHAR buf[1024];
+
+ buf[0] = '\0';
+ switch(lfCharSet){
+ case DEFAULT_CHARSET:
+ break;
+ case ANSI_CHARSET:
+ _tcsncpy(buf, TEXT("ANSI_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case OEM_CHARSET:
+ _tcsncpy(buf, TEXT("OEM_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case BALTIC_CHARSET:
+ _tcsncpy(buf, TEXT("BALTIC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case CHINESEBIG5_CHARSET:
+ _tcsncpy(buf, TEXT("CHINESE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case EASTEUROPE_CHARSET:
+ _tcsncpy(buf, TEXT("EASTEUROPE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case GB2312_CHARSET:
+ _tcsncpy(buf, TEXT("GF2312_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case GREEK_CHARSET:
+ _tcsncpy(buf, TEXT("GREEK_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case HANGUL_CHARSET:
+ _tcsncpy(buf, TEXT("HANGUL_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case MAC_CHARSET:
+ _tcsncpy(buf, TEXT("MAC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case RUSSIAN_CHARSET:
+ _tcsncpy(buf, TEXT("RUSSIAN_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case SHIFTJIS_CHARSET:
+ _tcsncpy(buf, TEXT("SHIFTJIS_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case SYMBOL_CHARSET:
+ _tcsncpy(buf, TEXT("SYMBOL_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case TURKISH_CHARSET:
+ _tcsncpy(buf, TEXT("TURKISH_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case VIETNAMESE_CHARSET:
+ _tcsncpy(buf, TEXT("VIETNAMESE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case JOHAB_CHARSET:
+ _tcsncpy(buf, TEXT("JOHAB_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case ARABIC_CHARSET:
+ _tcsncpy(buf, TEXT("ARABIC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case HEBREW_CHARSET:
+ _tcsncpy(buf, TEXT("HEBREW_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case THAI_CHARSET:
+ _tcsncpy(buf, TEXT("THAI_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ default:
+ /* default char set if we can't figure it out */
+ break;
+ }
+
+ buf[sizeof(buf)/sizeof(TCHAR) - 1] = '\0';
+
+ _tcsncpy(fontCharSet, buf, nfontCharSet);
+ fontCharSet[nfontCharSet-1] = '\0';
+
+ return 0;
+}
+
+
+BYTE
+mswin_string2charsetid(LPTSTR str)
+{
+ TCHAR tstr[1024];
+
+ if(!str || (lstrlen(str) >= 1024))
+ return DEFAULT_CHARSET;
+
+ if((lstrlen(str) > lstrlen(TEXT("_CHARSET")))
+ && (tcsucmp(str + lstrlen(str) - lstrlen(TEXT("_CHARSET")), TEXT("_CHARSET")) == 0)){
+ _tcsncpy(tstr, str, 1024);
+ tstr[lstrlen(str) - lstrlen(TEXT("_CHARSET"))] = '\0';
+ tstr[1024-1] = '\0';
+ if(tcsucmp(tstr, TEXT("ANSI")) == 0)
+ return ANSI_CHARSET;
+ else if(tcsucmp(tstr, TEXT("DEFAULT")) == 0)
+ return DEFAULT_CHARSET;
+ else if(tcsucmp(tstr, TEXT("OEM")) == 0)
+ return OEM_CHARSET;
+ else if(tcsucmp(tstr, TEXT("BALTIC")) == 0)
+ return BALTIC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("CHINESEBIG5")) == 0)
+ return CHINESEBIG5_CHARSET;
+ else if(tcsucmp(tstr, TEXT("EASTEUROPE")) == 0)
+ return EASTEUROPE_CHARSET;
+ else if(tcsucmp(tstr, TEXT("GB2312")) == 0)
+ return GB2312_CHARSET;
+ else if(tcsucmp(tstr, TEXT("GREEK")) == 0)
+ return GREEK_CHARSET;
+ else if(tcsucmp(tstr, TEXT("HANGUL")) == 0)
+ return HANGUL_CHARSET;
+ else if(tcsucmp(tstr, TEXT("MAC")) == 0)
+ return MAC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("RUSSIAN")) == 0)
+ return RUSSIAN_CHARSET;
+ else if(tcsucmp(tstr, TEXT("SHIFTJIS")) == 0)
+ return SHIFTJIS_CHARSET;
+ else if(tcsucmp(tstr, TEXT("SYMBOL")) == 0)
+ return SYMBOL_CHARSET;
+ else if(tcsucmp(tstr, TEXT("TURKISH")) == 0)
+ return TURKISH_CHARSET;
+ else if(tcsucmp(tstr, TEXT("VIETNAMESE")) == 0)
+ return VIETNAMESE_CHARSET;
+ else if(tcsucmp(tstr, TEXT("JOHAB")) == 0)
+ return JOHAB_CHARSET;
+ else if(tcsucmp(tstr, TEXT("ARABIC")) == 0)
+ return ARABIC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("HEBREW")) == 0)
+ return HEBREW_CHARSET;
+ else if(tcsucmp(tstr, TEXT("THAI")) == 0)
+ return THAI_CHARSET;
+ else
+ return DEFAULT_CHARSET;
+ }
+ else{
+ if(_tstoi(str) > 0)
+ return((BYTE)(_tstoi(str)));
+ else
+ return DEFAULT_CHARSET;
+ }
+}
+
+
+int
+mswin_setwindow(char *fontName_utf8, char *fontSize_utf8, char *fontStyle_utf8,
+ char *windowPosition, char *caretStyle, char *fontCharSet_utf8)
+{
+ LOGFONT newFont;
+ int newHeight;
+ HDC hDC;
+ int ppi;
+ RECT wndRect, cliRect;
+ char *c;
+ char *n;
+ int i;
+ BOOL toolBar = FALSE;
+ BOOL toolBarTop = TRUE;
+ int wColumns, wRows;
+ int wXPos, wYPos;
+ int wXBorder, wYBorder;
+ int wXSize, wYSize;
+ char wp[WIN_POS_STR_MAX_LEN + 1];
+ int showWin = 1;
+ LPTSTR fontName_lpt = NULL;
+ LPTSTR fontCharSet_lpt;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "mswin_setwindow::: entered, minimized: %d\n",
+ gpTTYInfo->fMinimized);
+#endif
+
+ /* Require a font name to set font info. */
+ if(fontName_utf8 != NULL && *fontName_utf8 != '\0' &&
+ (fontName_lpt = utf8_to_lptstr(fontName_utf8)) &&
+ _tcslen(fontName_lpt) <= LF_FACESIZE - 1){
+
+ hDC = GetDC(ghTTYWnd);
+ ppi = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(ghTTYWnd, hDC);
+
+ /* Default height, then examin the requested fontSize. */
+ newFont.lfHeight = -MulDiv(12, ppi, 72);
+ if(ScanInt(fontSize_utf8, FONT_MIN_SIZE, FONT_MAX_SIZE, &newHeight)){
+ newHeight = abs(newHeight);
+ newFont.lfHeight = -MulDiv(newHeight, ppi, 72);
+ }
+
+ /* Default font style, then read requested style. */
+ newFont.lfWeight = 0;
+ newFont.lfItalic = 0;
+ if(fontStyle_utf8 != NULL){
+ _strlwr(fontStyle_utf8);
+ if(strstr(fontStyle_utf8, "bold"))
+ newFont.lfWeight = FW_BOLD;
+ if(strstr(fontStyle_utf8, "italic"))
+ newFont.lfItalic = 1;
+ }
+
+ /* Everything else takes the default. */
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ fontCharSet_lpt = utf8_to_lptstr(fontCharSet_utf8);
+ if(fontCharSet_lpt){
+ newFont.lfCharSet = mswin_string2charsetid(fontCharSet_lpt);
+ fs_give((void **) &fontCharSet_lpt);
+ }
+
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+ _sntprintf(newFont.lfFaceName, LF_FACESIZE, TEXT("%s"), fontName_lpt);
+ ResetTTYFont (ghTTYWnd, gpTTYInfo, &newFont);
+ }
+
+ if(fontName_lpt)
+ fs_give((void **) &fontName_lpt);
+
+ /*
+ * Set window position. String format is: CxR+X+Y
+ * Where C is the number of columns, R is the number of rows,
+ * and X and Y specify the top left corner of the window.
+ */
+ if(windowPosition != NULL && *windowPosition != '\0'){
+ if(strlen(windowPosition) > sizeof(wp)-1)
+ return(0);
+
+ strncpy(wp, windowPosition, sizeof(wp));
+ wp[sizeof(wp)-1] = '\0';
+
+ /*
+ * Flag characters are at the end of the string. Strip them
+ * off till we get to a number.
+ */
+ i = (int)strlen(wp) - 1;
+ while(i > 0 && (*(wp+i) < '0' || *(wp+i) > '9')){
+ if(*(wp+i) == 't' || *(wp+i) == 'b'){
+ toolBar = TRUE;
+ toolBarTop = (*(wp+i) == 't');
+ }
+
+ if(*(wp+i) == 'd')
+ gfUseDialogs = TRUE;
+
+#ifdef ACCELERATORS_OPT
+ if(*(wp+i) == 'a')
+ AccelCtl(ghTTYWnd, ACCEL_LOAD, FALSE);
+#endif
+
+ *(wp+i) = 0;
+ --i;
+ }
+
+ if(strcmp(wp, "MIN0") == 0){
+ mswin_killsplash();
+ showWin = 0;
+ ShowWindow(ghTTYWnd, SW_MINIMIZE);
+ if(toolBar){
+ gpTTYInfo->toolBarTop = toolBarTop;
+ TBShow(ghTTYWnd);
+ }
+ }
+ else{
+ /* Look for Columns, deliminated by 'x'. */
+ c = strchr (wp, 'x');
+ if (c == NULL)
+ return(0);
+
+ *c = '\0';
+ if(!ScanInt(wp, -999, 9999, &wColumns))
+ return(0);
+
+ /* Look for Rows, deliminated by '+'. */
+ n = c + 1;
+ c = strchr(n, '+');
+ if(c == NULL)
+ return (0);
+
+ *c = '\0';
+ if(!ScanInt(n, -999, 9999, &wRows))
+ return(0);
+
+ /* Look for X position, deliminated by '+'. */
+ n = c + 1;
+ c = strchr(n, '+');
+ if(c == NULL)
+ return(0);
+
+ *c = '\0';
+ if(!ScanInt(n, -999, 9999, &wXPos))
+ return(0);
+
+ /* And get Y position, deliminated by end of string. */
+ n = c + 1;
+ if(!ScanInt(n, -999, 9999, &wYPos))
+ return(0);
+
+
+ /* Constrain the window position and size. */
+ if(wXPos < 0)
+ wXPos = 0;
+
+ if(wYPos < 0)
+ wYPos = 0;
+
+ GetWindowRect(GetDesktopWindow(), &wndRect);
+ if(wXPos > wndRect.right - 20)
+ wXPos = wndRect.right - 100;
+
+ if(wYPos > wndRect.bottom - 20)
+ wYPos = wndRect.bottom - 100;
+
+ /* Get the current window rect and client area. */
+ GetWindowRect(ghTTYWnd, &wndRect);
+ GetClientRect(ghTTYWnd, &cliRect);
+
+ /* Calculate boarder sizes. */
+ wXBorder = wndRect.right - wndRect.left - cliRect.right;
+ wYBorder = wndRect.bottom - wndRect.top - cliRect.bottom;
+
+ /* Show toolbar before calculating content size. */
+ if(toolBar){
+ gpTTYInfo->toolBarTop = toolBarTop;
+ TBShow(ghTTYWnd);
+ }
+
+ /* Calculate new window pos and size. */
+ wXSize = wXBorder + (wColumns * gpTTYInfo->xChar) +
+ (2 * gpTTYInfo->xOffset);
+ wYSize = wYBorder + (wRows * gpTTYInfo->yChar) +
+ gpTTYInfo->toolBarSize + (2 * MARGINE_TOP);
+ if(!gpTTYInfo->fMinimized)
+ MoveWindow(ghTTYWnd, wXPos, wYPos, wXSize, wYSize, TRUE);
+ else{
+ gpTTYInfo->fDesiredSize = TRUE;
+ gpTTYInfo->xDesPos = (CORD)wXPos;
+ gpTTYInfo->yDesPos = (CORD)wYPos;
+ gpTTYInfo->xDesSize = (CORD)wXSize;
+ gpTTYInfo->yDesSize = (CORD)wYSize;
+ }
+ }
+ }
+
+ if(caretStyle != NULL && *caretStyle != '\0')
+ for(i = 0; MSWinCaretTable[i].name; i++)
+ if(!strucmp(MSWinCaretTable[i].name, caretStyle)){
+ CaretTTY(ghTTYWnd, MSWinCaretTable[i].style);
+ break;
+ }
+
+ return(0);
+}
+
+
+int
+mswin_showwindow()
+{
+ mswin_killsplash();
+ ShowWindow (ghTTYWnd, SW_SHOWNORMAL);
+ UpdateWindow (ghTTYWnd);
+
+ return(0);
+}
+
+
+/*
+ * Retreive the current font name, font size, and window position
+ * These get stored in the pinerc file and will be passed to
+ * mswin_setwindow() when pine starts up. See pinerc for comments
+ * on the format.
+ */
+int
+mswin_getwindow(char *fontName_utf8, size_t nfontName,
+ char *fontSize_utf8, size_t nfontSize,
+ char *fontStyle_utf8, size_t nfontStyle,
+ char *windowPosition, size_t nwindowPosition,
+ char *foreColor, size_t nforeColor,
+ char *backColor, size_t nbackColor,
+ char *caretStyle, size_t ncaretStyle,
+ char *fontCharSet_utf8, size_t nfontCharSet)
+{
+ HDC hDC;
+ int ppi;
+ RECT wndRect;
+ char *t;
+
+ if(fontName_utf8 != NULL){
+ t = lptstr_to_utf8(gpTTYInfo->lfTTYFont.lfFaceName);
+ if(strlen(t) < nfontName)
+ snprintf(fontName_utf8, nfontName, "%s", t);
+
+ fs_give((void **) &t);
+ }
+
+ if(fontCharSet_utf8 != NULL){
+ LPTSTR lpt;
+
+ lpt = (LPTSTR) fs_get(nfontCharSet * sizeof(TCHAR));
+ if(lpt){
+ lpt[0] = '\0';
+ mswin_charsetid2string(lpt, nfontCharSet, gpTTYInfo->lfTTYFont.lfCharSet);
+ t = lptstr_to_utf8(lpt);
+ if(strlen(t) < nfontCharSet)
+ snprintf(fontCharSet_utf8, nfontCharSet, "%s", t);
+
+ fs_give((void **) &t);
+
+ fs_give((void **) &lpt);
+ }
+ }
+
+ if(fontSize_utf8 != NULL){
+ hDC = GetDC(ghTTYWnd);
+ ppi = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(ghTTYWnd, hDC);
+ snprintf(fontSize_utf8, nfontSize, "%d", MulDiv(-gpTTYInfo->lfTTYFont.lfHeight, 72, ppi));
+ }
+
+ if(fontStyle_utf8 != NULL){
+ char *sep[] = {"", ", "};
+ int iSep = 0;
+
+ *fontStyle_utf8 = '\0';
+ if(gpTTYInfo->lfTTYFont.lfWeight >= FW_BOLD){
+ strncpy(fontStyle_utf8, "bold", nfontStyle);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ iSep = 1;
+ }
+
+ if(gpTTYInfo->lfTTYFont.lfItalic){
+ strncat(fontStyle_utf8, sep[iSep], nfontStyle-strlen(fontStyle_utf8)-1);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ strncat(fontStyle_utf8, "italic", nfontStyle-strlen(fontStyle_utf8)-1);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ }
+ }
+
+ if(windowPosition != NULL){
+ if(gpTTYInfo->fMinimized){
+ strncpy(windowPosition, "MIN0", nwindowPosition);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+ else{
+ /*
+ * Get the window position. Constrain the top left corner
+ * to be on the screen.
+ */
+ GetWindowRect(ghTTYWnd, &wndRect);
+ if(wndRect.left < 0)
+ wndRect.left = 0;
+
+ if(wndRect.top < 0)
+ wndRect.top = 0;
+
+ snprintf(windowPosition, nwindowPosition, "%dx%d+%d+%d", gpTTYInfo->actNColumn,
+ gpTTYInfo->actNRow, wndRect.left, wndRect.top);
+ }
+
+ if(gpTTYInfo->toolBarSize > 0){
+ strncat(windowPosition, gpTTYInfo->toolBarTop ? "t" : "b",
+ nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gfUseDialogs){
+ strncat(windowPosition, "d", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gpTTYInfo->hAccel){
+ strncat(windowPosition, "a", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gpTTYInfo->fMaximized){
+ strncat(windowPosition, "!", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+ }
+
+ if(foreColor != NULL)
+ ConvertStringRGB(foreColor, nforeColor, gpTTYInfo->rgbFGColor);
+
+ if(backColor != NULL)
+ ConvertStringRGB(backColor, nbackColor, gpTTYInfo->rgbBGColor);
+
+ if(caretStyle != NULL){
+ int i;
+
+ for(i = 0; MSWinCaretTable[i].name; i++)
+ if(MSWinCaretTable[i].style == gpTTYInfo->cCaretStyle){
+ strncpy(caretStyle, MSWinCaretTable[i].name, ncaretStyle);
+ caretStyle[ncaretStyle-1] = '\0';
+ break;
+ }
+ }
+
+ return (0);
+}
+
+
+void
+mswin_noscrollupdate(int flag)
+{
+ gpTTYInfo->noScrollUpdate = (flag != 0);
+ if(flag == 0 && gpTTYInfo->scrollRangeChanged){
+ mswin_setscrollrange(gpTTYInfo->noSUpdatePage, gpTTYInfo->noSUpdateRange);
+ gpTTYInfo->noSUpdatePage = gpTTYInfo->noSUpdateRange = 0;
+ gpTTYInfo->scrollRangeChanged = 0;
+ }
+}
+
+
+/*
+ * Set the scroll range.
+ */
+void
+mswin_setscrollrange (long page, long max)
+{
+ SCROLLINFO scrollInfo;
+
+ if(gpTTYInfo->noScrollUpdate){
+ gpTTYInfo->noSUpdatePage = page;
+ gpTTYInfo->noSUpdateRange = max;
+ gpTTYInfo->scrollRangeChanged = 1;
+ return;
+ }
+ if (max != gpTTYInfo->scrollRange) {
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE;
+ scrollInfo.nMin = 0;
+
+ if (max > 0) {
+ scrollInfo.nMax = (int) max;
+ scrollInfo.nPage = page;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+ EnableScrollBar (ghTTYWnd, SB_VERT, ESB_ENABLE_BOTH);
+ }
+ else {
+ max = 0;
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask |= SIF_POS;
+ scrollInfo.nMax = 1;
+ scrollInfo.nPos = 0;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+ EnableScrollBar (ghTTYWnd, SB_VERT, ESB_DISABLE_BOTH);
+ gpTTYInfo->scrollPos = 0;
+ }
+ gpTTYInfo->scrollRange = (int)max;
+ }
+}
+
+
+/*
+ * Set the current scroll position.
+ */
+void
+mswin_setscrollpos (long pos)
+{
+ SCROLLINFO scrollInfo;
+
+ if (pos != gpTTYInfo->scrollPos) {
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_PAGE | SIF_RANGE;
+
+ GetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo);
+
+ scrollInfo.fMask |= SIF_POS;
+ scrollInfo.nPos = (int) pos;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+
+ gpTTYInfo->scrollPos = pos;
+ }
+}
+
+
+/*
+ * retreive the current scroll postion.
+ */
+long
+mswin_getscrollpos (void)
+{
+ return ((long)((float)GetScrollPos (ghTTYWnd, SB_VERT)));
+}
+
+
+/*
+ * retreive the latest scroll to position.
+ */
+long
+mswin_getscrollto (void)
+{
+ return (gpTTYInfo->scrollTo);
+}
+
+/*
+ * install function to deal with LINEDOWN events
+ */
+void
+mswin_setscrollcallback (cbarg_t cbfunc)
+{
+ gScrollCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with sort menu messages
+ */
+void
+mswin_setsortcallback (cbarg_t cbfunc)
+{
+ gSortCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with sort menu messages
+ */
+void
+mswin_setflagcallback (cbarg_t cbfunc)
+{
+ gFlagCallback = cbfunc;
+}
+
+
+void
+mswin_set_erasecreds_callback (cbvoid_t cbfunc)
+{
+ gEraseCredsCallback = cbfunc;
+}
+
+/*
+ * install function to deal with header mode flipping
+ */
+void
+mswin_sethdrmodecallback (cbarg_t cbfunc)
+{
+ gHdrCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with view in new window messages
+ */
+void
+mswin_setviewinwindcallback (cbvoid_t cbfunc)
+{
+ gViewInWindCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with zoom mode flipping
+ */
+void
+mswin_setzoomodecallback (cbarg_t cbfunc)
+{
+ gZoomCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with function key mode flipping
+ */
+void
+mswin_setfkeymodecallback (cbarg_t cbfunc)
+{
+ gFkeyCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with apply mode flipping
+ */
+void
+mswin_setselectedcallback (cbarg_t cbfunc)
+{
+ gSelectedCallback = cbfunc;
+}
+
+
+/*
+ * return 1 if new mail window is being used
+ */
+int
+mswin_newmailwinon (void)
+{
+ return(gMswinNewMailWin.hwnd ? 1 : 0);
+}
+
+
+/*
+ */
+void
+mswin_setdebugoncallback (cbvoid_t cbfunc)
+{
+ gIMAPDebugONCallback = cbfunc;
+}
+
+
+void
+mswin_setdebugoffcallback (cbvoid_t cbfunc)
+{
+ gIMAPDebugOFFCallback = cbfunc;
+}
+
+
+int
+mswin_setconfigcallback (cbvoid_t cffunc)
+{
+ gConfigScreenCallback = cffunc;
+ return(1);
+}
+
+
+/*
+ * Set the printer font
+ */
+void
+mswin_setprintfont(char *fontName, char *fontSize,
+ char *fontStyle, char *fontCharSet)
+{
+ LPTSTR fn = NULL, fstyle = NULL, fc = NULL;
+
+ if(fontName)
+ fn = utf8_to_lptstr(fontName);
+
+ if(fontStyle)
+ fstyle = utf8_to_lptstr(fontStyle);
+
+ if(fontCharSet)
+ fc = utf8_to_lptstr(fontCharSet);
+
+ /* Require a font name to set font info. */
+ if(fn != NULL && *fn != '\0' && lstrlen(fn) <= LF_FACESIZE - 1){
+
+ _tcsncpy(gPrintFontName, fn, sizeof(gPrintFontName)/sizeof(TCHAR));
+ gPrintFontName[sizeof(gPrintFontName)/sizeof(TCHAR)-1] = 0;
+ if(fstyle){
+ _tcsncpy(gPrintFontStyle, fstyle, sizeof(gPrintFontStyle)/sizeof(TCHAR));
+ gPrintFontStyle[sizeof(gPrintFontStyle)/sizeof(TCHAR)-1] = 0;
+ _tcslwr(gPrintFontStyle); /* Lower case font style. */
+ }
+
+ if(fc){
+ _tcsncpy(gPrintFontCharSet, fc, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ gPrintFontCharSet[sizeof(gPrintFontCharSet)/sizeof(TCHAR)-1] = 0;
+ }
+
+ gPrintFontSize = 12;
+ if(ScanInt(fontSize, FONT_MIN_SIZE, FONT_MAX_SIZE, &gPrintFontSize))
+ gPrintFontSize = abs(gPrintFontSize);
+
+ gPrintFontSameAs = FALSE;
+ }
+ else{
+ gPrintFontName[0] = '\0';
+ gPrintFontSameAs = TRUE;
+ }
+
+ if(fn)
+ fs_give((void **) &fn);
+
+ if(fstyle)
+ fs_give((void **) &fstyle);
+
+ if(fc)
+ fs_give((void **) &fc);
+}
+
+
+void
+mswin_getprintfont(char *fontName_utf8, size_t nfontName,
+ char *fontSize_utf8, size_t nfontSize,
+ char *fontStyle_utf8, size_t nfontStyle,
+ char *fontCharSet_utf8, size_t nfontCharSet)
+{
+ if(gPrintFontName[0] == '\0' || gPrintFontSameAs){
+ if(fontName_utf8 != NULL)
+ *fontName_utf8 = '\0';
+ if(fontSize_utf8 != NULL)
+ *fontSize_utf8 = '\0';
+ if(fontStyle_utf8 != NULL)
+ *fontStyle_utf8 = '\0';
+ if(fontCharSet_utf8 != NULL)
+ *fontCharSet_utf8 = '\0';
+ }
+ else{
+ char *u;
+
+ if(fontName_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontName);
+ if(u){
+ strncpy(fontName_utf8, u, nfontName);
+ fontName_utf8[nfontName-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+
+ if(fontSize_utf8 != NULL)
+ snprintf(fontSize_utf8, nfontSize, "%d", gPrintFontSize);
+
+
+ if(fontStyle_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontStyle);
+ if(u){
+ strncpy(fontStyle_utf8, u, nfontStyle);
+ fontStyle_utf8[nfontStyle-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+
+ if(fontCharSet_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontCharSet);
+ if(u){
+ strncpy(fontCharSet_utf8, u, nfontCharSet);
+ fontCharSet_utf8[nfontCharSet-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+ }
+}
+
+
+/*
+ * Set the window help text. Add or delete the Help menu item as needed.
+ */
+int
+mswin_sethelptextcallback(cbstr_t cbfunc)
+{
+ HMENU hMenu;
+
+ gHelpCallback = cbfunc;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return (1);
+
+ return(MSWHelpSetMenu (hMenu));
+
+}
+
+
+/*
+ * Set the window help text. Add or delete the Help menu item as needed.
+ */
+int
+mswin_setgenhelptextcallback(cbstr_t cbfunc)
+{
+ HMENU hMenu;
+
+ gHelpGenCallback = cbfunc;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return (1);
+
+ return(MSWHelpSetMenu (hMenu));
+
+}
+
+
+int
+MSWHelpSetMenu(HMENU hMenu)
+{
+ BOOL brc;
+ int count;
+
+ /*
+ * Find menu and update it.
+ */
+ count = GetMenuItemCount (hMenu);
+ if (count == -1)
+ return (1);
+
+ hMenu = GetSubMenu (hMenu, count - 1);
+ if (hMenu == NULL)
+ return (1);
+
+ /*
+ * Insert or delete generic help item
+ */
+ if (gHelpGenCallback == NULL){
+ if (gfHelpGenMenu) {
+ brc = DeleteMenu (hMenu, IDM_MI_GENERALHELP, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpGenMenu = FALSE;
+ }
+ else {
+ if (!gfHelpGenMenu) {
+ brc = InsertMenu (hMenu, 0,
+ MF_BYPOSITION | MF_STRING,
+ IDM_MI_GENERALHELP, TEXT("&General Help"));
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpGenMenu = TRUE;
+ }
+
+ /*
+ * Insert or delete screen help item
+ */
+ if (gHelpCallback == NULL){
+ if (gfHelpMenu) {
+ brc = DeleteMenu (hMenu, IDM_HELP, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpMenu = FALSE;
+ }
+ else {
+ if (!gfHelpMenu) {
+ brc = InsertMenu (hMenu, gHelpGenCallback ? 2 : 1,
+ MF_BYPOSITION | MF_STRING,
+ IDM_HELP, TEXT("Screen Help in &New Window"));
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpMenu = TRUE;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Set the text displayed when someone tries to close the application
+ * the wrong way.
+ */
+void
+mswin_setclosetext (char *pCloseText)
+{
+ gpCloseText = pCloseText;
+}
+
+
+/*
+ * Called when upper layer is in a busy loop. Allows us to yeild to
+ * Windows and perhaps process some events. Does not yeild control
+ * to other applications.
+ */
+int
+mswin_yield (void)
+{
+ MSG msg;
+ DWORD start;
+ int didmsg = FALSE;
+
+ if (gScrolling)
+ return (TRUE);
+
+ start = GetTickCount ();
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_yeild:: Entered\n");
+#endif
+ if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ didmsg = TRUE;
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_yeild:: Delay %ld msec\n",
+ GetTickCount () - start);
+#endif
+ return (didmsg);
+}
+
+
+/*
+ * Called to see if we can process input.
+ * We can't process input when we are in a scrolling mode.
+ */
+int
+mswin_caninput (void)
+{
+ return (!gScrolling && !gMouseTracking);
+}
+
+
+/*
+ * Called to see if there is a character available.
+ */
+int
+mswin_charavail (void)
+{
+ MSG msg;
+ BOOL ca, pa, ma;
+
+ if (gScrolling)
+ return (FALSE);
+
+ RestoreMouseCursor();
+
+ /*
+ * If there are no windows messages waiting for this app, GetMessage
+ * can take a long time. So before calling GetMessage check if
+ * there is anything I should be doing. If there is, then only
+ * call PeekMessage.
+ * BUT! Don't let too much time elapse between calls to GetMessage
+ * or we'll shut out other windows.
+ */
+ ca = CQAvailable ();
+ pa = EditPasteAvailable ();
+#ifdef CDEBUG
+ start = GetTickCount ();
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "%s mswin_charavail:: Entered, ca %d, pa %d\n",dtime(),ca,pa);
+#endif
+ if ((ca || pa) && GetTickCount () < gGMLastCall + GM_MAX_TIME)
+ ma = PeekMessage (&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE);
+ else {
+ ma = GetMessage (&msg, NULL, 0, 0);
+ gGMLastCall = GetTickCount ();
+ }
+ if (ma) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "%s mswin_charavail:: Delay %ld msec\n",
+ dtime(), GetTickCount () - start);
+#endif
+
+ return (pa || ca || CQAvailable ());
+}
+
+
+/*
+ * Call to get next character. Dispatch one message.
+ */
+UCS
+mswin_getc (void)
+{
+ BOOL ca, pa, ma;
+ MSG msg;
+
+ if (gScrolling)
+ return (NODATA);
+
+ RestoreMouseCursor();
+
+ mswin_flush();
+
+
+ /*
+ * If there are no windows messages waiting for this app, GetMessage
+ * can take a long time. So before calling GetMessage check if
+ * there is anything I should be doing. If there is, then only
+ * call PeekMessage.
+ */
+ ca = CQAvailable ();
+ pa = EditPasteAvailable ();
+#ifdef CDEBUG
+ if (mswin_debug > 16) {
+ start = GetTickCount ();
+ fprintf (mswin_debugfile, "mswin_getc:: Entered, ca %d pa %d\n", ca, pa);
+ }
+#endif
+ if ((ca || pa) && GetTickCount () < gGMLastCall + GM_MAX_TIME)
+ ma = PeekMessage (&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE);
+ else {
+ ma = GetMessage (&msg, NULL, 0, 0);
+ gGMLastCall = GetTickCount ();
+ }
+ if (ma) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_getc:: Delay %ld msec\n",
+ GetTickCount () - start);
+#endif
+
+
+ if (pa) {
+ SelClear ();
+ return (EditPasteGet ());
+ }
+ if (ca || CQAvailable ()) {
+ SelClear();
+ return ((UCS)(CQGet ()));
+ }
+
+ return (NODATA);
+}
+
+
+/*
+ * Like mswin_getc, but don't yield control. Returns a CTRL key values
+ * where, for example, ctrl+c --> CTRL|'C'.
+ */
+LOCAL UCS
+mswin_getc_fast (void)
+{
+ RestoreMouseCursor();
+
+ if (EditPasteAvailable ()) {
+ SelClear ();
+ return (EditPasteGet ());
+ }
+ if (CQAvailable ()) {
+ SelClear ();
+ return ((UCS)CQGet ());
+ }
+
+ return (NODATA);
+}
+
+
+/*
+ * Flush the character input queue.
+ */
+void
+mswin_flush_input(void)
+{
+ /*
+ * GetQueueStatus tells us if there are any input messages in the message
+ * queue. If there are, we call mswin_getc which will retrieve the
+ * next message (which may or may not be an input message) and process
+ * it. We do that until all the input messages are gone.
+ */
+ CQInit();
+
+ while(GetQueueStatus(QS_INPUT))
+ (void) mswin_getc();
+
+ /* And we clear Pine's character input queue, too. */
+ CQInit();
+}
+
+
+/*
+ * ibmmove - Move cursor to...
+ */
+int
+mswin_move (int row, int column)
+
+{
+ FlushWriteAccum ();
+ if (row < 0 || row >= gpTTYInfo->actNRow)
+ return (-1);
+ if (column < 0 || column >= gpTTYInfo->actNColumn)
+ return (-1);
+ gpTTYInfo->nRow = (CORD)row;
+ gpTTYInfo->nColumn = (CORD)column;
+ MoveTTYCursor (ghTTYWnd);
+ return (0);
+}
+
+
+int
+mswin_getpos (int *row, int *column)
+{
+ FlushWriteAccum ();
+ if (row == NULL || column == NULL)
+ return (-1);
+ *row = gpTTYInfo->nRow;
+ *column = gpTTYInfo->nColumn;
+ return (0);
+}
+
+
+int
+mswin_getscreensize (int *row, int *column)
+{
+ if (row == NULL || column == NULL)
+ return (-1);
+ *row = gpTTYInfo->actNRow;
+ *column = gpTTYInfo->actNColumn;
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "mswin_getscreensize::: reported size %d, %d\n",
+ *row, *column);
+#endif
+
+ *column = MIN(*column, NLINE-1);
+
+ return (0);
+}
+
+
+void
+mswin_minimize()
+{
+ if (!gpTTYInfo->fMinimized)
+ ShowWindow(ghTTYWnd, SW_MINIMIZE);
+}
+
+
+int
+mswin_showcursor (int show)
+{
+ int was_shown = gpTTYInfo->fCursorOn;
+
+ if (show) {
+ /* if the cursor was not already show, show it now. */
+ if (!was_shown) {
+ gpTTYInfo->fCursorOn = TRUE;
+ ShowCursor(TRUE);
+ }
+ }
+ else {
+ /* If the cursor is shown, hide it. */
+ if (was_shown){
+ gpTTYInfo->fCursorOn = FALSE;
+ ShowCursor(FALSE);
+ }
+ }
+
+ return (was_shown);
+}
+
+
+int
+mswin_showcaret (int show)
+{
+ int was_shown = gpTTYInfo->fCaretOn;
+
+ if (show) {
+ /* if the caret was not already show, show it now. */
+ if (!was_shown) {
+ gpTTYInfo->fCaretOn = TRUE;
+ ShowCaret(ghTTYWnd);
+ }
+ }
+ else {
+ /* If the caret is shown, hide it. */
+ if (was_shown){
+ gpTTYInfo->fCaretOn = FALSE;
+ HideCaret(ghTTYWnd);
+ }
+ }
+
+ return (was_shown);
+}
+
+
+int
+mswin_puts (char *utf8_str)
+{
+ int strLen;
+ LPTSTR lptstr_str;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ if(!(lptstr_str = utf8_to_lptstr(utf8_str)))
+ return(-1);
+ strLen = (int)_tcslen (lptstr_str);
+ if (strLen > 0)
+ WriteTTYText (ghTTYWnd, lptstr_str, strLen);
+
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+int
+mswin_puts_n (char *utf8_str, int n)
+{
+ LPTSTR lptstr_str, lptstr_p;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ lptstr_str = utf8_to_lptstr(utf8_str);
+ if(n < _tcslen(lptstr_str))
+ lptstr_str[n] = '\0';
+ for (lptstr_p = lptstr_str; n > 0 && *lptstr_p; n--, lptstr_p++)
+ ;
+ if (lptstr_p > lptstr_str)
+ WriteTTYText (ghTTYWnd, lptstr_str, (int)(lptstr_p - lptstr_str));
+
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+int
+mswin_putblock (char *utf8_str, int strLen)
+{
+ LPTSTR lptstr_str;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ lptstr_str = utf8_to_lptstr(utf8_str);
+ if (strLen > 0)
+ WriteTTYText (ghTTYWnd, lptstr_str, strLen);
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+/*
+ * mswin_putc - put a character at the current position in the
+ * current colors
+ */
+int
+mswin_putc (UCS ucs)
+{
+ TCHAR cc = (TCHAR)ucs;
+ if (ucs >= (UCS)(' ')) {
+ /* Not carriage control. */
+ gpTTYInfo->writeAccum[gpTTYInfo->writeAccumCount++] = (TCHAR)ucs;
+ if (gpTTYInfo->writeAccumCount == WRITE_ACCUM_SIZE)
+ FlushWriteAccum ();
+ }
+ else {
+ /* Carriage control. Need to flush write accumulator and
+ * write this char. */
+ FlushWriteAccum ();
+ WriteTTYBlock (ghTTYWnd, &cc, 1);
+ }
+ return (0);
+}
+
+
+/*
+ * ibmoutc - output a single character with the right attributes, but
+ * don't advance the cursor
+ */
+int
+mswin_outc (char c)
+{
+ RECT rect;
+ long offset;
+
+ FlushWriteAccum ();
+
+ switch (c) {
+ case ASCII_BEL:
+ MessageBeep (0);
+ break;
+
+ case ASCII_BS:
+ case ASCII_CR:
+ case ASCII_LF:
+ /* Do nothing for these screen motion characters. */
+ break;
+
+
+ default:
+ /* Paint character to screen. */
+ offset = (gpTTYInfo->nRow * gpTTYInfo->actNColumn) + gpTTYInfo->nColumn;
+ gpTTYInfo->pScreen[offset] = c;
+ gpTTYInfo->pCellWidth[offset] = wcellwidth((UCS)c) * gpTTYInfo->xChar;
+ gpTTYInfo->pAttrib[offset] = gpTTYInfo->curAttrib;
+
+ /* Invalidate rectange covering singel character. */
+ rect.left = (gpTTYInfo->nColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ rect.right = rect.left + gpTTYInfo->xChar;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = rect.top + gpTTYInfo->yChar;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_rev (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_REV) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state) {
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_REV;
+ SetReverseColor();
+ }
+ else{
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_REV;
+ pico_set_normal_color();
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Get current reverse video state.
+ */
+int
+mswin_getrevstate (void)
+{
+ return ((gpTTYInfo->curAttrib.style & CHAR_ATTR_REV) != 0);
+}
+
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_bold (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_BOLD) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state)
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_BOLD;
+ else
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_BOLD;
+ }
+
+ return (0);
+}
+
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_uline (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_ULINE) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state)
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_ULINE;
+ else
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_ULINE;
+ }
+
+ return (0);
+}
+
+
+/*
+ * ibmeeol - erase to the end of the line
+ */
+int
+mswin_eeol (void)
+{
+ TCHAR *cStart;
+ CharAttrib *aStart;
+ int *cwStart;
+ long length, i;
+ RECT rect;
+
+ FlushWriteAccum ();
+
+ /* From current position to end of line. */
+ length = gpTTYInfo->actNColumn - gpTTYInfo->nColumn;
+
+ cStart = gpTTYInfo->pScreen + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ cwStart = gpTTYInfo->pCellWidth + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ for(i = 0; i < length; i++){
+ cStart[i] = (TCHAR)(' ');
+ cwStart[i] = gpTTYInfo->xChar;
+ }
+
+ aStart = gpTTYInfo->pAttrib
+ + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ for(; length > 0; length--, aStart++){
+ aStart->style = CHAR_ATTR_NORM;
+ aStart->rgbFG = gpTTYInfo->curAttrib.rgbFG;
+ aStart->rgbBG = gpTTYInfo->curAttrib.rgbBG;
+ }
+
+ rect.left = (gpTTYInfo->nColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ rect.right = gpTTYInfo->xSize;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = rect.top + gpTTYInfo->yChar;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+
+ return (0);
+}
+
+
+/*
+ * ibmeeop - clear from cursor to end of page
+ */
+int
+mswin_eeop (void)
+{
+ TCHAR *cStart;
+ CharAttrib *aStart;
+ int *cwStart;
+ long length, i;
+ RECT rect;
+
+ FlushWriteAccum ();
+ /* From current position to end of screen. */
+
+ cStart = gpTTYInfo->pScreen + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ cwStart = gpTTYInfo->pCellWidth + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ length = (long)((gpTTYInfo->pScreen
+ + (gpTTYInfo->actNColumn * gpTTYInfo->actNRow))
+ - cStart);
+ for(i = 0; i < length; i ++){
+ cStart[i] = (TCHAR)(' ');
+ cwStart[i] = gpTTYInfo->xChar;
+ }
+
+ aStart = gpTTYInfo->pAttrib
+ + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+
+ for(; length-- > 0; aStart++){
+ aStart->style = CHAR_ATTR_NORM;
+ aStart->rgbFG = gpTTYInfo->curAttrib.rgbFG;
+ aStart->rgbBG = gpTTYInfo->curAttrib.rgbBG;
+ }
+
+ /* Invalidate a rectangle that includes all of the current line down
+ * to the bottom of the window. */
+ rect.left = 0;
+ rect.right = gpTTYInfo->xSize;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = gpTTYInfo->ySize;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+
+ return (0);
+}
+
+
+/*
+ * ibmbeep - system beep...
+ */
+int
+mswin_beep (void)
+{
+ MessageBeep (MB_OK);
+ return (0);
+}
+
+
+/*
+ * pause - wait in function for specified number of seconds.
+ */
+void
+mswin_pause (int seconds)
+{
+ DWORD stoptime;
+
+ stoptime = GetTickCount () + (DWORD) seconds * 1000;
+ while (stoptime > GetTickCount ())
+ mswin_yield ();
+}
+
+
+/*
+ * ibmflush - Flush output to screen.
+ *
+ */
+int
+mswin_flush (void)
+{
+
+ /*
+ * Flush cached changes, then update the window.
+ */
+ FlushWriteAccum ();
+ UpdateWindow (ghTTYWnd);
+
+ return (0);
+}
+
+
+/*
+ * A replacement for fflush
+ * relies on #define fflush mswin_fflush
+ */
+#undef fflush
+int
+mswin_fflush (FILE *f)
+{
+ if (f == stdout) {
+ mswin_flush();
+ }
+ else
+ fflush (f);
+
+ return(0);
+}
+
+
+/*
+ * Set the cursor
+ */
+void
+mswin_setcursor (int newcursor)
+{
+ HCURSOR hNewCursor;
+
+ switch(newcursor){
+ case MSWIN_CURSOR_BUSY :
+ hNewCursor = ghCursorBusy;
+ mswin_showcursor(TRUE);
+ break;
+
+ case MSWIN_CURSOR_IBEAM :
+ hNewCursor = ghCursorIBeam;
+ break;
+
+ case MSWIN_CURSOR_HAND :
+ if(hNewCursor = ghCursorHand)
+ break;
+ /* ELSE fall thru to normal cursor */
+
+ case MSWIN_CURSOR_ARROW :
+ default :
+ hNewCursor = ghCursorArrow;
+ break;
+ }
+
+ /* If new cursor requested, select it. */
+ if (ghCursorCurrent != hNewCursor)
+ SetCursor (ghCursorCurrent = hNewCursor);
+}
+
+
+void
+RestoreMouseCursor()
+{
+ if(ghCursorCurrent == ghCursorBusy)
+ mswin_setcursor(MSWIN_CURSOR_ARROW);
+}
+
+
+/*
+ * Display message in windows dialog box.
+ */
+void
+mswin_messagebox (char *msg_utf8, int err)
+{
+ LPTSTR msg_lptstr;
+
+ mswin_killsplash();
+
+ msg_lptstr = utf8_to_lptstr(msg_utf8);
+ MessageBox (NULL, msg_lptstr, gszAppName,
+ MB_OK | ((err) ? MB_ICONSTOP : MB_ICONINFORMATION));
+ fs_give((void **) &msg_lptstr);
+}
+
+
+/*
+ * Signals whether or not Paste should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowpaste (int on)
+{
+ static short stackp = 0;
+ static unsigned long stack = 0L;
+
+ switch(on){
+ case MSWIN_PASTE_DISABLE :
+ if(stackp){ /* previous state? */
+ if((stackp -= 2) < 0)
+ stackp = 0;
+
+ gPasteEnabled = ((stack >> stackp) & 0x03);
+ }
+ else
+ gPasteEnabled = MSWIN_PASTE_DISABLE;
+
+ break;
+
+ case MSWIN_PASTE_FULL :
+ case MSWIN_PASTE_LINE :
+ if(gPasteEnabled){ /* current state? */
+ stack |= ((unsigned long) gPasteEnabled << stackp);
+ stackp += 2;
+ }
+
+ gPasteEnabled = on;
+ break;
+ }
+
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Signals whether or not Copy/Cut should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowcopy (getc_t copyfunc)
+{
+ gCopyCutFunction = copyfunc;
+ gAllowCopy = (copyfunc != NULL);
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Signals whether or not Copy/Cut should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowcopycut (getc_t copyfunc)
+{
+ gCopyCutFunction = copyfunc;
+ gAllowCopy = gAllowCut = (copyfunc != NULL);
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Replace the clipboard's contents with the given string
+ */
+void
+mswin_addclipboard(char *s)
+{
+ HANDLE hCB;
+ char *pCB;
+ size_t sSize;
+
+ if(s && (sSize = strlen(s))){
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()
+ && (hCB = GlobalAlloc (GMEM_MOVEABLE, sSize+2))){
+ pCB = GlobalLock (hCB);
+ memcpy (pCB, s, sSize);
+ pCB[sSize] = '\0'; /* tie it off */
+
+ GlobalUnlock (hCB);
+
+ if (SetClipboardData (CF_TEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ }
+
+ CloseClipboard ();
+ }
+ }
+}
+
+
+/*
+ * Signals if the upper layer wants to track the mouse.
+ */
+void
+mswin_allowmousetrack (int b)
+{
+ gAllowMouseTrack = b;
+ if (b)
+ SelClear ();
+ MyTimerSet ();
+}
+
+
+/*
+ * register's callback to warn
+ */
+void
+mswin_mousetrackcallback(cbarg_t cbfunc)
+{
+ if(!(gMouseTrackCallback = cbfunc))
+ mswin_setcursor (MSWIN_CURSOR_ARROW);
+}
+
+
+/*
+ * Add text to the new mail icon.
+ * Nothing done in win 3.1 (consider changing the program name?)
+ * For win95 we add some tool tip text to the tray icon.
+ */
+void
+mswin_newmailtext (char *t_utf8)
+{
+ LPTSTR t_lptstr;
+
+ /*
+ * If we're given text, then blip the icon to let the user know.
+ * (NOTE: the new shell also gets an updated tooltip.)
+ * Otherwise, we're being asked to resume our normal state...
+ */
+ if(t_utf8){
+ /*
+ * Change the appearance of minimized icon so user knows there's new
+ * mail waiting for them. On win 3.1 systems we redraw the icon.
+ * on win95 systems we update the icon in the task bar,
+ * and possibly udpate the small icon in the taskbar tool tray.
+ */
+ t_lptstr = utf8_to_lptstr(t_utf8);
+ UpdateTrayIcon(NIM_MODIFY, ghNewMailIcon, t_lptstr);
+ fs_give((void **) &t_lptstr);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNewMailIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNewMailIcon);
+
+ gpTTYInfo->fNewMailIcon = TRUE;
+ }
+ else if(gpTTYInfo->fNewMailIcon) {
+ UpdateTrayIcon(NIM_MODIFY, ghNormalIcon, TEXT("Alpine"));
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNormalIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNormalIcon);
+
+ gpTTYInfo->fNewMailIcon = FALSE;
+ }
+}
+
+
+void
+mswin_mclosedtext (char *t_utf8)
+{
+ LPTSTR t_lptstr;
+
+ if(t_utf8 && gpTTYInfo->fMClosedIcon == FALSE){
+ /*
+ * Change the appearance of minimized icon so user knows
+ * the mailbox closed.
+ */
+ t_lptstr = utf8_to_lptstr(t_utf8);
+ UpdateTrayIcon(NIM_MODIFY, ghMClosedIcon, t_lptstr);
+ fs_give((void **) &t_lptstr);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghMClosedIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghMClosedIcon);
+
+ gpTTYInfo->fMClosedIcon = TRUE;
+ }
+ else if(t_utf8 == NULL && gpTTYInfo->fMClosedIcon) {
+ /* only go from yellow to green */
+ UpdateTrayIcon(NIM_MODIFY, ghNormalIcon, TEXT("Alpine"));
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNormalIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNormalIcon);
+
+ gpTTYInfo->fMClosedIcon = FALSE;
+ }
+}
+
+void
+mswin_trayicon(int show)
+{
+ if(show){
+ if(!gpTTYInfo->fTrayIcon){
+ UpdateTrayIcon(NIM_ADD, ghNormalIcon, TEXT("Alpine"));
+ gpTTYInfo->fTrayIcon = TRUE;
+ }
+ }
+ else{
+ if(gpTTYInfo->fTrayIcon){
+ UpdateTrayIcon(NIM_DELETE, 0, NULL);
+ gpTTYInfo->fTrayIcon = FALSE;
+ }
+ }
+}
+
+
+void
+UpdateTrayIcon(DWORD dwMsg, HICON hIcon, LPTSTR tip)
+{
+ NOTIFYICONDATA nt;
+
+ nt.cbSize = sizeof (nt);
+ nt.hWnd = ghTTYWnd;
+ nt.uID = TASKBAR_ICON_NEWMAIL;
+ switch(dwMsg){
+ case NIM_DELETE :
+ nt.uFlags = 0;
+ break;
+
+ case NIM_ADD : /* send msg to add icon to tray */
+ nt.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ nt.uCallbackMessage = TASKBAR_ICON_MESSAGE;
+
+ if(tip){
+ _tcsncpy (nt.szTip, tip, 63);
+ nt.szTip[63] = '\0';
+ }
+ else
+ nt.szTip[0] = '\0';
+ nt.hIcon = hIcon;
+ break;
+
+ case NIM_MODIFY : /* send msg to modify icon in tray */
+ nt.uFlags = NIF_ICON | NIF_TIP;
+ if(tip){
+ _tcsncpy (nt.szTip, tip, 63);
+ nt.szTip[63] = '\0';
+ }
+ else
+ nt.szTip[0] = '\0';
+ nt.hIcon = hIcon;
+ break;
+
+ default :
+ return;
+ }
+
+ Shell_NotifyIcon (dwMsg, &nt);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Client level menu item stuff.
+ *
+ * These are menu items that activate commands in the "client" program.
+ * Generally, the client calls us to tell us which menu items are active
+ * and what key stroke they generate. When such an item is selected it's
+ * key stroke is injected into the character queue as if it was typed by
+ * the user.
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Clear active status of all "menu items".
+ */
+void
+mswin_menuitemclear (void)
+{
+ int i;
+ HWND hWnd;
+
+
+ for (i = 0; i < KS_COUNT; ++i) {
+ gpTTYInfo->menuItems[i].miActive = FALSE;
+ if (gpTTYInfo->toolBarSize > 0) {
+ hWnd = GetDlgItem(gpTTYInfo->hTBWnd, i + KS_RANGESTART);
+ if (hWnd != NULL)
+ EnableWindow(hWnd, FALSE);
+ }
+ }
+
+ gpTTYInfo->menuItemsCurrent = FALSE;
+ gpTTYInfo->menuItemsIndex = 1;
+ for(i = 0; i < KS_COUNT; i++)
+ gpTTYInfo->menuItems[i].miIndex = 0;
+}
+
+
+/*
+ * Add an item to the cmdmenu
+ */
+void
+mswin_menuitemadd (UCS key, char *label, int menuitem, int flags)
+{
+ int i;
+ HWND hWnd;
+
+ if (menuitem >= KS_RANGESTART && menuitem <= KS_RANGEEND) {
+
+ gpTTYInfo->menuItemsCurrent = FALSE;
+
+ i = menuitem - KS_RANGESTART;
+ gpTTYInfo->menuItems[i].miActive = TRUE;
+ gpTTYInfo->menuItems[i].miKey = key;
+ if(!gpTTYInfo->menuItems[i].miIndex){
+ gpTTYInfo->menuItems[i].miLabel = label;
+ gpTTYInfo->menuItems[i].miIndex = gpTTYInfo->menuItemsIndex++;
+ }
+
+ if (gpTTYInfo->toolBarSize > 0) {
+ hWnd = GetDlgItem(gpTTYInfo->hTBWnd, menuitem);
+ if (hWnd != NULL)
+ EnableWindow(hWnd, TRUE);
+ }
+ }
+}
+
+
+/*
+ * Called when a menu command arrives with an unknown ID. If it is
+ * within the range of the additional menu items, insert the
+ * corresponding character into the char input queue.
+ */
+void
+ProcessMenuItem (HWND hWnd, WPARAM wParam)
+{
+ PTTYINFO pTTYInfo;
+ int i;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND) {
+ i = (int)(wParam - KS_RANGESTART);
+ if (pTTYInfo->menuItems[i].miActive)
+ CQAdd (pTTYInfo->menuItems[i].miKey, 0);
+ }
+}
+
+
+/*
+ * Called to set a new menu.
+ */
+int
+mswin_setwindowmenu (int menu)
+{
+ int oldmenu;
+ HMENU holdmenu;
+ HMENU hmenu;
+
+
+ oldmenu = gpTTYInfo->curWinMenu;
+ holdmenu = GetMenu (ghTTYWnd);
+ if (gpTTYInfo->curWinMenu != menu) {
+
+ hmenu = LoadMenu (ghInstance, MAKEINTRESOURCE (menu));
+ if (hmenu != NULL) {
+ if (SetMenu (ghTTYWnd, hmenu) != 0) {
+ DestroyMenu (holdmenu);
+ gfHelpMenu = gfHelpGenMenu = FALSE;
+ gpTTYInfo->curWinMenu = menu;
+ }
+ }
+
+ if(menu == MENU_DEFAULT){
+ TBSwap (ghTTYWnd, IDD_TOOLBAR);
+ if(hmenu != NULL)
+ (void) MSWHelpSetMenu(hmenu);
+ }
+ else{
+ TBSwap (ghTTYWnd, IDD_COMPOSER_TB);
+ }
+ }
+ return (oldmenu);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Printing stuff
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Printing globals
+ */
+LOCAL HDC P_PrintDC; /* Printer device context. */
+LOCAL int P_PageRows; /* Number of rows we put on a page. */
+LOCAL int P_PageColumns; /* Number of columns we put on a page. */
+LOCAL int P_RowHeight; /* Hight of a row in printer pixels. */
+LOCAL int P_CurRow; /* Current row, starting at zero */
+LOCAL int P_CurCol; /* Current col, starting at zero. */
+LOCAL int P_TopOffset; /* Top Margin offset, in pixels. */
+LOCAL int P_LeftOffset; /* Top Margin offset, in pixels. */
+LOCAL HFONT P_hFont; /* Handle to printing font. */
+LPTSTR P_LineText; /* Pointer to line buffer. */
+
+
+
+
+/*
+ * Define the margin as number of lines at top and bottom of page.
+ * (might be better to define as a percent of verticle page size)
+ */
+#define VERTICLE_MARGIN 3 /* lines at top and bottom of page. */
+#define HORIZONTAL_MARGIN 1 /* margine at left & right in chars */
+
+/*
+ * Several errors that can be reported.
+ */
+#define PE_DIALOG_FAILED 1
+#define PE_USER_CANCEL 2
+#define PE_CANT_START_DOC 3
+#define PE_CANT_FINISH_DOC 4
+#define PE_OUT_OF_MEMORY 5
+#define PE_GENERAL_ERROR 6
+#define PE_OUT_OF_DISK 7
+#define PE_PRINTER_NOT_FOUND 8
+#define PE_PINE_INTERNAL 9
+#define PE_FONT_FAILED 10
+
+
+LOCAL struct pe_error_message {
+ int error_code;
+ char *error_message;
+ } P_ErrorMessages[] = {
+ { PE_DIALOG_FAILED, "Print Dialog Failed"},
+ { PE_USER_CANCEL, "User canceled" },
+ { PE_CANT_START_DOC, "Can't start document" },
+ { PE_OUT_OF_MEMORY, "Out of memory" },
+ { PE_CANT_FINISH_DOC, "Can't finish document" },
+ { PE_OUT_OF_MEMORY, "Out of memory" },
+ { PE_OUT_OF_DISK, "Out of disk space" },
+ { PE_PRINTER_NOT_FOUND, "Printer not found" },
+ { PE_PINE_INTERNAL, "Pine internal error" },
+ { PE_FONT_FAILED, "Failed to create font" },
+ { 0, NULL }};
+
+
+
+/*
+ * Send text in the line buffer to the printer.
+ * Advance to next page if necessary.
+ */
+LOCAL int
+_print_send_line (void)
+{
+ int status;
+
+ status = 0;
+ if (P_CurCol > 0)
+ TextOut (P_PrintDC, P_LeftOffset,
+ P_TopOffset + (P_CurRow * P_RowHeight),
+ P_LineText, P_CurCol);
+ P_CurCol = 0;
+ if (++P_CurRow >= P_PageRows)
+ status = _print_send_page ();
+
+ return (status);
+}
+
+
+
+/*
+ * Advance one page.
+ */
+int
+_print_send_page (void)
+{
+ DWORD status;
+
+ if((status = EndPage (P_PrintDC)) > 0){
+ P_CurRow = 0;
+ if((status = StartPage (P_PrintDC)) > 0){
+ SelectObject (P_PrintDC, P_hFont);
+ return(0);
+ }
+ }
+
+#ifdef WIN32
+ ExplainSystemErr();
+ return(PE_GENERAL_ERROR);
+#else
+ switch (status) {
+ case SP_USERABORT: return (PE_USER_CANCEL);
+ case SP_OUTOFDISK: return (PE_OUT_OF_DISK);
+ case SP_OUTOFMEMORY: return (PE_OUT_OF_MEMORY);
+ default:
+ case SP_ERROR: break;
+ }
+#endif
+ return (PE_GENERAL_ERROR);
+}
+
+
+
+/*
+ * Map errors reported to my own error set.
+ */
+int
+_print_map_dlg_error (DWORD error)
+{
+ switch (error) {
+ case 0: return (PE_USER_CANCEL);
+ case CDERR_MEMALLOCFAILURE:
+ case CDERR_MEMLOCKFAILURE:
+ return (PE_OUT_OF_MEMORY);
+ case PDERR_PRINTERNOTFOUND:
+ case PDERR_NODEVICES:
+ return (PE_PRINTER_NOT_FOUND);
+ case CDERR_STRUCTSIZE:
+ return (PE_PINE_INTERNAL);
+ default:
+ return (PE_GENERAL_ERROR);
+ }
+}
+
+
+/*
+ * This is used for converting from UTF-8 to UCS and is
+ * global so that mswin_print_ready can initialize it.
+ */
+static CBUF_S print_cb;
+
+/*
+ * Get the printer ready. Returns ZERO for success, or an error code that
+ * can be passed to mswin_print_error() to get a text message.
+ */
+int
+mswin_print_ready(WINHAND hWnd, LPTSTR docDesc)
+{
+ PRINTDLG pd;
+ DOCINFO di;
+ TEXTMETRIC tm;
+ HDC hDC;
+ int fontSize; /* Size in Points. */
+ int ppi; /* Pixels per inch in device. */
+ int xChar;
+ int status;
+ HFONT oldFont;
+ LOGFONT newFont;
+
+
+ status = 0;
+ P_PrintDC = NULL;
+
+ print_cb.cbufp = print_cb.cbuf;
+
+ /*
+ * Show print dialog.
+ */
+ memset(&pd, 0, sizeof(PRINTDLG));
+ pd.lStructSize = sizeof (PRINTDLG);
+ pd.hwndOwner = (hWnd ? (HWND) hWnd : ghTTYWnd);
+ pd.hDevMode = NULL;
+ pd.hDevNames = NULL;
+ pd.Flags = PD_ALLPAGES | PD_NOSELECTION | PD_NOPAGENUMS |
+ PD_HIDEPRINTTOFILE | PD_RETURNDC;
+ pd.nCopies = 1;
+ if(PrintDlg (&pd) == 0)
+ return(_print_map_dlg_error (CommDlgExtendedError()));
+
+ /*
+ * Returns the device name which we could use to remember what printer
+ * they selected. But instead, we just toss them.
+ */
+ if (pd.hDevNames)
+ GlobalFree (pd.hDevNames);
+ if (pd.hDevMode)
+ GlobalFree (pd.hDevMode);
+
+ /*
+ * Get the device drawing context.
+ * (does PringDlg() ever return success but fail to return a DC?)
+ */
+ if (pd.hDC != NULL)
+ P_PrintDC = pd.hDC;
+ else {
+ status = PE_DIALOG_FAILED;
+ goto Done;
+ }
+
+ /*
+ * Start Document
+ */
+ memset(&di, 0, sizeof (DOCINFO));
+ di.cbSize = sizeof (DOCINFO);
+ di.lpszDocName = docDesc; /* This appears in the print manager*/
+ di.lpszOutput = NULL; /* Could suply a file name to print
+ to. */
+ if(StartDoc(P_PrintDC, &di) <= 0) {
+ ExplainSystemErr();
+ status = PE_CANT_START_DOC;
+ DeleteDC (P_PrintDC);
+ P_PrintDC = NULL;
+ goto Done;
+ }
+
+ /*
+ * Printer font is either same as window font, or is it's own
+ * font.
+ */
+ if (gPrintFontSameAs) {
+
+ /*
+ * Get the current font size in points, then create a new font
+ * of same size for printer. Do the calculation using the actual
+ * screen resolution instead of the logical resolution so that
+ * we get pretty close to the same font size on the printer
+ * as we see on the screen.
+ */
+ hDC = GetDC (ghTTYWnd); /* Temp screen DC. */
+ ppi = (int) ((float)GetDeviceCaps (hDC, VERTRES) /
+ ((float) GetDeviceCaps (hDC, VERTSIZE) / 25.3636));
+#ifdef FDEBUG
+ if (mswin_debug >= 8) {
+ fprintf (mswin_debugfile, "mswin_print_ready: Screen res %d ppi, font height %d pixels\n",
+ ppi, -gpTTYInfo->lfTTYFont.lfHeight);
+ fprintf (mswin_debugfile, " Screen height %d pixel, %d mm\n",
+ GetDeviceCaps (hDC, VERTRES), GetDeviceCaps (hDC, VERTSIZE));
+ }
+#endif
+ ReleaseDC (ghTTYWnd, hDC);
+
+ /* Convert from screen pixels to points. */
+ fontSize = MulDiv (-gpTTYInfo->lfTTYFont.lfHeight, 72, ppi);
+ ++fontSize; /* Fudge a little. */
+
+
+ /* Get printer resolution and convert form points to printer pixels. */
+ ppi = GetDeviceCaps (P_PrintDC, LOGPIXELSY);
+ newFont.lfHeight = -MulDiv (fontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gpTTYInfo->lfTTYFont.lfFaceName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfItalic = gpTTYInfo->lfTTYFont.lfItalic;
+ newFont.lfWeight = gpTTYInfo->lfTTYFont.lfWeight;
+ newFont.lfCharSet = gpTTYInfo->lfTTYFont.lfCharSet;
+
+
+#ifdef FDEBUG
+ if (mswin_debug >= 8) {
+ fprintf (mswin_debugfile, " font Size %d points\n",
+ fontSize);
+ fprintf (mswin_debugfile, " printer res %d ppi, font height %d pixels\n",
+ ppi, -newFont.lfHeight);
+ fprintf (mswin_debugfile, " paper height %d pixel, %d mm\n",
+ GetDeviceCaps (P_PrintDC, VERTRES),
+ GetDeviceCaps (P_PrintDC, VERTSIZE));
+ }
+#endif
+ }
+ else {
+ ppi = GetDeviceCaps (P_PrintDC, LOGPIXELSY);
+ newFont.lfHeight = -MulDiv (gPrintFontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gPrintFontName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfWeight = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("bold")))
+ newFont.lfWeight = FW_BOLD;
+
+ newFont.lfItalic = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("italic")))
+ newFont.lfItalic = 1;
+
+ newFont.lfCharSet = mswin_string2charsetid(gPrintFontCharSet);
+ }
+
+
+ /* Fill out rest of font description and request font. */
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+ P_hFont = CreateFontIndirect (&newFont);
+ if (P_hFont == NULL) {
+ status = PE_FONT_FAILED;
+ DeleteDC (P_PrintDC);
+ goto Done;
+ }
+
+
+ /*
+ * Start page.
+ * Must select font for each page or it returns to default.
+ * Windows seems good about maping selected font to a font that
+ * will actually print on the printer.
+ */
+ StartPage (P_PrintDC);
+ oldFont = SelectObject (P_PrintDC, P_hFont);
+
+
+ /*
+ * Find out about the font we got and set up page size and margins.
+ * This assumes all pages are the same size - which seems reasonable.
+ */
+ GetTextMetrics (P_PrintDC, &tm);
+ xChar = tm.tmAveCharWidth;
+ P_RowHeight = tm.tmHeight + tm.tmExternalLeading;
+
+ /* HORZRES and VERTRES report size of page in printer pixels. */
+ P_PageColumns = GetDeviceCaps (P_PrintDC, HORZRES) / xChar;
+ P_PageRows = GetDeviceCaps (P_PrintDC, VERTRES) / P_RowHeight;
+
+ /* We allow a margin at top and bottom measured in text rows. */
+ P_PageRows -= VERTICLE_MARGIN * 2;
+ P_TopOffset = VERTICLE_MARGIN * P_RowHeight;
+
+ /* And allow for a left and right margine measured in characters. */
+ P_PageColumns -= HORIZONTAL_MARGIN * 2;
+ P_LeftOffset = HORIZONTAL_MARGIN * xChar;
+
+ P_CurRow = 0; /* Start counting at row 0. */
+ P_CurCol = 0; /* At character 0. */
+ P_LineText = (LPTSTR) MemAlloc((P_PageColumns + 1) * sizeof(TCHAR));
+ if(P_LineText == NULL){
+ if(EndDoc (P_PrintDC) <= 0)
+ ExplainSystemErr();
+
+ DeleteObject (P_hFont);
+ P_hFont = NULL;
+ DeleteDC (P_PrintDC);
+ P_PrintDC = NULL;
+ status = PE_OUT_OF_MEMORY;
+ goto Done;
+ }
+
+
+Done:
+ return (status);
+}
+
+
+/*
+ * Called when printing is done.
+ * xxx what happens if there is an error? Will this get called?
+ */
+int
+mswin_print_done(void)
+{
+ if(P_PrintDC != NULL){
+ if(P_LineText != NULL)
+ MemFree((void *) P_LineText);
+
+ if(EndPage (P_PrintDC) <= 0 || EndDoc (P_PrintDC) <= 0)
+ ExplainSystemErr();
+
+ DeleteObject(P_hFont);
+ P_hFont = NULL;
+ DeleteDC(P_PrintDC);
+ P_PrintDC = NULL;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Return ponter to a text string that describes the erorr.
+ */
+char *
+mswin_print_error(int error_code)
+{
+ int i;
+
+ for(i = 0; P_ErrorMessages[i].error_message != NULL; ++i)
+ if(P_ErrorMessages[i].error_code == error_code)
+ return(P_ErrorMessages[i].error_message);
+
+ return("(Unknown error)");
+}
+
+
+/*
+ * Add a single character to the current line.
+ * Only handles CRLF carrage control.
+ */
+int
+mswin_print_char(TCHAR c)
+{
+ switch(c){
+ case ASCII_CR:
+ return(0);
+
+ case ASCII_LF:
+ return(_print_send_line());
+
+ case ASCII_TAB:
+ do{
+ if(P_CurCol == P_PageColumns)
+ return(_print_send_line());
+
+ *(P_LineText + P_CurCol++) = ' ';
+ }
+ while(P_CurCol % PRINT_TAB_SIZE != 0);
+ return(0);
+
+ default:
+ if(P_CurCol == P_PageColumns){
+ int status;
+
+ if((status = _print_send_line()) != 0)
+ return(status);
+ }
+
+ *(P_LineText + P_CurCol++) = c;
+ return(0);
+ }
+}
+
+
+int
+mswin_print_char_utf8(int c)
+{
+ UCS ucs;
+ TCHAR tc;
+ int ret = 0;
+
+ if(utf8_to_ucs4_oneatatime(c, &print_cb, &ucs, NULL)){
+ /* bogus conversion ignores UTF-16 */
+ tc = (TCHAR) ucs;
+ ret = mswin_print_char(tc);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Send a string to the printer.
+ */
+int
+mswin_print_text(LPTSTR text)
+{
+ int status;
+
+ if(text)
+ while(*text)
+ if((status = mswin_print_char(*(text++))) != 0)
+ return(status);
+
+ return(0);
+}
+
+
+int
+mswin_print_text_utf8(char *text_utf8)
+{
+ LPTSTR text_lpt;
+ int ret = 0;
+
+ if(text_utf8){
+ text_lpt = utf8_to_lptstr(text_utf8);
+ if(text_lpt){
+ ret = mswin_print_text(text_lpt);
+ fs_give((void **) &text_lpt);
+ }
+ }
+
+ return(ret);
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * File dialog boxes.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL TCHAR gHomeDir[PATH_MAX];
+LOCAL TCHAR gLastDir[PATH_MAX];
+
+/*
+ * Keep track of the last dir visited. Most of the time pine just passes us
+ * the "home directory", which usually is not where the user wants to start.
+ * Assume that the first time we are called we are being passed the home
+ * direcory.
+ */
+static void
+FillInitialDir (LPCTSTR *iDir, LPTSTR targDir)
+{
+ if (_tcslen (gHomeDir) == 0) {
+ _tcscpy (gHomeDir, targDir);
+ *iDir = targDir;
+ }
+ else if (_tcscmp (gHomeDir, targDir) == 0 && *gLastDir)
+ *iDir = gLastDir;
+ else
+ *iDir = targDir;
+}
+
+
+
+/*
+ * Display a save file dialog box.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Name of file selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_savefile(char *dir_utf8, int nMaxDName, char *fName_utf8, int nMaxFName)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[128], moniker[128];
+ DWORD rc, len;
+ LPTSTR p, extlist_lpt;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp;
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ fName_lpt[0] = '\0';
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ dir_lpt[0] = '\0';
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ for(extlist_lpt = NULL, p = _tcschr(fName_lpt, '.'); p; p = _tcschr(++p, '.'))
+ extlist_lpt = p;
+
+ len = sizeof(moniker)/sizeof(TCHAR);
+ if(extlist_lpt && MSWRPeek(HKEY_CLASSES_ROOT, extlist_lpt, NULL, moniker, &len) == TRUE){
+ len = sizeof(filters)/sizeof(TCHAR);
+ filters[0] = '\0';
+ if(MSWRPeek(HKEY_CLASSES_ROOT, moniker, NULL, filters, &len) == TRUE)
+ _sntprintf(filters + _tcslen(filters),
+ sizeof(filters)/sizeof(TCHAR) - _tcslen(filters),
+ TEXT(" (*%s)#*%s#"), extlist_lpt, extlist_lpt);
+ else
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s (*%s)#*%s#"), moniker, extlist_lpt, extlist_lpt);
+ }
+ else
+ filters[0] = '\0';
+
+ _tcsncat(filters, TEXT("Text Files (*.txt)#*.txt#All Files (*.*)#*.*#"),
+ sizeof(filters)/sizeof(TCHAR));
+ filters[sizeof(filters)/sizeof(TCHAR) - 1] = '\0';
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset (&ofn, 0, sizeof(ofn));
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir (&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Save To File");
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetSaveFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ p = fName_lpt + ofn.nFileOffset;
+
+ /* convert fName back to UTF-8 */
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ strncpy(fName_utf8, cp, nMaxFName-1);
+ fName_utf8[nMaxFName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+
+
+/*
+ * Display an open file dialog box.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Name of file selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_openfile(char *dir_utf8, int nMaxDName, char *fName_utf8,
+ int nMaxFName, char *extlist_utf8)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[1024];
+ DWORD rc;
+ LPTSTR p;
+ LPTSTR extlist_lpt = NULL;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp;
+
+
+ if(extlist_utf8)
+ extlist_lpt = utf8_to_lptstr(extlist_utf8);
+
+ /*
+ * Set filters array. (pairs of null terminated strings, terminated
+ * by a double null).
+ */
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s%sAll Files (*.*)#*.*#"),
+ extlist_lpt ? extlist_lpt : TEXT(""), extlist_lpt ? TEXT("#") : TEXT(""));
+
+ if(extlist_lpt)
+ fs_give((void **) &extlist_lpt);
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset(&ofn, 0, sizeof(ofn));
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ fName_lpt[0] = '\0';
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ dir_lpt[0] = '\0';
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir(&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Select File");
+ ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetOpenFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ p = fName_lpt + ofn.nFileOffset;
+
+ /* convert fName back to UTF-8 */
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ strncpy(fName_utf8, cp, nMaxFName-1);
+ fName_utf8[nMaxFName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+/*
+ * Display an open file dialog box.
+ * Allow selection of multiple files.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Names of files selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_multopenfile(char *dir_utf8, int nMaxDName, char *fName_utf8,
+ int nMaxFName, char *extlist_utf8)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[1024];
+ DWORD rc;
+ LPTSTR p;
+ LPTSTR extlist_lpt = NULL;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp, *q;
+
+
+ if(extlist_utf8)
+ extlist_lpt = utf8_to_lptstr(extlist_utf8);
+
+ /*
+ * Set filters array. (pairs of null terminated strings, terminated
+ * by a double null).
+ */
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s%sAll Files (*.*)#*.*#"),
+ extlist_lpt ? extlist_lpt : TEXT(""), extlist_lpt ? TEXT("#") : TEXT(""));
+
+ if(extlist_lpt)
+ fs_give((void **) &extlist_lpt);
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset (&ofn, 0, sizeof(ofn));
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ memset(fName_lpt, 0, nMaxFName * sizeof(TCHAR));
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ memset(dir_lpt, 0, nMaxDName * sizeof(TCHAR));
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir(&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Select Files");
+ ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER |
+ OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetOpenFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ /*
+ * The file names are all in the same directory and are separated
+ * by '\0' characters and terminated by double '\0'.
+ * This fact depends on the OFN_EXPLORER bit being set in the flags
+ * above.
+ *
+ * This is complicated because we need to convert all of these file
+ * names to UTF-8.
+ */
+ for(q=fName_utf8, p=fName_lpt + ofn.nFileOffset; *p; p += _tcslen(p)+1){
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ sstrncpy(&q, cp, (int)(nMaxFName-(q-fName_utf8)));
+ if(q-fName_utf8 < nMaxFName){
+ *q++ = '\0';
+ if(q-fName_utf8 < nMaxFName)
+ *q = '\0'; /* the double null if this is the end */
+ }
+ else
+ fName_utf8[nMaxFName-1] = '\0';
+
+ fs_give((void **) &cp);
+ }
+ }
+
+ fName_utf8[nMaxFName-1] = fName_utf8[nMaxFName-2] = '\0';
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return (1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ */
+
+/*
+ * pico_XXcolor() - each function sets a particular attribute
+ */
+void
+pico_nfcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbFGColor, s);
+ pico_set_nfg_color();
+
+ if(the_normal_color){
+ strncpy(the_normal_color->fg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbFGColor),
+ MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbFGColor = GetSysColor (COLOR_WINDOWTEXT);
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+ }
+
+ // Update all textwindows with the new FG color.
+ mswin_tw_setcolor((MSWIN_TEXTWINDOW *)-1,
+ gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbBGColor, s);
+ pico_set_nbg_color();
+
+ if(the_normal_color){
+ strncpy(the_normal_color->bg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbBGColor),
+ MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbBGColor = GetSysColor (COLOR_WINDOW);
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+ }
+
+ // Update all textwindows with the new BG color.
+ mswin_tw_setcolor((MSWIN_TEXTWINDOW *)-1,
+ gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+}
+
+
+void
+pico_rfcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbRFGColor, s);
+
+ if(the_rev_color){
+ strncpy(the_rev_color->fg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbRFGColor),
+ MAXCOLORLEN+1);
+ the_rev_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbRFGColor = GetSysColor (COLOR_HIGHLIGHTTEXT);
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+ }
+}
+
+
+void
+pico_rbcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbRBGColor, s);
+
+ if(the_rev_color){
+ strncpy(the_rev_color->bg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbRBGColor),
+ MAXCOLORLEN+1);
+ the_rev_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbRBGColor = GetSysColor (COLOR_HIGHLIGHT);
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+ }
+}
+
+
+int
+pico_usingcolor()
+{
+ return(TRUE);
+}
+
+
+int
+pico_count_in_color_table()
+{
+ return(visibleColorTableSize);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+ COLORREF cf;
+ int l;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ if(ConvertRGBString(colorName, &cf)){
+ snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%.3d,%.3d,%.3d",
+ GetRValue(cf), GetGValue(cf), GetBValue(cf));
+ }
+ else{
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN);
+ l = (int)strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ }
+
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+void
+pico_set_nfg_color()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbFG = gpTTYInfo->rgbFGColor;
+}
+
+void
+pico_set_nbg_color()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbBG = gpTTYInfo->rgbBGColor;
+}
+
+void
+pico_set_normal_color()
+{
+ pico_set_nfg_color();
+ pico_set_nbg_color();
+}
+
+COLOR_PAIR *
+pico_get_rev_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ if(!the_rev_color)
+ the_rev_color =
+ new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbRFGColor),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbRBGColor));
+ return(the_rev_color);
+}
+
+COLOR_PAIR *
+pico_get_normal_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ if(!the_normal_color)
+ the_normal_color =
+ new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbFGColor),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbBGColor));
+ return(the_normal_color);
+}
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ COLOR_PAIR *cp = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(!(fg && bg && pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(flags & PSC_NORM)
+ pico_set_normal_color();
+ else if(flags & PSC_REV)
+ SetReverseColor();
+ }
+
+ return(cp);
+}
+
+
+int
+pico_is_good_color(char *colorName)
+{
+ COLORREF cf;
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)
+ || !struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &cf));
+}
+
+
+int
+pico_set_fg_color(char *colorName)
+{
+ char fgbuf[MAXCLEN];
+
+ FlushWriteAccum ();
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)){
+ ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbFGColor);
+ colorName = fgbuf;
+ }
+ else if(!struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &gpTTYInfo->curAttrib.rgbFG));
+}
+
+
+int
+pico_set_bg_color(char *colorName)
+{
+ char bgbuf[MAXCLEN];
+
+ FlushWriteAccum ();
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)){
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbBGColor);
+ colorName = bgbuf;
+ }
+ else if(!struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &gpTTYInfo->curAttrib.rgbBG));
+}
+
+
+char *
+pico_get_last_fg_color()
+{
+ return(NULL);
+}
+
+
+char *
+pico_get_last_bg_color()
+{
+ return(NULL);
+}
+
+
+unsigned
+pico_get_color_options()
+{
+ return((unsigned)0);
+}
+
+
+void
+pico_set_color_options(unsigned int opts)
+{
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ return(new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->curAttrib.rgbFG),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->curAttrib.rgbBG)));
+}
+
+
+char *
+mswin_rgbchoice(char *pOldRGB)
+{
+ CHOOSECOLOR cc;
+ static COLORREF custColors[16] = {
+ RGB(0,0,0),
+ RGB(0,0,255),
+ RGB(0,255,0),
+ RGB(0,255,255),
+ RGB(255,0,0),
+ RGB(255,0,255),
+ RGB(255,255,0),
+ RGB(255,255,255),
+ RGB(192,192,192),
+ RGB(128,128,128),
+ RGB(64,64,64)
+ };
+
+ memset(&cc, 0, sizeof(CHOOSECOLOR));
+
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = ghTTYWnd;
+ cc.Flags = CC_ANYCOLOR;
+ cc.lpCustColors = &custColors[0];
+
+ if(pOldRGB){
+ int i;
+
+ ConvertRGBString (pOldRGB, &cc.rgbResult);
+ cc.Flags |= CC_RGBINIT;
+
+ for(i = 0; i < 11 && custColors[i] != cc.rgbResult; i++)
+ ;
+
+ if(i == 11){
+ custColors[i] = cc.rgbResult;
+ cc.Flags |= CC_FULLOPEN;
+ }
+
+ }
+
+ if(ChooseColor(&cc)){
+ char rgbbuf[MAXCLEN], *p;
+
+ ConvertStringRGB(rgbbuf, sizeof(rgbbuf), cc.rgbResult);
+ if(p = MemAlloc(MAXCLEN * sizeof(char))){
+ strncpy(p, rgbbuf, MAXCLEN);
+ p[MAXCLEN-1] = '\0';
+ return(p);
+ }
+ }
+
+ return(NULL);
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Signal and alarm functions
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Provide a rough implementation of the SIGALRM and alarm functions
+ */
+
+
+
+#if 0
+/*
+ * Set a new handler for a signal.
+ */
+void (__cdecl * __cdecl signal (int sig,void (__cdecl *hndlr)(int)))(int)
+
+{
+ SignalType oldValue;
+
+ switch(sig) {
+ case SIGALRM :
+ oldValue = gSignalAlarm;
+ gSignalAlarm = hndlr;
+ break;
+
+ case SIGHUP :
+ oldValue = gSignalHUP;
+ gSignalHUP = hndlr;
+
+ default:
+ /* All other's are always ignored. */
+ oldValue = SIG_IGN;
+ break;
+ }
+
+ return (oldValue);
+}
+#endif
+
+
+/*
+ * Set the alarm expiration time (in seconds)
+ */
+int
+mswin_alarm (int seconds)
+{
+ int prevtime;
+
+ prevtime = gAlarmTimeout ? (gAlarmTimeout - (GetTickCount () / 1000)): 0;
+ gAlarmTimeout = seconds ? (GetTickCount() / 1000) + seconds : 0;
+ MyTimerSet ();
+ return (prevtime);
+}
+
+
+
+/*
+ * Deliver and clear the alarm.
+ */
+void
+AlarmDeliver ()
+{
+ if (gSignalAlarm != SIG_DFL && gSignalAlarm != SIG_IGN) {
+ /* Clear AlarmTimeout BEFORE calling handler. handler may call back
+ * to reset timeout. */
+ gAlarmTimeout = 0;
+ MyTimerSet ();
+ gSignalAlarm (SIGALRM);
+ }
+}
+
+
+
+void
+HUPDeliver ()
+{
+ if (gSignalHUP) {
+ gSignalHUP (SIGHUP);
+ exit (0);
+ }
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Printer font selection menu
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Set the print font to be the same as the window font.
+ * Toggle setting.
+ */
+void
+PrintFontSameAs (HWND hWnd)
+{
+ HDC hDC;
+ int ppi;
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (gPrintFontSameAs) {
+
+ /* No longer same as window font. Use window font as starting point
+ * for new printer font. User may later modify printer font. */
+ hDC = GetDC (hWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+ ExtractFontInfo(&pTTYInfo->lfTTYFont,
+ gPrintFontName, sizeof(gPrintFontName)/sizeof(TCHAR),
+ &gPrintFontSize,
+ gPrintFontStyle, sizeof(gPrintFontStyle)/sizeof(TCHAR),
+ ppi,
+ gPrintFontCharSet, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ gPrintFontSameAs = FALSE;
+ }
+ else {
+
+ /* Set to be same as the printer font. Destroy printer font info
+ * and set "sameAs" flag to TRUE. */
+ gPrintFontName[0] = '\0';
+ gPrintFontSameAs = TRUE;
+ }
+ DidResize (gpTTYInfo);
+}
+
+
+
+
+
+void
+PrintFontSelect (HWND hWnd)
+{
+ CHOOSEFONT cfTTYFont;
+ LOGFONT newFont;
+ DWORD drc;
+ int ppi;
+ HDC hDC;
+
+
+
+ hDC = GetDC (hWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+
+
+ newFont.lfHeight = -MulDiv (gPrintFontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gPrintFontName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfWeight = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("bold")))
+ newFont.lfWeight = FW_BOLD;
+
+ newFont.lfItalic = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("italic")))
+ newFont.lfItalic = 1;
+
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfCharSet = mswin_string2charsetid(gPrintFontCharSet);
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+
+
+ cfTTYFont.lStructSize = sizeof (CHOOSEFONT);
+ cfTTYFont.hwndOwner = hWnd ;
+ cfTTYFont.hDC = NULL ;
+ cfTTYFont.rgbColors = 0;
+ cfTTYFont.lpLogFont = &newFont;
+ cfTTYFont.Flags = CF_BOTH | CF_FIXEDPITCHONLY |
+ CF_INITTOLOGFONTSTRUCT | CF_ANSIONLY |
+ CF_FORCEFONTEXIST | CF_LIMITSIZE;
+ cfTTYFont.nSizeMin = FONT_MIN_SIZE;
+ cfTTYFont.nSizeMax = FONT_MAX_SIZE;
+ cfTTYFont.lCustData = 0 ;
+ cfTTYFont.lpfnHook = NULL ;
+ cfTTYFont.lpTemplateName = NULL ;
+ cfTTYFont.hInstance = GET_HINST (hWnd);
+
+
+ if (ChooseFont (&cfTTYFont)) {
+ ExtractFontInfo(&newFont,
+ gPrintFontName, sizeof(gPrintFontName)/sizeof(TCHAR),
+ &gPrintFontSize,
+ gPrintFontStyle, sizeof(gPrintFontStyle)/sizeof(TCHAR),
+ ppi,
+ gPrintFontCharSet, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ DidResize (gpTTYInfo);
+ }
+ else
+ /* So I can see with the debugger. */
+ drc = CommDlgExtendedError();
+}
+
+
+void
+ExtractFontInfo(LOGFONT *pFont, LPTSTR fontName, size_t nfontName,
+ int *fontSize, LPTSTR fontStyle, size_t nfontStyle,
+ int ppi, LPTSTR fontCharSet, size_t nfontCharSet)
+{
+ TCHAR *sep[] = {TEXT(""), TEXT(", ")};
+ int iSep = 0;
+
+
+ _tcsncpy(fontName, pFont->lfFaceName, nfontName);
+ fontName[nfontName-1] = '\0';
+
+ *fontStyle = '\0';
+ if(pFont->lfWeight >= FW_BOLD) {
+ _tcsncpy(fontStyle, TEXT("bold"), nfontStyle);
+ fontStyle[nfontStyle-1] = '\0';
+ iSep = 1;
+ }
+
+ if(pFont->lfItalic){
+ _tcsncat(fontStyle, sep[iSep], nfontStyle - _tcslen(fontStyle));
+ fontStyle[nfontStyle-1] = '\0';
+
+ _tcsncat(fontStyle, TEXT("italic"), nfontStyle - _tcslen(fontStyle));
+ fontStyle[nfontStyle-1] = '\0';
+ }
+
+ mswin_charsetid2string(fontCharSet, nfontCharSet, pFont->lfCharSet);
+
+ if(fontSize)
+ *fontSize = MulDiv(-pFont->lfHeight, 72, ppi);
+}
+
+
+LOCAL void
+DidResize (PTTYINFO pTTYInfo)
+{
+ int i;
+
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (pTTYInfo->resizer[i] != NULL)
+ pTTYInfo->resizer[i] (pTTYInfo->actNRow, pTTYInfo->actNColumn);
+ }
+ /*
+ * Put a null character into the input queue so that the data input
+ * loops stop and return to their callers who will then re-calculate the
+ * mouse regions so that the user can click on the new regions of the
+ * screen and have the right thing happen.
+ */
+ CQAdd (NODATA, 0);
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Cut, Copy, and Paste operations
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+/*
+ * Gets called right before the menu is displayed so we can make
+ * any last minute adjustments.
+ */
+LOCAL void
+UpdateMenu (HWND hWnd)
+{
+ HMENU hMenu;
+ PTTYINFO pTTYInfo;
+ int i;
+#ifdef ACCELERATORS
+ UINT fAccel = EM_NONE;
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ hMenu = GetMenu (hWnd);
+ if (hMenu == NULL)
+ return;
+
+ if (ghPaste) {
+ /* Currently pasting so disable paste and enable cancel paste. */
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_CANCEL_PASTE, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_PST | EM_PST_ABORT);
+#endif
+ }
+ else {
+ /*
+ * Not pasting. If text is available on clipboard and we are
+ * at a place where we can paste, enable past menu option.
+ */
+ if (IsClipboardFormatAvailable (CF_UNICODETEXT) && gPasteEnabled){
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= EM_PST;
+#endif
+ }
+ else
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_GRAYED);
+
+ EnableMenuItem (hMenu, IDM_EDIT_CANCEL_PASTE, MF_BYCOMMAND|MF_GRAYED);
+ }
+
+ if (SelAvailable ()) {
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND|MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND|MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_CP | EM_CP_APPEND);
+#endif
+ }
+ else {
+ if (gAllowCut){
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= EM_CUT;
+#endif
+ }
+ else
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_GRAYED);
+
+ if (gAllowCopy) {
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND,
+ MF_BYCOMMAND | MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_CP | EM_CP_APPEND);
+#endif
+ }
+ else {
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND,
+ MF_BYCOMMAND | MF_GRAYED);
+ }
+ }
+
+ /*
+ * Set up Font selection menu
+ */
+ if (gPrintFontName[0] == '\0') {
+ CheckMenuItem (hMenu, IDM_OPT_FONTSAMEAS, MF_BYCOMMAND | MF_CHECKED);
+ EnableMenuItem (hMenu, IDM_OPT_SETPRINTFONT,
+ MF_BYCOMMAND | MF_GRAYED);
+ }
+ else {
+ CheckMenuItem (hMenu, IDM_OPT_FONTSAMEAS,
+ MF_BYCOMMAND | MF_UNCHECKED);
+ EnableMenuItem (hMenu, IDM_OPT_SETPRINTFONT,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /*
+ * Setup Caret selection menu
+ */
+ EnableMenuItem (hMenu, IDM_OPT_CARETBLOCK, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETSMALLBLOCK, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETHBAR, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETVBAR, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuRadioItem(hMenu, IDM_OPT_CARETBLOCK, IDM_OPT_CARETVBAR,
+ IDM_OPT_CARETBLOCK + pTTYInfo->cCaretStyle,
+ MF_BYCOMMAND);
+
+ /*
+ * Check toolbar menu.
+ */
+ EnableMenuItem (hMenu, IDM_OPT_TOOLBAR, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuItem (hMenu, IDM_OPT_TOOLBAR, MF_BYCOMMAND |
+ (pTTYInfo->toolBarSize > 0 ? MF_CHECKED : MF_UNCHECKED));
+ EnableMenuItem (hMenu, IDM_OPT_TOOLBARPOS, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuItem (hMenu, IDM_OPT_TOOLBARPOS, MF_BYCOMMAND |
+ (pTTYInfo->toolBarTop > 0 ? MF_CHECKED : MF_UNCHECKED));
+
+
+
+ /*
+ * Check the dialogs menu.
+ */
+ /* xxx EnableMenuItem (hMenu, IDM_OPT_USEDIALOGS, MF_BYCOMMAND | MF_ENABLED);*/
+ CheckMenuItem (hMenu, IDM_OPT_USEDIALOGS, MF_BYCOMMAND |
+ (gfUseDialogs ? MF_CHECKED : MF_UNCHECKED));
+
+ /*
+ * Enable the Erase Credentials menu
+ */
+ EnableMenuItem (hMenu, IDM_OPT_ERASE_CREDENTIALS,
+ MF_BYCOMMAND | (gEraseCredsCallback ? MF_ENABLED : MF_GRAYED));
+
+ /*
+ * Enable the View in New Window menu item
+ */
+ EnableMenuItem (hMenu, IDM_MI_VIEWINWIND,
+ MF_BYCOMMAND | (gViewInWindCallback ? MF_ENABLED : MF_GRAYED));
+
+#ifdef ACCELERATORS_OPT
+ CheckMenuItem (hMenu, IDM_OPT_USEACCEL, MF_BYCOMMAND |
+ (pTTYInfo->hAccel ? MF_CHECKED : MF_UNCHECKED));
+#endif
+
+ /*
+ * Setup the sort menu...
+ */
+ if(gSortCallback){
+ i = (*gSortCallback)(0, 0);
+
+ /* NOTE: this func's args are dependent on definition order
+ * in resource.h
+ */
+ CheckMenuRadioItem(hMenu, IDM_MI_SORTSUBJECT, IDM_MI_SORTTHREAD,
+ IDM_MI_SORTSUBJECT + (i & 0x00ff), MF_BYCOMMAND);
+ CheckMenuItem(hMenu, IDM_MI_SORTREVERSE,
+ MF_BYCOMMAND|((i & 0x0100) ? MF_CHECKED : MF_UNCHECKED));
+ }
+ else{
+ CheckMenuRadioItem(hMenu, IDM_MI_SORTSUBJECT, IDM_MI_SORTTHREAD,
+ IDM_MI_SORTARRIVAL, MF_BYCOMMAND);
+ CheckMenuItem(hMenu, IDM_MI_SORTREVERSE, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+
+ /*
+ * Setup the flag menu...
+ */
+ if(gFlagCallback){
+ int flags = (*gFlagCallback)(0, 0);
+ for(i = IDM_MI_FLAGIMPORTANT; i <= IDM_MI_FLAGDELETED; i++)
+ CheckMenuItem(hMenu, i, MF_BYCOMMAND
+ | (((flags >> (i - IDM_MI_FLAGIMPORTANT)) & 0x0001)
+ ? MF_CHECKED : MF_UNCHECKED));
+ }
+
+ /*
+ *
+ */
+ if(gHdrCallback){
+ i = (*gHdrCallback)(0, 0);
+ CheckMenuItem(hMenu, IDM_MI_HDRMODE,
+ MF_BYCOMMAND|((i != 0) ? MF_CHECKED : MF_UNCHECKED));
+ }
+
+ /*
+ *
+ */
+ if(gZoomCallback){
+ i = (*gZoomCallback)(0, 0);
+ CheckMenuItem(hMenu, IDM_MI_ZOOM,
+ MF_BYCOMMAND | (i ? MF_CHECKED : MF_UNCHECKED));
+ }
+ /*
+ * Set up command menu.
+ */
+ if (!pTTYInfo->menuItemsCurrent) {
+ for (i = 0; i < KS_COUNT; ++i)
+ if(i + KS_RANGESTART != KS_GENERALHELP)
+ EnableMenuItem (hMenu, i + KS_RANGESTART,
+ MF_BYCOMMAND | ((pTTYInfo->menuItems[i].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ /*
+ * Special command-specific knowledge here
+ */
+ for(i = IDM_MI_SORTSUBJECT; i <= IDM_MI_SORTREVERSE; i++)
+ EnableMenuItem (hMenu, i,
+ MF_BYCOMMAND
+ | ((pTTYInfo->menuItems[KS_SORT-KS_RANGESTART].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ for(i = IDM_MI_FLAGIMPORTANT; i <= IDM_MI_FLAGDELETED; i++)
+ EnableMenuItem (hMenu, i,
+ MF_BYCOMMAND
+ | ((pTTYInfo->menuItems[KS_FLAG - KS_RANGESTART].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ }
+
+ /*
+ * deal with any callback state dependent enabling
+ */
+ if(pTTYInfo->menuItems[IDM_MI_APPLY - KS_RANGESTART].miActive)
+ EnableMenuItem(hMenu, IDM_MI_APPLY,
+ MF_BYCOMMAND | ((gSelectedCallback
+ && (*gSelectedCallback)(0, 0))
+ ? MF_ENABLED : MF_GRAYED));
+
+ if(pTTYInfo->menuItems[IDM_MI_ZOOM - KS_RANGESTART].miActive)
+ EnableMenuItem(hMenu, IDM_MI_ZOOM,
+ MF_BYCOMMAND | ((gSelectedCallback
+ && (*gSelectedCallback)(0, 0))
+ ? MF_ENABLED : MF_GRAYED));
+
+#ifdef ACCELERATORS
+ if(pTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miActive)
+ fAccel |= EM_FIND;
+
+ AccelManage (hWnd, fAccel);
+#endif
+
+ pTTYInfo->menuItemsCurrent = TRUE;
+}
+
+
+
+/*
+ * Cut region to kill buffer.
+ */
+LOCAL void
+EditCut (void)
+{
+ HANDLE hCB;
+
+ if(gCopyCutFunction == (getc_t)kremove){
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL) {
+ kdelete(); /* Clear current kill buffer. */
+ killregion (1, 0); /* Kill Region (and copy to clipboard). */
+ update (); /* And update the screen */
+ }
+ }
+}
+
+
+
+/*
+ * This function copies the kill buffer to the window's clip board.
+ * (actually, it can copy any buffer for which a copyfunc is provided).
+ * Called from ldelete().
+ */
+void
+mswin_killbuftoclip (getc_t copyfunc)
+{
+ HANDLE hCB;
+ getc_t oldfunc;
+
+ /* Save old copy function. */
+ oldfunc = gCopyCutFunction;
+ gCopyCutFunction = copyfunc;
+
+ /* Allocate clip buffer. */
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL) {
+ EditDoCopyData (hCB, 0);
+ }
+
+ /* restore copy function. */
+ gCopyCutFunction = oldfunc;
+}
+
+
+
+/*
+ * Copy region to kill buffer.
+ */
+LOCAL void
+EditCopy (void)
+{
+ HANDLE hCB;
+
+ if (SelAvailable()) {
+ /* This is a copy of the windows selection. */
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL)
+ SelDoCopy (hCB, 0);
+ }
+ else {
+
+ /* Otherwise, it's a Pico/Pine copy. */
+ if(gCopyCutFunction == (getc_t)kremove){
+ kdelete(); /* Clear current kill buffer. */
+ copyregion (1, 0);
+ }
+
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL)
+ EditDoCopyData (hCB, 0);
+ }
+}
+
+
+
+/*
+ * Called in responce to "Copy Append" menu command, when there is an active
+ * Windows selection on the screen.
+ */
+LOCAL void
+EditCopyAppend (void)
+{
+ HANDLE hCB;
+ HANDLE hMyCopy;
+ TCHAR *pCB;
+ TCHAR *pMyCopy;
+ size_t cbSize = 0;
+
+ /* Attempt to copy clipboard data to my own handle. */
+ hMyCopy = NULL;
+ if (OpenClipboard (ghTTYWnd)) { /* And can get clipboard. */
+ hCB = GetClipboardData (CF_UNICODETEXT);
+ if (hCB != NULL) { /* And can get data. */
+ pCB = GlobalLock (hCB);
+ cbSize = _tcslen (pCB); /* It's a null term string. */
+ hMyCopy = GlobalAlloc (GMEM_MOVEABLE, (cbSize+1)*sizeof(*pCB));
+ if (hMyCopy != NULL) { /* And can get memory. */
+ pMyCopy = GlobalLock (hMyCopy);
+ if (pMyCopy != NULL) {
+ memcpy (pMyCopy, pCB, cbSize*sizeof(*pCB)); /* Copy data. */
+ GlobalUnlock (hMyCopy);
+ }
+ else {
+ GlobalFree (hMyCopy);
+ hMyCopy = NULL;
+ }
+ }
+ GlobalUnlock (hCB);
+ } /* GetClipboardData. */
+ CloseClipboard ();
+ } /* OpenClipboard. */
+
+
+
+ /* Now, if I got a copy, append current selection to that
+ * and stuff it back into the clipboard. */
+ if (hMyCopy != NULL) {
+ if (SelAvailable ()) {
+ SelDoCopy (hMyCopy, (DWORD)cbSize);
+ }
+ else {
+ if(gCopyCutFunction == (getc_t)kremove) {
+ kdelete(); /* Clear current kill buffer. */
+ copyregion (1, 0);
+ }
+ EditDoCopyData (hMyCopy, (DWORD)cbSize);
+ }
+ }
+}
+
+
+/*
+ * Copy data from the kill buffer to the clipboard. Handle LF->CRLF
+ * translation if necessary.
+ */
+LOCAL void
+EditDoCopyData (HANDLE hCB, DWORD lenCB)
+{
+ TCHAR *pCB;
+ TCHAR *p;
+ long c; /* would be TCHAR but needs -1 retval from callback */
+ TCHAR lastc = (TCHAR)'\0';
+ DWORD cbSize; /* Allocated size of hCB. */
+ DWORD i;
+#define BUF_INC 4096
+
+ if (gCopyCutFunction != NULL) { /* If there really is data. */
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()) { /* ...and clear previous CB.*/
+ pCB = GlobalLock (hCB);
+ p = pCB + lenCB;
+ cbSize = lenCB;
+ /* Copy it. (BUG: change int arg) */
+ for(i = 0L; (c = (*gCopyCutFunction)((int)i)) != -1; i++){
+ /*
+ * Rather than fix every function that might
+ * get called for character retrieval to supply
+ * CRLF EOLs, let's just fix it here. The downside
+ * is a much slower copy for large buffers, but
+ * hey, what do they want?
+ */
+ if(lenCB + 2L >= cbSize){
+ cbSize += BUF_INC;
+ GlobalUnlock (hCB);
+ hCB = GlobalReAlloc (hCB, cbSize*sizeof(TCHAR), GMEM_MOVEABLE);
+ if (hCB == NULL)
+ return;
+
+ pCB = GlobalLock (hCB);
+ p = pCB + lenCB;
+ }
+
+ if(c == (TCHAR)ASCII_LF && lastc != (TCHAR)ASCII_CR) {
+ *p++ = (TCHAR)ASCII_CR; /* insert CR before LF */
+ lenCB++;
+ }
+
+ *p++ = lastc = (TCHAR)c;
+ lenCB++;
+ }
+
+ /* Only if we got some data. */
+ if (lenCB > 0) {
+ *p = (TCHAR)'\0';
+ GlobalUnlock (hCB);
+
+ if (SetClipboardData (CF_UNICODETEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ }
+ else {
+ /* There was no data copied. */
+ GlobalUnlock (hCB);
+ GlobalFree (hCB);
+ }
+ }
+ CloseClipboard ();
+ }
+ }
+}
+
+
+/*
+ * Get a handle to the current (text) clipboard and make my own copy.
+ * Keep my copy locked because I'll be using it to read bytes from.
+ */
+LOCAL void
+EditPaste (void)
+{
+ HANDLE hCB;
+ LPTSTR pCB;
+ LPTSTR pPaste;
+ size_t cbSize;
+
+ if (ghPaste == NULL) { /* If we are not already pasting. */
+ if (OpenClipboard (ghTTYWnd)) { /* And can get clipboard. */
+ hCB = GetClipboardData (CF_UNICODETEXT);
+ if (hCB != NULL) { /* And can get data. */
+ pCB = GlobalLock (hCB);
+ cbSize = _tcslen (pCB); /* It's a null term string. */
+ ghPaste = GlobalAlloc (GMEM_MOVEABLE, (cbSize+1)*sizeof(TCHAR));
+ if (ghPaste != NULL) { /* And can get memory. */
+ gpPasteNext = GlobalLock (ghPaste);
+ memcpy (gpPasteNext, pCB, (cbSize+1)*sizeof(TCHAR)); /* Copy data. */
+ /* Keep ghPaste locked. */
+
+ /*
+ * If we're paste is enabled but limited to the first
+ * line of the clipboard, prune the paste buffer...
+ */
+ if(gPasteEnabled == MSWIN_PASTE_LINE
+ && (pPaste = _tcschr(gpPasteNext, (TCHAR)ASCII_CR))){
+ *pPaste = (TCHAR)'\0';
+ cbSize = _tcslen(gpPasteNext);
+ }
+
+ /*
+ * If there is a selection (gCopyCutFunction != NULL)
+ * then delete it so that it will be replaced by
+ * the pasted text.
+ */
+ if (gCopyCutFunction != NULL)
+ deleteregion (1, 0);
+
+ gPasteBytesRemain = cbSize;
+ gPasteWasCR = FALSE;
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPaste:: Paste %d bytes\n",
+ gPasteBytesRemain);
+#endif
+ }
+ GlobalUnlock (hCB);
+ }
+ CloseClipboard ();
+ }
+ }
+}
+
+
+
+
+
+
+/*
+ * Cancel an active paste operation.
+ */
+LOCAL void
+EditCancelPaste (void)
+{
+ if (ghPaste != NULL) { /* Must be pasting. */
+ GlobalUnlock (ghPaste); /* Then Unlock... */
+ GlobalFree (ghPaste); /* ...and free the paste buffer. */
+ ghPaste = NULL; /* Indicates no paste data. */
+ gpPasteNext = NULL; /* Just being tidy. */
+ gPasteBytesRemain = 0; /* ditto. */
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditCancelPaste:: Free Paste Data\n");
+#endif
+ }
+}
+
+
+/*
+ * Get the next byte from the paste buffer. If all bytes have been
+ * retreived, free the paste buffer.
+ * Map all CRLF sequence to a single CR.
+ */
+LOCAL UCS
+EditPasteGet (void)
+{
+ UCS b = NODATA;
+
+ if (ghPaste != NULL) { /* ghPaste tells if we are pasting. */
+ if (gPasteBytesRemain > 0) { /* Just in case... */
+ /* Get one byte and move pointer. */
+ b = (TCHAR) *gpPasteNext++;
+ --gPasteBytesRemain; /* one less. */
+ if (gPasteWasCR && b == (TCHAR)ASCII_LF) {
+ if (gPasteBytesRemain) {
+ /* Skip of LF. */
+ b = (TCHAR) *gpPasteNext++;
+ --gPasteBytesRemain;
+ }
+ else
+ b = NODATA; /* Ignore last LF. */
+ }
+ gPasteWasCR = (b == (TCHAR)ASCII_CR);
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPasteGet:: char %c, gPasteWasCR %d, gPasteBytesRemain %d\n",
+ b, gPasteWasCR, gPasteBytesRemain);
+#endif
+ }
+ if (gPasteBytesRemain <= 0) { /* All Done? */
+ GlobalUnlock (ghPaste); /* Then Unlock... */
+ GlobalFree (ghPaste); /* ...and free the paste buffer. */
+ ghPaste = NULL; /* Indicates no paste data. */
+ gpPasteNext = NULL; /* Just being tidy. */
+ gPasteBytesRemain = 0; /* ditto. */
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPasteGet:: Free Paste Data\n");
+#endif
+ }
+ }
+
+ if(b < ' ') {
+ b += '@';
+ b |= CTRL;
+ }
+
+ return (b);
+}
+
+
+/*
+ * Return true if Paste data is available. If gpPaste != NULL then there
+ * is paste data.
+ */
+LOCAL BOOL
+EditPasteAvailable (void)
+{
+ return (ghPaste != NULL);
+}
+
+
+
+/*
+ * Select everything in the buffer
+ */
+LOCAL void
+EditSelectAll()
+{
+ if(ComposerEditing){
+ }
+ else{
+ gotobob(0, 1);
+ setmark(0, 1);
+ gotoeob(0, 1);
+ update (); /* And update the screen */
+ }
+}
+
+
+LOCAL void
+SortHandler(int order, int reverse)
+{
+ int old = (*gSortCallback)(0, 0);
+
+ if(order < 0){
+ old ^= 0x0100; /* flip reverse bit */
+ (*gSortCallback)(1, old);
+ }
+ else
+ (*gSortCallback)(1, order | (old & 0x0100));
+}
+
+
+LOCAL void
+FlagHandler(int index, int args)
+{
+ if(gFlagCallback)
+ (void) (*gFlagCallback)(index + 1, 0L);
+}
+
+
+LOCAL void
+MSWHelpShow (cbstr_t fpHelpCallback)
+{
+ if (fpHelpCallback != NULL){
+ char title[256], *help;
+
+ if(help = (*fpHelpCallback) (title))
+ mswin_displaytext (title, help, strlen(help), NULL, NULL, 0);
+ }
+}
+
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Adjust the timer frequency as needed.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL void
+MyTimerSet (void)
+{
+ UINT period;
+ /* Decide on period to use. */
+ if (gAllowMouseTrack)
+ period = MY_TIMER_EXCEEDINGLY_SHORT_PERIOD;
+ else
+ period = my_timer_period;
+
+ if (period != gTimerCurrentPeriod) {
+ if (SetTimer (ghTTYWnd, MY_TIMER_ID, period, NULL) == 0)
+ MessageBox (ghTTYWnd, TIMER_FAIL_MESSAGE, NULL,
+ MB_OK | MB_ICONINFORMATION);
+ else
+ gTimerCurrentPeriod = period;
+ }
+}
+
+
+
+
+
+void
+mswin_setperiodiccallback (cbvoid_t periodiccb, long period)
+{
+ if (periodiccb != NULL && period > 0) {
+ gPeriodicCallback = periodiccb;
+ gPeriodicCBTime = period;
+ gPeriodicCBTimeout = GetTickCount () / 1000 + gPeriodicCBTime;
+ }
+ else {
+ gPeriodicCallback = NULL;
+ }
+}
+
+/*
+ * Structure for variables used by mswin_exec_and_wait which need to be
+ * freed in multiple places.
+ */
+typedef struct MSWIN_EXEC_DATA {
+ HANDLE infd;
+ HANDLE outfd;
+ LPTSTR lptstr_whatsit;
+ LPTSTR lptstr_command;
+ LPTSTR lptstr_infile;
+ LPTSTR lptstr_outfile;
+ MSWIN_TEXTWINDOW *mswin_tw;
+} MSWIN_EXEC_DATA;
+
+LOCAL void
+mswin_exec_data_init(MSWIN_EXEC_DATA *exec_data)
+{
+ memset(exec_data, 0, sizeof(MSWIN_EXEC_DATA));
+ exec_data->infd = INVALID_HANDLE_VALUE;
+ exec_data->outfd = INVALID_HANDLE_VALUE;
+}
+
+LOCAL void
+mswin_exec_data_free(MSWIN_EXEC_DATA *exec_data, BOOL delete_outfile)
+{
+ if(exec_data->infd != INVALID_HANDLE_VALUE)
+ CloseHandle(exec_data->infd);
+
+ if(exec_data->outfd != INVALID_HANDLE_VALUE) {
+ CloseHandle(exec_data->outfd);
+ if(delete_outfile)
+ _tunlink(exec_data->lptstr_outfile);
+ }
+
+ if(exec_data->lptstr_infile)
+ fs_give((void **) &exec_data->lptstr_infile);
+ if(exec_data->lptstr_outfile)
+ fs_give((void **) &exec_data->lptstr_outfile);
+ if(exec_data->lptstr_whatsit)
+ fs_give((void **) &exec_data->lptstr_whatsit);
+ if(exec_data->lptstr_command)
+ fs_give((void **) &exec_data->lptstr_command);
+
+ if(exec_data->mswin_tw) {
+ /*
+ * Set the out_file is zero. We don't need mswin_tw
+ * to save the file anymore since we're bailing.
+ */
+ exec_data->mswin_tw->out_file = NULL;
+
+ /*
+ * If the window is still open, then set the id to 0 so
+ * mswin_tw_close_callback() will free the memory whenever
+ * the window closes. Otherwise free it now.
+ */
+ if(exec_data->mswin_tw->hwnd)
+ exec_data->mswin_tw->id = 0;
+ else
+ MemFree(exec_data->mswin_tw);
+ }
+}
+
+/*
+ * Execute command and wait for the child to exit
+ *
+ * whatsit - description of reason exec being called
+ * command - command to run
+ * infile - name of file to pass as stdin
+ * outfile - name of file to pass as stdout
+ * exit_val - where to store return value of the process
+ * mswe_flags -
+ * MSWIN_EAW_CAPT_STDERR - capture stderr along with stdout
+ * MSWIN_EAW_CTRL_C_CANCELS - if user presses ctrl-c, detach child
+ * Returns: 0, successfully completed program
+ * -1, errors occurred
+ * -2, user chose to stop waiting for program before it finished
+ */
+int
+mswin_exec_and_wait (char *utf8_whatsit, char *utf8_command,
+ char *utf8_infile, char *utf8_outfile,
+ int *exit_val, unsigned mswe_flags)
+{
+ MEvent mouse;
+ BOOL brc;
+ int rc;
+ TCHAR waitingFor[256];
+ PROCESS_INFORMATION proc_info;
+ DWORD exit_code;
+ MSWIN_EXEC_DATA exec_data;
+#ifdef ALTED_DOT
+ BOOL b_use_mswin_tw;
+#endif
+
+ mswin_exec_data_init(&exec_data);
+
+ memset(&proc_info, 0, sizeof(proc_info));
+
+ mswin_flush ();
+
+ exec_data.lptstr_infile = utf8_infile ? utf8_to_lptstr(utf8_infile) : NULL;
+ exec_data.lptstr_outfile = utf8_outfile ? utf8_to_lptstr(utf8_outfile) : NULL;
+
+ exec_data.lptstr_command = utf8_to_lptstr(utf8_command);
+ exec_data.lptstr_whatsit = utf8_to_lptstr(utf8_whatsit);
+
+#ifdef ALTED_DOT
+ /* If the command is '.', then use mswin_tw to open the file. */
+ b_use_mswin_tw = utf8_command &&
+ utf8_command[0] == '.' && utf8_command[1] == '\0';
+
+ if(b_use_mswin_tw) {
+
+ proc_info.hThread = INVALID_HANDLE_VALUE;
+ proc_info.hProcess = INVALID_HANDLE_VALUE;
+
+ exec_data.mswin_tw = mswin_tw_displaytext_lptstr(
+ exec_data.lptstr_whatsit, exec_data.lptstr_infile, 4, NULL,
+ exec_data.mswin_tw, MSWIN_DT_FILLFROMFILE);
+
+ if(exec_data.mswin_tw) {
+ mswin_set_readonly(exec_data.mswin_tw, FALSE);
+
+ /* Tell mswin_tw to write the edit contents to this file. */
+ exec_data.mswin_tw->out_file = exec_data.lptstr_outfile;
+
+ /* Make sure mswin_tw isn't freed behind our back. */
+ exec_data.mswin_tw->id = (UINT)-1;
+ brc = TRUE;
+ }
+ else {
+ brc = FALSE;
+ }
+ }
+ else
+#endif /* ALTED_DOT */
+ {
+ SECURITY_ATTRIBUTES atts;
+ STARTUPINFO start_info;
+
+ memset(&atts, 0, sizeof(atts));
+ memset(&start_info, 0, sizeof(start_info));
+
+ /* set file attributes of temp files*/
+ atts.nLength = sizeof(SECURITY_ATTRIBUTES);
+ atts.bInheritHandle = TRUE;
+ atts.lpSecurityDescriptor = NULL;
+
+ /* open files if asked for */
+ if(utf8_infile
+ && ((exec_data.infd = CreateFile(exec_data.lptstr_infile,
+ GENERIC_READ, 0, &atts,
+ OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL))
+ == INVALID_HANDLE_VALUE)){
+
+ mswin_exec_data_free(&exec_data, TRUE);
+ return(-1);
+ }
+
+ if(utf8_outfile
+ && ((exec_data.outfd = CreateFile(exec_data.lptstr_outfile,
+ GENERIC_WRITE, 0, &atts,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))
+ == INVALID_HANDLE_VALUE)){
+
+ mswin_exec_data_free(&exec_data, TRUE);
+ return(-1);
+ }
+
+ start_info.dwFlags = STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = (utf8_infile || utf8_outfile) ? SW_SHOWMINNOACTIVE : SW_SHOWNA;
+
+ /* set up i/o redirection */
+ if(utf8_infile)
+ start_info.hStdInput = exec_data.infd;
+ if(utf8_outfile)
+ start_info.hStdOutput = exec_data.outfd;
+ if(utf8_outfile && (mswe_flags & MSWIN_EAW_CAPT_STDERR))
+ start_info.hStdError = exec_data.outfd;
+ if(utf8_infile || utf8_outfile)
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ brc = CreateProcess(NULL, exec_data.lptstr_command, NULL, NULL,
+ (utf8_infile || utf8_outfile) ? TRUE : FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL, NULL, &start_info, &proc_info);
+ }
+
+ if(brc) {
+ _sntprintf(waitingFor, sizeof(waitingFor)/sizeof(TCHAR),
+ TEXT("%s is currently waiting for the %s (%s) to complete. Click \"Cancel\" to stop waiting, or \"OK\" to continue waiting."),
+ gszAppName, exec_data.lptstr_whatsit, exec_data.lptstr_command);
+
+ if(proc_info.hThread != INVALID_HANDLE_VALUE) {
+ /* Don't need the thread handle, close it now. */
+ CloseHandle (proc_info.hThread);
+ }
+
+ /*
+ * Go into holding pattern until the other application terminates
+ * or we are told to stop waiting.
+ */
+ while(TRUE){
+
+#ifdef ALTED_DOT
+ if(b_use_mswin_tw)
+ {
+ if(!exec_data.mswin_tw)
+ break;
+
+ exit_code = exec_data.mswin_tw->hwnd && exec_data.mswin_tw->out_file ?
+ STILL_ACTIVE : 0;
+ }
+ else
+#endif /* ALTED_DOT */
+ {
+ if(GetExitCodeProcess(proc_info.hProcess, &exit_code) == FALSE)
+ break;
+ }
+
+ if(exit_code == STILL_ACTIVE){
+ rc = mswin_getc();
+ brc = mswin_getmouseevent (&mouse);
+
+ if (rc != NODATA ||
+ (brc && mouse.event == M_EVENT_DOWN)) {
+ if(mswe_flags & MSWIN_EAW_CTRL_C_CANCELS){
+ if(rc == (CTRL|'C'))
+ rc = IDCANCEL;
+ }
+ else{
+ rc = MessageBox (ghTTYWnd, waitingFor, exec_data.lptstr_whatsit,
+ MB_ICONSTOP | MB_OKCANCEL);
+ }
+ SelClear ();
+ if (rc == IDCANCEL){
+ /* terminate message to child ? */
+ mswin_exec_data_free(&exec_data, TRUE);
+ return (-2);
+ }
+ }
+ }
+ else{
+ if(proc_info.hProcess != INVALID_HANDLE_VALUE) {
+ /* do something about child's exit status */
+ CloseHandle (proc_info.hProcess);
+ }
+ if(exit_val)
+ *exit_val = exit_code;
+ break;
+ }
+ }
+
+ if (gpTTYInfo->fMinimized)
+ ShowWindow (ghTTYWnd, SW_SHOWNORMAL);
+
+ /*
+ * If we're using a mswin_tw and we're not capturing the output, we
+ * just bailed immediately. If that's the case, don't bring the main
+ * window up over the top of the textwindow we just brought up.
+ */
+#ifdef ALTED_DOT
+ if(!b_use_mswin_tw || !exec_data.mswin_tw || exec_data.mswin_tw->out_file)
+#endif /* ALTED_DOT */
+ BringWindowToTop (ghTTYWnd);
+
+ mswin_exec_data_free(&exec_data, FALSE);
+ return (0);
+ }
+ else{
+ mswin_exec_data_free(&exec_data, TRUE);
+ return((rc = (int) GetLastError()) ? rc : -1); /* hack */
+ }
+
+ /* NOTREACHED */
+ return(-1);
+}
+
+
+int
+mswin_shell_exec(char *command_utf8, HINSTANCE *pChildProc)
+{
+ int quoted = 0;
+ SHELLEXECUTEINFO shell_info;
+ LPTSTR command_lpt, free_command_lpt;
+ LPTSTR p, q, parm = NULL;
+ TCHAR buf[1024];
+
+ if(!command_utf8)
+ return(-1);
+
+ free_command_lpt = command_lpt = utf8_to_lptstr(command_utf8);
+ if(!command_lpt)
+ return(-1);
+
+ mswin_flush ();
+
+ /*
+ * Pick first arg apart by whitespace until what's to the left
+ * is no longer a valid path/file. Everything else is then an
+ * command line arg...
+ */
+ if(*(p = command_lpt) == '\"'){
+ p = ++command_lpt; /* don't include quote */
+ quoted++;
+ }
+
+ q = buf;
+ while(1)
+ if(!quoted && _istspace(*p)){
+ char *buf_utf8;
+
+ *q = '\0';
+ buf_utf8 = lptstr_to_utf8(buf);
+ if(*buf == '*' || (buf_utf8 && fexist(buf_utf8, "x", (off_t *) NULL) == FIOSUC)){
+ parm = p;
+ if(buf_utf8)
+ fs_give((void **) &buf_utf8);
+
+ break;
+ }
+
+ if(buf_utf8)
+ fs_give((void **) &buf_utf8);
+ }
+ else if(quoted && *p == '\"'){
+ parm = p;
+ break;
+ }
+ else if(!(*q++ = *p++)){
+ parm = p - 1;
+ break;
+ }
+
+ if(*command_lpt && parm){
+ do
+ *parm++ = '\0';
+ while(*parm && _istspace((unsigned char) *parm));
+
+
+ /*
+ * HACK -- since star is very unlikely to actually appear
+ * in a command name thats launched via a shell command line,
+ * a leading one indicates special handling.
+ */
+ if(command_lpt[0] == '*'){
+ if(!_tcsncmp(command_lpt + 1, TEXT("Shell*"), 8)){
+ /* Leave it to ShellExecute magic to "open" the thing */
+ command_lpt = parm;
+ parm = NULL;
+ }
+ }
+ }
+ else{
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(-1);
+ }
+
+ memset(&shell_info, 0, sizeof(SHELLEXECUTEINFO));
+ shell_info.cbSize = sizeof(SHELLEXECUTEINFO);
+ shell_info.fMask = SEE_MASK_DOENVSUBST
+ | SEE_MASK_NOCLOSEPROCESS
+ | SEE_MASK_FLAG_DDEWAIT;
+ shell_info.hwnd = ghTTYWnd;
+ shell_info.lpFile = command_lpt;
+ shell_info.lpParameters = parm;
+ shell_info.lpDirectory = NULL; /* default is current directory */
+ shell_info.nShow = SW_SHOWNORMAL;
+
+ ShellExecuteEx(&shell_info);
+
+ if((int)(LONG_PTR)shell_info.hInstApp > 32){
+ if(pChildProc)
+ *pChildProc = shell_info.hProcess;
+
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(0); /* success! */
+ }
+
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(-1);
+}
+
+
+/*
+ * Generate an error message for a failed windows exec or loadlibrary.
+ */
+void
+mswin_exec_err_msg(char *what, int status, char *buf, size_t buflen)
+{
+ switch(status){
+ case 2:
+ case 3:
+ snprintf(buf, buflen, "%s not found.", what);
+ break;
+
+ case 8:
+ snprintf(buf, buflen, "Not enough memory to run %s.", what);
+ break;
+
+ default:
+ snprintf(buf, buflen, "Error %d starting %s.", status, what);
+ break;
+ }
+}
+
+
+int
+mswin_set_quit_confirm (int confirm)
+{
+ gConfirmExit = (confirm != 0);
+ return (confirm);
+}
+
+
+/*
+ * Called when Windows is in shutting down. Before actually shutting down
+ * Windows goes around to all the applications and asks if it is OK with
+ * them to shut down (WM_QUERYENDSESSION).
+ * If gConfirmExit is set, ask the user if they want to exit.
+ * Returning zero will stop the shutdown, non-zero allows it to proceed.
+ */
+LOCAL LRESULT
+ConfirmExit (void)
+{
+ TCHAR msg[256];
+ int rc;
+
+ if(gConfirmExit){
+ _sntprintf(msg, sizeof(msg)/sizeof(TCHAR),
+ TEXT("Exiting may cause you to lose work in %s, Exit?"),
+ gszAppName);
+ rc = MessageBox (ghTTYWnd, msg, gszAppName, MB_ICONSTOP | MB_OKCANCEL);
+ if(rc == IDCANCEL)
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Registry access functions
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Useful def's
+ */
+#define MSWR_KEY_MAX 128
+#define MSWR_VAL_MAX 128
+#define MSWR_CLASS_MAX 128
+#define MSWR_DATA_MAX 1024
+
+
+#define MSWR_ROOT TEXT("Software\\University of Washington\\Alpine\\1.0")
+#define MSWR_CAPABILITIES TEXT("Software\\University of Washington\\Alpine\\1.0\\Capabilities")
+#define MSWR_APPNAME TEXT("Alpine")
+#define MSWR_DLLPATH TEXT("DLLPath")
+#define MSWR_DLLNAME TEXT("pmapi32.dll")
+#define MSWRDBUF 1152
+
+
+struct mswin_reg_key {
+ HKEY rhk; /* root key (HKEY_LOCAL_MACHINE, ...) */
+ LPTSTR *knames; /* NULL terminated list of keys */
+};
+
+LPTSTR mswin_pine_hklm_regs[] = {
+ MSWR_ROOT,
+ TEXT("Software\\Clients\\Mail\\Alpine"),
+ TEXT("Software\\Clients\\News\\Alpine"),
+ TEXT("Software\\Classes\\Alpine.Url.Mailto"),
+ TEXT("Software\\Classes\\Alpine.Url.News"),
+ TEXT("Software\\Classes\\Alpine.Url.Nntp"),
+ TEXT("Software\\Classes\\Alpine.Url.Imap"),
+ NULL
+};
+
+LPTSTR mswin_pine_hkcu_regs[] = {
+ MSWR_ROOT,
+ NULL
+};
+
+static struct mswin_reg_key mswin_pine_regs[] = {
+ {HKEY_LOCAL_MACHINE, mswin_pine_hklm_regs},
+ {HKEY_CURRENT_USER, mswin_pine_hkcu_regs},
+ {NULL, NULL}
+};
+
+
+/*
+ * data: unitialized buffer, could be null
+ */
+int
+mswin_reg(int op, int tree, char *data_utf8, size_t size)
+{
+ LPTSTR data_lptstr = NULL;
+ int rv;
+
+ if(data_utf8){
+ if(size == 0){
+ /* size is zero when op & MSWR_OP_SET */
+ data_lptstr = utf8_to_lptstr(data_utf8);
+ }
+ else {
+ data_lptstr = (LPTSTR)MemAlloc(size * sizeof(TCHAR));
+ data_lptstr[0] = '\0';
+ }
+ }
+
+ rv = mswin_reg_lptstr(op, tree, data_lptstr, size);
+
+ if(data_utf8 && data_lptstr){
+ char *t_utf8str;
+ if(size){
+ t_utf8str = lptstr_to_utf8(data_lptstr);
+ strncpy(data_utf8, t_utf8str, size);
+ data_utf8[size-1] = '\0';
+ MemFree((void *)t_utf8str);
+ }
+ MemFree((void *)data_lptstr);
+ }
+ return(rv);
+}
+
+int
+mswin_reg_lptstr(int op, int tree, LPTSTR data_lptstr, size_t size)
+{
+ if(op & MSWR_OP_SET){
+ switch(tree){
+ case MSWR_PINE_RC :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineRC"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_CONF :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineConf"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_AUX :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineAux"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_DIR :
+ MSWRAlpineSet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("Pinedir"), op & MSWR_OP_FORCE, data_lptstr);
+ MSWRAlpineSetHandlers(op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_EXE :
+ MSWRAlpineSet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("PineEXE"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_POS :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PinePos"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ default :
+ break;
+ }
+ }
+ else if(op & MSWR_OP_GET){
+ switch(tree){
+ case MSWR_PINE_RC :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineRC"), data_lptstr, size));
+ case MSWR_PINE_CONF :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineConf"), data_lptstr, size));
+ case MSWR_PINE_AUX :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineAux"), data_lptstr, size));
+ case MSWR_PINE_DIR :
+ return(MSWRAlpineGet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("Pinedir"), data_lptstr, size));
+ case MSWR_PINE_EXE :
+ return(MSWRAlpineGet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("PineEXE"), data_lptstr, size));
+ case MSWR_PINE_POS :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PinePos"), data_lptstr, size));
+ default :
+ break;
+ }
+ }
+ else if(op & MSWR_OP_BLAST){
+ int rv = 0, i, j;
+
+ for(i = 0; mswin_pine_regs[i].rhk; i++){
+ for(j = 0; mswin_pine_regs[i].knames[j]; j++)
+ MSWRClear(mswin_pine_regs[i].rhk, mswin_pine_regs[i].knames[j]);
+ }
+ if(rv) return -1;
+ }
+ /* else, ignore unknown op? */
+
+ return(0);
+}
+
+
+LOCAL void
+MSWRAlpineSet(HKEY hRootKey, LPTSTR subkey, LPTSTR val, int update, LPTSTR data)
+{
+ HKEY hKey;
+ TCHAR keybuf[MSWR_KEY_MAX+1];
+
+ _sntprintf(keybuf, MSWR_KEY_MAX+1, TEXT("%s%s%s"), MSWR_ROOT,
+ (subkey && *subkey != '\\') ? TEXT("\\") : TEXT(""),
+ (subkey) ? TEXT("\\") : TEXT(""));
+
+ if(RegCreateKeyEx(hRootKey, keybuf, 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, NULL) == ERROR_SUCCESS){
+ if(update || RegQueryValueEx(hKey, val, NULL, NULL,
+ NULL, NULL) != ERROR_SUCCESS)
+ RegSetValueEx(hKey, val, 0, REG_SZ, (LPBYTE)data, (DWORD)(_tcslen(data)+1)*sizeof(TCHAR));
+
+ RegCloseKey(hKey);
+ }
+}
+
+
+
+LOCAL int
+MSWRAlpineGet(HKEY hKey, LPTSTR subkey, LPTSTR val, LPTSTR data_lptstr, size_t len)
+{
+ TCHAR keybuf[MSWR_KEY_MAX+1];
+ DWORD dlen = (DWORD)len;
+
+ _sntprintf(keybuf, MSWR_KEY_MAX+1, TEXT("%s%s%s"), MSWR_ROOT,
+ (subkey && *subkey != '\\') ? TEXT("\\") : TEXT(""),
+ (subkey) ? TEXT("\\") : TEXT(""));
+
+ return(MSWRPeek(hKey, keybuf, val, data_lptstr, &dlen) == TRUE);
+}
+
+
+
+LOCAL void
+MSWRAlpineSetHandlers(int update, LPTSTR path_lptstr)
+{
+ HKEY hKey, hSubKey;
+ DWORD dwDisp;
+ BYTE tmp_b[MSWR_DATA_MAX];
+ unsigned long tmplen = MSWR_DATA_MAX, tmp_lptstr_tcharlen = MSWR_DATA_MAX/sizeof(TCHAR);
+ int exists;
+ LPTSTR tmp_lptstr = (LPTSTR)tmp_b;
+
+ /* Register as a mail client on this system */
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ MSWR_ROOT, 0, KEY_ALL_ACCESS,
+ &hKey) == ERROR_SUCCESS){
+ if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, MSWR_CAPABILITIES, 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationDescription"),
+ TEXT("Alpine - A program for sending, receiving, and filing email and news, whether stored locally or accessed over the network via IMAP, POP3, or NNTP. Alpine is the successor to Pine, and also is maintained by the University of Washington."));
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationName"),
+ TEXT("Alpine"));
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen, TEXT("%salpine.exe,0"), path_lptstr);
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationIcon"), tmp_lptstr);
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("mailto"), TEXT("Alpine.Url.Mailto"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("news"), TEXT("Alpine.Url.News"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("nntp"), TEXT("Alpine.Url.Nntp"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("imap"), TEXT("Alpine.Url.Imap"));
+ RegCloseKey(hSubKey);
+ }
+ RegCloseKey(hKey);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\RegisteredApplications"), 0,
+ KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ MSWRPoke(hKey, NULL, TEXT("Alpine"), MSWR_CAPABILITIES);
+ RegCloseKey(hKey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("Software\\Classes"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.News"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Imap"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_IMAP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ RegCloseKey(hKey);
+ }
+ }
+
+ if((exists = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\Mail\\Alpine"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\Mail\\Alpine"),
+ 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists){
+ DWORD dType;
+ char *tmp_utf8str = NULL;
+
+ MSWRPoke(hKey, NULL, NULL, MSWR_APPNAME);
+ /* set up MAPI dll stuff */
+ *tmp_b = 0;
+ RegQueryValueEx(hKey, MSWR_DLLPATH, NULL, &dType, tmp_b, &tmplen);
+ tmp_lptstr = (LPTSTR)tmp_b;
+ if(!(*tmp_lptstr)
+ || (can_access(tmp_utf8str = lptstr_to_utf8(tmp_lptstr), ACCESS_EXISTS) != 0)){
+ if(*tmp_lptstr)
+ RegDeleteValue(hKey, MSWR_DLLPATH);
+ if(tmp_utf8str)
+ MemFree((void *)tmp_utf8str);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("%s%s"), path_lptstr, MSWR_DLLNAME);
+
+ if(can_access(tmp_utf8str = lptstr_to_utf8(tmp_lptstr), ACCESS_EXISTS) == 0)
+ MSWRPoke(hKey, NULL, MSWR_DLLPATH, tmp_lptstr);
+ }
+ if(tmp_utf8str)
+ MemFree((void *)tmp_utf8str);
+ /* Set "mailto" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\Mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+
+ /* Set normal handler */
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("\"%salpine.exe\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ /* Register as a news client on this system */
+ if((exists = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\News\\Alpine"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\News\\Alpine"),
+ 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists){
+ MSWRPoke(hKey, NULL, NULL, MSWR_APPNAME);
+
+ /* Set "news" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\news"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ /* Set "nntp" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+
+ /* Set normal handler */
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("\"%salpine.exe\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ /* Register as a IMAP url handler */
+ if((exists = RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("imap"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("imap"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists)
+ MSWRProtocolSet(hKey, MSWR_SDC_IMAP, path_lptstr);
+
+ RegCloseKey(hKey);
+ }
+}
+
+
+
+char *
+mswin_reg_default_browser(char *url_utf8)
+{
+ TCHAR scheme[MSWR_KEY_MAX+1], *p;
+ LPTSTR url_lptstr;
+ char cmdbuf[MSWR_DATA_MAX], *cmd = NULL;
+
+ url_lptstr = utf8_to_lptstr(url_utf8);
+
+ if(url_lptstr && (p = _tcschr(url_lptstr, ':')) && p - url_lptstr < MSWR_KEY_MAX){
+ _tcsncpy(scheme, url_lptstr, p - url_lptstr);
+ scheme[p-url_lptstr] = '\0';
+
+ if(MSWRShellCanOpen(scheme, cmdbuf, MSWR_DATA_MAX, 0)){
+ size_t len;
+
+ len = strlen(cmdbuf) + 2;
+ cmd = (char *) fs_get((len+1) * sizeof(char));
+ if(cmd){
+ if(strchr(cmdbuf, '*'))
+ snprintf(cmd, len+1, "\"%s\"", cmdbuf);
+ else{
+ strncpy(cmd, cmdbuf, len);
+ cmd[len] = '\0';
+ }
+ }
+ }
+ }
+
+ MemFree((void *)url_lptstr);
+
+ return(cmd);
+}
+
+
+int
+mswin_is_def_client(int type)
+{
+ TCHAR buf[MSWR_KEY_MAX+1];
+ DWORD buflen = MSWR_KEY_MAX;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS)
+ return -1;
+
+ if(MSWRPeek(HKEY_CURRENT_USER,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"), NULL,
+ buf, &buflen) && !_tcscmp(buf, TEXT("Alpine")))
+ return 1;
+ buflen = MSWR_KEY_MAX;
+ if(MSWRPeek(HKEY_LOCAL_MACHINE,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"), NULL,
+ buf, &buflen) && !_tcscmp(buf, TEXT("Alpine")))
+ return 1;
+ return 0;
+}
+
+int
+mswin_set_def_client(int type)
+{
+ HKEY hKey;
+ int successful_set = 0;
+ TCHAR path_lptstr[MSWR_DATA_MAX];
+ DWORD dwDisp;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS)
+ return 1;
+ if(RegOpenKeyEx(HKEY_CURRENT_USER,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ successful_set = MSWRPoke(hKey, NULL, NULL, TEXT("Alpine"));
+ RegCloseKey(hKey);
+ }
+ else if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ successful_set = MSWRPoke(hKey, NULL, NULL, TEXT("Alpine"));
+ RegCloseKey(hKey);
+ }
+ if(successful_set){
+ mswin_reg_lptstr(MSWR_OP_GET, MSWR_PINE_DIR, path_lptstr, sizeof(path_lptstr)/sizeof(TCHAR));
+ if(type == MSWR_SDC_MAIL){
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("mailto"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ }
+ else if(type == MSWR_SDC_NEWS){
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("news"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("news"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("nntp"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+MSWRProtocolSet(HKEY hKey, int type, LPTSTR path_lptstr)
+{
+ TCHAR tmp_lptstr[MSWR_DATA_MAX];
+ BYTE EditFlags[4];
+ unsigned long tmp_lptstr_len = MSWR_DATA_MAX;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS
+ && type != MSWR_SDC_NNTP && type != MSWR_SDC_IMAP)
+ return -1;
+ MSWRPoke(hKey, NULL, NULL, type == MSWR_SDC_MAIL
+ ? TEXT("URL:MailTo Protocol")
+ : type == MSWR_SDC_NEWS ? TEXT("URL:News Protocol")
+ : type == MSWR_SDC_NNTP ? TEXT("URL:NNTP Protocol")
+ : TEXT("URL:IMAP Prototcol"));
+ MSWRPoke(hKey, NULL, TEXT("URL Protocol"), TEXT(""));
+
+ EditFlags[0] = 0x02;
+ EditFlags[1] = EditFlags[2] = EditFlags[3] = 0;
+
+ (void) RegDeleteValue(hKey, TEXT("EditFlags"));
+ (void) RegSetValueEx(hKey, TEXT("EditFlags"), 0, REG_BINARY,
+ EditFlags, (DWORD) 4);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_len,
+ TEXT("%salpine.exe,0"), path_lptstr);
+ MSWRPoke(hKey, TEXT("DefaultIcon"), NULL, tmp_lptstr);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_len,
+ TEXT("\"%salpine.exe\" -url \"%%1\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ return 0;
+}
+
+
+/* cmdbuf can stay char * since it's our string */
+BOOL
+MSWRShellCanOpen(LPTSTR key, char *cmdbuf, int clen, int allow_noreg)
+{
+ HKEY hKey;
+ BOOL rv = FALSE;
+
+ /* See if Shell provides a method to open the thing... */
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, key,
+ 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+
+ if(cmdbuf){
+ strncpy(cmdbuf, "*Shell*", clen);
+ cmdbuf[clen-1] = '\0';
+ }
+
+ rv = TRUE;
+
+ RegCloseKey(hKey);
+ }
+ else if(allow_noreg && cmdbuf){
+ strncpy(cmdbuf, "*Shell*", clen);
+ cmdbuf[clen-1] = '\0';
+ rv = TRUE;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Fundamental registry access function that queries for particular values.
+ */
+LOCAL BOOL
+MSWRPeek(HKEY hRootKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data_lptstr, DWORD *dlen)
+{
+ HKEY hKey;
+ DWORD dtype, dlen_bytes = (dlen ? *dlen : 0) * sizeof(TCHAR);
+ LONG rv = !ERROR_SUCCESS;
+
+ if(RegOpenKeyEx(hRootKey, subkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+ rv = RegQueryValueEx(hKey, valstr, NULL, &dtype, (LPBYTE)data_lptstr, &dlen_bytes);
+ if(dlen)
+ (*dlen) = dlen_bytes;
+ RegCloseKey(hKey);
+ }
+
+ return(rv == ERROR_SUCCESS);
+}
+
+
+/*
+ * Fundamental registry access function that sets particular values.
+ */
+LOCAL BOOL
+MSWRPoke(HKEY hKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data_lptstr)
+{
+ DWORD dtype, dwDisp, dlen = MSWR_DATA_MAX;
+ BYTE olddata[MSWR_DATA_MAX];
+ BOOL rv = FALSE;
+
+ if(!subkey
+ || RegCreateKeyEx(hKey, subkey,
+ 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, NULL,
+ &hKey, &dwDisp) == ERROR_SUCCESS){
+
+ if(RegQueryValueEx(hKey, valstr, NULL, &dtype,
+ olddata, &dlen) != ERROR_SUCCESS
+ || _tcscmp((LPTSTR)olddata, data_lptstr)){
+ (void) RegDeleteValue(hKey, valstr);
+ rv = RegSetValueEx(hKey, valstr, 0, REG_SZ,
+ (LPBYTE)data_lptstr,
+ (DWORD)(_tcslen(data_lptstr) + 1)*sizeof(TCHAR)) == ERROR_SUCCESS;
+ }
+
+ if(subkey)
+ RegCloseKey(hKey);
+ }
+
+ return(rv);
+}
+
+
+LOCAL void
+MSWRLineBufAdd(MSWR_LINE_BUFFER_S *lpLineBuf, LPTSTR line)
+{
+ if(lpLineBuf->offset >= lpLineBuf->size){
+ /* this probably won't happen, but just in case */
+ lpLineBuf->size *= 2;
+ lpLineBuf->linep = (char **)MemRealloc(lpLineBuf->linep,
+ (lpLineBuf->size + 1)*sizeof(char *));
+ }
+
+ lpLineBuf->linep[lpLineBuf->offset++] = lptstr_to_utf8(line);
+ lpLineBuf->linep[lpLineBuf->offset] = NULL;
+}
+
+/*
+ * Dump all of the registry values from a list of keys into an array
+ * of UTF8-formatted strings.
+ */
+char **
+mswin_reg_dump(void)
+{
+ MSWR_LINE_BUFFER_S lineBuf;
+ unsigned long initial_size = 256;
+ int i, j;
+
+ lineBuf.linep = (char **)MemAlloc((initial_size+1)*sizeof(char *));
+ lineBuf.size = initial_size;
+ lineBuf.offset = 0;
+
+ MSWRLineBufAdd(&lineBuf, TEXT("Registry values for Alpine:"));
+ MSWRLineBufAdd(&lineBuf, TEXT(""));
+
+ for(i = 0; mswin_pine_regs[i].rhk; i++){
+ MSWRLineBufAdd(&lineBuf, mswin_pine_regs[i].rhk == HKEY_LOCAL_MACHINE
+ ? TEXT("HKEY_LOCAL_MACHINE")
+ : TEXT("HKEY_CURRENT_USER"));
+ for(j = 0; mswin_pine_regs[i].knames[j]; j++)
+ MSWRDump(mswin_pine_regs[i].rhk,
+ mswin_pine_regs[i].knames[j],
+ 1, &lineBuf);
+ }
+
+ return(lineBuf.linep);
+}
+
+
+/*
+ * Recursive function to crawl a registry hierarchy and print the contents.
+ *
+ * Returns: 0
+ */
+LOCAL int
+MSWRDump(HKEY hKey, LPTSTR pSubKey, int keyDepth, MSWR_LINE_BUFFER_S *lpLineBuf)
+{
+ HKEY hSubKey;
+ TCHAR KeyBuf[MSWR_KEY_MAX+1];
+ TCHAR ValBuf[MSWR_VAL_MAX+1];
+ BYTE DataBuf[MSWR_DATA_MAX+1];
+ DWORD dwKeyIndex, dwKeyLen;
+ DWORD dwValIndex, dwValLen, dwDataLen;
+ DWORD dwType;
+ FILETIME ftKeyTime;
+ TCHAR new_buf[1024];
+ unsigned int new_buf_len = 1024;
+ int i, j, k, tab_width = 4;
+
+ /* open the passed subkey */
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_READ, &hSubKey) == ERROR_SUCCESS){
+
+ /* print out key name here */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k, TEXT("%s"), pSubKey);
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+
+ keyDepth++;
+
+ /* Loop through the string values and print their data */
+ for(dwValIndex = 0L, dwValLen = MSWR_VAL_MAX + 1, dwDataLen = MSWR_DATA_MAX + 1;
+ RegEnumValue(hSubKey, dwValIndex, ValBuf, &dwValLen, NULL, &dwType,
+ DataBuf, &dwDataLen) == ERROR_SUCCESS;
+ dwValIndex++, dwValLen = MSWR_VAL_MAX + 1, dwDataLen = MSWR_DATA_MAX + 1){
+
+ /* print out value here */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k,
+ TEXT("%.*s = %.*s"),
+ dwValLen ? dwValLen : 128,
+ dwValLen ? ValBuf : TEXT("(Default)"),
+ dwType == REG_SZ && dwDataLen ? dwDataLen/sizeof(TCHAR) : 128,
+ (dwType == REG_SZ
+ ? (dwDataLen ? (LPTSTR)DataBuf : TEXT("(No data)"))
+ : TEXT("(Some non-string data)")));
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+ }
+
+ /* Loop through the subkeys and recursively print their data */
+ for(dwKeyIndex = 0L, dwKeyLen = MSWR_KEY_MAX + 1;
+ RegEnumKeyEx(hSubKey, dwKeyIndex, KeyBuf, &dwKeyLen,
+ NULL, NULL, NULL, &ftKeyTime) == ERROR_SUCCESS;
+ dwKeyIndex++, dwKeyLen = MSWR_KEY_MAX + 1){
+ MSWRDump(hSubKey, KeyBuf, keyDepth, lpLineBuf);
+ }
+ }
+ else {
+ /* Couldn't open the key. Must not be defined. */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k, TEXT("%s - Not Defined"), pSubKey);
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Fundamental registry access function that removes a registry key
+ * and all its subkeys, their values and data
+ */
+LOCAL int
+MSWRClear(HKEY hKey, LPTSTR pSubKey)
+{
+ HKEY hSubKey;
+ TCHAR KeyBuf[MSWR_KEY_MAX+1];
+ DWORD dwKeyIndex, dwKeyLen;
+ FILETIME ftKeyTime;
+ int rv = 0;
+
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_READ, &hSubKey) == ERROR_SUCCESS){
+ RegCloseKey(hSubKey);
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_ALL_ACCESS, &hSubKey) == ERROR_SUCCESS){
+ for(dwKeyIndex = 0L, dwKeyLen = MSWR_KEY_MAX + 1;
+ RegEnumKeyEx(hSubKey, dwKeyIndex, KeyBuf, &dwKeyLen,
+ NULL, NULL, NULL, &ftKeyTime) == ERROR_SUCCESS;
+ dwKeyLen = MSWR_KEY_MAX + 1)
+ if(MSWRClear(hSubKey, KeyBuf)!= 0){
+ rv = -1;
+ dwKeyIndex++;
+ }
+ RegCloseKey(hSubKey);
+ if(RegDeleteKey(hKey, pSubKey) != ERROR_SUCCESS || rv)
+ return -1;
+ return 0;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Text display Windows.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Show a help message.
+ * Help text comes as a null terminated array of pointers to lines of
+ * text. Stuff these into a buffer and pass that to MessageBox.
+ */
+void
+mswin_showhelpmsg(WINHAND wnd, char **helplines)
+{
+ char **l;
+ char *helptext_utf8, *p;
+ size_t buflen;
+ HWND hWnd;
+ LPTSTR helptext_lpt;
+
+ hWnd = (HWND) wnd;
+ if(hWnd == NULL)
+ hWnd = ghTTYWnd;
+
+ buflen = 0;
+ for(l = helplines; *l != NULL; ++l)
+ buflen += (strlen(*l)+1);
+
+ helptext_utf8 = (char *) fs_get((buflen + 1) * sizeof(char));
+ if(helptext_utf8 == NULL)
+ return;
+
+ *helptext_utf8 = '\0';
+
+ p = helptext_utf8;
+ for(l = helplines; *l != NULL; ++l){
+ snprintf(p, buflen+1-(p-helptext_utf8), "%s%s", (p == helptext_utf8) ? "" : " ", *l);
+ p += strlen(p);
+ }
+
+ helptext_lpt = utf8_to_lptstr(helptext_utf8);
+
+ MessageBox(hWnd, helptext_lpt, TEXT("Help"),
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+
+ fs_give((void **) &helptext_utf8);
+ fs_give((void **) &helptext_lpt);
+}
+
+/*
+ * Callback for when new mail or imap telem window gets canned.
+ */
+LOCAL void
+mswin_tw_close_imap_telem_or_new_mail(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ HMENU hMenu;
+
+ if((mswin_tw->id == IDM_OPT_IMAPTELEM) && gIMAPDebugOFFCallback)
+ (*gIMAPDebugOFFCallback)();
+
+ if(hMenu = GetMenu(ghTTYWnd))
+ CheckMenuItem (hMenu, mswin_tw->id, MF_BYCOMMAND | MF_UNCHECKED);
+}
+
+/*
+ * Callback for when new mail or imap telem window gets cleared.
+ */
+LOCAL void
+mswin_tw_clear_imap_telem_or_new_mail(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ char *tmtxt;
+ time_t now = time((time_t *)0);
+ LPCTSTR desc = (mswin_tw->id == IDM_OPT_IMAPTELEM) ?
+ TEXT("IMAP telemetry recorder") :
+ TEXT("New Mail window");
+
+ tmtxt = ctime(&now);
+
+ mswin_tw_printf(mswin_tw, TEXT("%s started at %.*S"),
+ desc, MIN(100, strlen(tmtxt)-1), tmtxt);
+
+ if(mswin_tw->id == IDM_OPT_NEWMAILWIN)
+ {
+ int i;
+ int fromlen, subjlen, foldlen, len;
+ TCHAR ring2[256];
+
+ foldlen = (int)(.18 * gNMW_width);
+ foldlen = foldlen > 5 ? foldlen : 5;
+ fromlen = (int)(.28 * gNMW_width);
+ subjlen = gNMW_width - 2 - foldlen - fromlen;
+
+ mswin_tw_printf(mswin_tw,
+ TEXT(" %-*s%-*s%-*s"),
+ fromlen, TEXT("From:"),
+ subjlen, TEXT("Subject:"),
+ foldlen, TEXT("Folder:"));
+
+ len = 2 + fromlen + subjlen + foldlen;
+ if(len >= ARRAYSIZE(ring2) + 1)
+ len = ARRAYSIZE(ring2) - 2;
+ for(i = 0; i < len; i++)
+ ring2[i] = '-';
+ ring2[i] = '\0';
+ mswin_tw_puts_lptstr(mswin_tw, ring2);
+ }
+}
+
+/*
+ * Init new mail or imap telem window
+ */
+LOCAL int
+mswin_tw_init(MSWIN_TEXTWINDOW *mswin_tw, int id, LPCTSTR title)
+{
+ if(mswin_tw->hwnd){
+ /* destroy it */
+ mswin_tw_close(mswin_tw);
+ }
+ else{
+ HMENU hMenu;
+
+ mswin_tw->id = id;
+ mswin_tw->hInstance = ghInstance;
+ mswin_tw->print_callback = mswin_tw_print_callback;
+ mswin_tw->close_callback = mswin_tw_close_imap_telem_or_new_mail;
+ mswin_tw->clear_callback = mswin_tw_clear_imap_telem_or_new_mail;
+
+ // If the rcSize rect is empty, then init it to something resembling
+ // the size of the current Pine window. Otherwise we just re-use
+ // whatever the last position/size was.
+ if(IsRectEmpty(&mswin_tw->rcSize))
+ {
+ RECT cliRect;
+ RECT sizeRect;
+
+ GetClientRect(ghTTYWnd, &cliRect);
+ sizeRect.left = CW_USEDEFAULT;
+ sizeRect.top = CW_USEDEFAULT;
+ sizeRect.right = cliRect.right;
+ sizeRect.bottom = cliRect.bottom;
+ mswin_tw->rcSize = sizeRect;
+ }
+
+ if(!mswin_tw_create(mswin_tw, title))
+ return 1;
+
+ mswin_tw_setfont(mswin_tw, gpTTYInfo->hTTYFont);
+ mswin_tw_setcolor(mswin_tw, gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+
+ mswin_tw_clear(mswin_tw);
+
+ if(id == IDM_OPT_IMAPTELEM)
+ {
+ if(gIMAPDebugONCallback)
+ (*gIMAPDebugONCallback)();
+
+ mswin_tw_showwindow(mswin_tw, SW_SHOWNA);
+ }
+ else if(id == IDM_OPT_NEWMAILWIN){
+ mswin_tw_showwindow(mswin_tw, SW_SHOW);
+ }
+
+ if(hMenu = GetMenu(ghTTYWnd))
+ CheckMenuItem (hMenu, mswin_tw->id, MF_BYCOMMAND | MF_CHECKED);
+ }
+
+ return(0);
+}
+
+/*
+ * Display text in a window.
+ *
+ * Parameters:
+ * title - Title of window.
+ * pText - address of text to display.
+ * textLen - Length of text, in bytes. Limited to 64K.
+ * pLines - Array of pointers to lines of text. Each
+ * line is a sepreate allocation block. The
+ * entry in the array of pointers should be a
+ * NULL.
+ *
+ * The text can be supplied as a buffer (pText and textLen) in which
+ * lines are terminated by CRLF (including the last line in buffer).
+ * This buffer should be NULL terminated, too.
+ * Or it can be supplied as a NULL terminated array of pointers to
+ * lines. Each entry points to a separately allocated memory block
+ * containing a null terminated string.
+ *
+ * If the function succeeds the memory containing the text will be
+ * used until the user closes the window, at which point it will be
+ * freed.
+ *
+ * Returns:
+ * mswin_tw - SUCCESS
+ * NULL - Failed.
+ */
+MSWIN_TEXTWINDOW *
+mswin_displaytext(char *title_utf8, char *pText_utf8, size_t npText,
+ char **pLines_utf8, MSWIN_TEXTWINDOW *mswin_tw, int flags)
+{
+ LPTSTR title_lpt = NULL, pText_lpt = NULL, *pLines_lpt = NULL;
+ char **l;
+ size_t pText_lpt_len = 0;
+ int i, count = 0;
+
+ if(pLines_utf8 != NULL){
+ for(count=0, l = pLines_utf8; *l != NULL; ++l)
+ ++count;
+
+ pLines_lpt = (LPTSTR *) fs_get((count + 1) * sizeof(LPTSTR));
+ memset(pLines_lpt, 0, (count + 1) * sizeof(LPTSTR));
+ for(i=0, l = pLines_utf8; *l != NULL && i < count; ++l, ++i)
+ pLines_lpt[i] = utf8_to_lptstr(*l);
+
+ /*caller expects this to be freed */
+ if(!(flags & MSWIN_DT_NODELETE)){
+ for(l = pLines_utf8; *l != NULL; ++l)
+ fs_give((void **) l);
+
+ fs_give((void **) &pLines_utf8);
+ }
+ }
+
+ if(pText_utf8 != NULL && npText > 0){
+ pText_lpt = utf8_to_lptstr(pText_utf8);
+ pText_lpt_len = lstrlen(pText_lpt);
+
+ /*caller expects this to be freed */
+ if(!(flags & MSWIN_DT_NODELETE))
+ fs_give((void **) &pText_utf8);
+ }
+
+ if(title_utf8 != NULL)
+ title_lpt = utf8_to_lptstr(title_utf8);
+
+ mswin_tw = mswin_tw_displaytext_lptstr(title_lpt, pText_lpt, pText_lpt_len,
+ pLines_lpt, mswin_tw, flags);
+
+ if(pLines_lpt != NULL){
+ for(i=0; i < count; ++i)
+ if(pLines_lpt[i])
+ fs_give((void **) &pLines_lpt[i]);
+
+ fs_give((void **) &pLines_lpt);
+ }
+
+ if(pText_lpt != NULL)
+ fs_give((void **) &pText_lpt);
+
+ if(title_lpt != NULL)
+ fs_give((void **) &title_lpt);
+
+ return(mswin_tw);
+}
+
+/*
+ * Callback for when a generic mswin_tw gets killed.
+ */
+LOCAL void
+mswin_tw_close_callback(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw->id != -1)
+ MemFree(mswin_tw);
+}
+
+/*
+ * Create a new mswin_tw window. If the MSWIN_DT_USEALTWINDOW flag is set,
+ * then (re)use gMswinAltWin.
+ */
+LOCAL MSWIN_TEXTWINDOW *
+mswin_tw_displaytext_lptstr (LPTSTR title, LPTSTR pText, size_t textLen, LPTSTR *pLines,
+ MSWIN_TEXTWINDOW *mswin_tw, int flags)
+{
+ if (pText == NULL && pLines == NULL)
+ return (NULL);
+
+ /* Was a valid existing window supplied? */
+ if(!mswin_tw)
+ {
+ int ctrl_down = GetKeyState(VK_CONTROL) < 0;
+
+ if((flags & MSWIN_DT_USEALTWINDOW) && !ctrl_down)
+ {
+ mswin_tw = &gMswinAltWin;
+ mswin_tw->id = (UINT)-1; // Tell mswin_tw_close_callback not
+ // to free this buffer.
+ }
+ else
+ {
+ mswin_tw = (MSWIN_TEXTWINDOW *)MemAlloc (sizeof (MSWIN_TEXTWINDOW));
+ if(!mswin_tw)
+ return NULL;
+
+ memset(mswin_tw, 0, sizeof(MSWIN_TEXTWINDOW));
+ mswin_tw->id = 0;
+ }
+
+ mswin_tw->hInstance = ghInstance;
+ mswin_tw->print_callback = mswin_tw_print_callback;
+ mswin_tw->close_callback = mswin_tw_close_callback;
+ mswin_tw->clear_callback = NULL;
+ }
+
+ /* Create a new window. */
+ if (!mswin_tw->hwnd) {
+
+ if(IsRectEmpty(&mswin_tw->rcSize))
+ {
+ RECT sizeRect;
+ RECT cliRect;
+
+ GetClientRect(ghTTYWnd, &cliRect);
+ sizeRect.left = CW_USEDEFAULT;
+ sizeRect.top = CW_USEDEFAULT;
+ sizeRect.right = cliRect.right;
+ sizeRect.bottom = cliRect.bottom;
+ mswin_tw->rcSize = sizeRect;
+ }
+
+ if(!mswin_tw_create(mswin_tw, title)) {
+ MemFree (mswin_tw);
+ return (NULL);
+ }
+
+ mswin_tw_setfont(mswin_tw, gpTTYInfo->hTTYFont);
+ mswin_tw_setcolor(mswin_tw, gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+ }
+ else {
+ /* Invalidate whole window, change title, and move to top. */
+ SetWindowText (mswin_tw->hwnd, title);
+ SetWindowPos (mswin_tw->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ mswin_tw_clear(mswin_tw);
+
+ /*
+ * How was text supplied?
+ */
+ if (pLines != NULL) {
+ LPTSTR *l;
+
+ /* Array of pointers to lines supplied. Count lines. */
+ for (l = pLines; *l != NULL; ++l){
+ mswin_tw_puts_lptstr(mswin_tw, *l);
+ }
+ }
+ else {
+#ifdef ALTED_DOT
+ if(flags & MSWIN_DT_FILLFROMFILE)
+ mswin_tw_fill_from_file(mswin_tw, pText);
+ else
+#endif /* ALTED_DOT */
+ /* Pointer to block of text supplied. */
+ mswin_tw_puts_lptstr(mswin_tw, pText);
+ }
+
+ mswin_tw_setsel(mswin_tw, 0, 0);
+
+ mswin_tw_showwindow(mswin_tw, SW_SHOW);
+ return mswin_tw;
+}
+
+void
+mswin_enableimaptelemetry(int state)
+{
+ HMENU hMenu;
+ MENUITEMINFO mitem;
+ TCHAR buf[256];
+ int i;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return;
+
+ /*
+ * Make sure hMenu's the right menubar
+ */
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = (MIIM_SUBMENU | MIIM_TYPE);
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = buf;
+ mitem.cch = 255;
+ if(GetMenuItemCount(hMenu) == 7
+ && GetMenuItemInfo(hMenu, 5, TRUE, &mitem)){
+ if(mitem.fType == MFT_STRING
+ && !_tcscmp(mitem.dwTypeData, TEXT("&Config")))
+ hMenu = mitem.hSubMenu;
+ else
+ return;
+ }
+ else
+ return;
+
+ i = GetMenuItemCount(hMenu);
+ if(state == TRUE && i < 10){
+ mitem.fMask = MIIM_TYPE;
+ mitem.fType = MFT_SEPARATOR;
+ InsertMenuItem (hMenu, 8, TRUE, &mitem);
+
+ mitem.fMask = (MIIM_TYPE | MIIM_ID);
+ mitem.fType = MFT_STRING;
+ mitem.wID = IDM_OPT_IMAPTELEM;
+ mitem.dwTypeData = TEXT("&IMAP Telemetry");
+ mitem.cch = 15;
+ InsertMenuItem (hMenu, 9, TRUE, &mitem);
+
+ DrawMenuBar (ghTTYWnd);
+ }
+ else if(state == FALSE && i == 10){
+ DeleteMenu (hMenu, 8, MF_BYPOSITION);
+ DeleteMenu (hMenu, IDM_OPT_IMAPTELEM, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+}
+
+int
+mswin_imaptelemetry(char *msg)
+{
+ if(gMswinIMAPTelem.hwnd){
+ LPTSTR msg_lptstr;
+ msg_lptstr = utf8_to_lptstr(msg);
+ mswin_tw_puts_lptstr(&gMswinIMAPTelem, msg_lptstr);
+ fs_give((void **) &msg_lptstr);
+ return(1);
+ }
+ return(0);
+}
+
+
+/*
+ * The newmail window has a format proportional to screen width.
+ * At this point, we've figured out the sizes of the fields, now
+ * we fill that field to it's desired column size. This used to
+ * be a lot eaier when it was one char per column, but in the
+ * unicode world it's not that easy. This code does make the
+ * assumption that ASCII characters are 1 column.
+ */
+LPTSTR
+format_newmail_string(LPTSTR orig_lptstr, int format_len)
+{
+ LPTSTR new_lptstr;
+ int i, colLen;
+
+ new_lptstr = (LPTSTR)MemAlloc((format_len+1)*sizeof(TCHAR));
+
+ /*
+ * Fill up string till we reach the format_len, we can backtrack
+ * if we need elipses.
+ */
+ for(i = 0, colLen = 0;
+ i < format_len && colLen < format_len && orig_lptstr && orig_lptstr[i];
+ i++){
+ new_lptstr[i] = orig_lptstr[i];
+ colLen += wcellwidth(new_lptstr[i]);
+ }
+
+ if(colLen > format_len || (colLen == format_len && orig_lptstr[i])){
+ /*
+ * If we hit the edge of the format and there's still stuff
+ * to write, go back and add ".."
+ */
+ i--;
+ if(wcellwidth(new_lptstr[i]) > 1){
+ colLen -= wcellwidth(new_lptstr[i]);
+ }
+ else{
+ colLen -= wcellwidth(new_lptstr[i]);
+ i--;
+ colLen -= wcellwidth(new_lptstr[i]);
+ }
+ while(colLen < format_len && i < format_len){
+ new_lptstr[i++] = '.';
+ colLen++;
+ }
+ }
+ else{
+ /*
+ * If we've hit the end of the string, add spaces until
+ * we get to the correct length.
+ */
+ for(; colLen < format_len && i < format_len; i++, colLen++){
+ new_lptstr[i] = ' ';
+ }
+ }
+
+ if(i <= format_len)
+ new_lptstr[i] = '\0';
+ else
+ new_lptstr[format_len] = '\0';
+
+ return(new_lptstr);
+}
+
+/*
+ * We're passed the relevant fields, now format them according to window with
+ * and put up for display
+ */
+int
+mswin_newmailwin(int is_us, char *from_utf8, char *subject_utf8, char *folder_utf8)
+{
+ TCHAR tcbuf[256];
+ int foldlen, fromlen, subjlen;
+ LPTSTR from_lptstr = NULL, subject_lptstr = NULL, folder_lptstr = NULL;
+ LPTSTR from_format, subject_format, folder_format;
+
+ if(!gMswinNewMailWin.hwnd)
+ return 0;
+
+ if(from_utf8)
+ from_lptstr = utf8_to_lptstr(from_utf8);
+ if(subject_utf8)
+ subject_lptstr = utf8_to_lptstr(subject_utf8);
+ if(folder_utf8)
+ folder_lptstr = utf8_to_lptstr(folder_utf8);
+
+
+ foldlen = (int)(.18 * gNMW_width);
+ foldlen = foldlen > 5 ? foldlen : 5;
+ fromlen = (int)(.28 * gNMW_width);
+ subjlen = gNMW_width - 2 - foldlen - fromlen;
+
+
+ from_format = format_newmail_string(from_lptstr
+ ? from_lptstr : TEXT(""),
+ fromlen - 1);
+ subject_format = format_newmail_string(subject_lptstr
+ ? subject_lptstr : TEXT("(no subject)"),
+ subjlen - 1);
+ folder_format = format_newmail_string(folder_lptstr
+ ? folder_lptstr : TEXT("INBOX"),
+ foldlen);
+
+ _sntprintf(tcbuf, 256, TEXT("%c %s %s %s"), is_us ? '+' : ' ',
+ from_format, subject_format, folder_format);
+
+ if(from_lptstr)
+ fs_give((void **) &from_lptstr);
+ if(subject_lptstr)
+ fs_give((void **) &subject_lptstr);
+ if(folder_lptstr)
+ fs_give((void **) &folder_lptstr);
+ MemFree((void *)from_format);
+ MemFree((void *)subject_format);
+ MemFree((void *)folder_format);
+
+ mswin_tw_puts_lptstr(&gMswinNewMailWin, tcbuf);
+ return 1;
+}
+
+/*
+ * Mouse up, end selection
+ */
+
+LOCAL void
+mswin_tw_print_callback(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ LPTSTR text;
+ UINT text_len;
+ int rc;
+#define DESC_LEN 180
+ TCHAR description[DESC_LEN+1];
+
+ GetWindowText(mswin_tw->hwnd, description, DESC_LEN);
+
+ rc = mswin_print_ready((WINHAND)mswin_tw->hwnd, description);
+ if (rc != 0) {
+ if (rc != PE_USER_CANCEL) {
+ LPTSTR e;
+
+ e = utf8_to_lptstr(mswin_print_error(rc));
+ if(e){
+ _sntprintf(description, DESC_LEN+1, TEXT("Printing failed: %s"), e);
+ fs_give((void **) &e);
+ }
+
+ MessageBox(mswin_tw->hwnd, description, TEXT("Print Failed"),
+ MB_OK | MB_ICONEXCLAMATION);
+ }
+ return;
+ }
+
+ text_len = mswin_tw_gettextlength(mswin_tw);
+ text = (LPTSTR) fs_get(text_len * sizeof(TCHAR));
+ mswin_tw_gettext(mswin_tw, text, text_len);
+ mswin_print_text(text);
+ fs_give((void **)&text);
+
+ mswin_print_done();
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Character Queue
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+typedef struct {
+ BOOL fKeyControlDown;
+ UCS c; /* Bigger than TCHAR for CTRL and MENU setting */
+} CQEntry;
+
+LOCAL CQEntry CQBuffer [CHARACTER_QUEUE_LENGTH];
+LOCAL int CQHead;
+LOCAL int CQTail;
+LOCAL int CQCount;
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQInit ()
+ *
+ * Description:
+ * Initialize the Character queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CQInit (void)
+{
+ CQHead = 0;
+ CQTail = 0;
+ CQCount = 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAvailable (void)
+ *
+ * Description:
+ * Return TRUE if there are characters in the queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAvailable (void)
+{
+ return (CQCount > 0);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAdd (WORD c, DWORC keyData)
+ *
+ * Description:
+ * Add 'c' to the end of the character queue.
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAdd (UCS c, BOOL fKeyControlDown)
+{
+ if (CQCount == CHARACTER_QUEUE_LENGTH)
+ return (FALSE);
+
+ CQBuffer[CQTail].fKeyControlDown = fKeyControlDown;
+ CQBuffer[CQTail].c = c;
+ CQTail = (CQTail + 1) % CHARACTER_QUEUE_LENGTH;
+ ++CQCount;
+ return (TRUE);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAddUniq (WORD c, DWORC keyData)
+ *
+ * Description:
+ * Add 'c' to the end of the character queue, only if
+ * there is no other 'c' in the queue
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAddUniq (UCS c, BOOL fKeyControlDown)
+{
+ int i;
+ int pos;
+
+ if (CQCount == CHARACTER_QUEUE_LENGTH)
+ return (FALSE);
+
+ pos = CQHead;
+ for (i = 0; i < CQCount; ++i) {
+ if (CQBuffer[pos].c == c)
+ return (FALSE);
+ pos = (pos + 1) % CHARACTER_QUEUE_LENGTH;
+ }
+ return (CQAdd (c, fKeyControlDown));
+}
+
+
+
+
+/*---------------------------------------------------------------------------
+ * int CQGet ()
+ *
+ * Description:
+ * Return the next byte from the head of the queue. If there is
+ * no byte available, returns 0, which is indistinquishable from
+ * '\0'. So it is a good idea to call CQAvailable first.
+ *
+ * Parameters:
+ * none.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL UCS
+CQGet ()
+{
+ UCS c;
+
+ if (CQCount == 0)
+ return (0);
+
+ c = CQBuffer[CQHead].c;
+
+ if(CQBuffer[CQHead].fKeyControlDown)
+ c |= CTRL;
+
+ if(c < ' ') {
+ c += '@';
+ c |= CTRL;
+ }
+
+ CQHead = (CQHead + 1) % CHARACTER_QUEUE_LENGTH;
+ --CQCount;
+ return (c);
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Mouse Event Queue
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+
+LOCAL MEvent MQBuffer [MOUSE_QUEUE_LENGTH];
+LOCAL int MQHead;
+LOCAL int MQTail;
+LOCAL int MQCount;
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQInit ()
+ *
+ * Description:
+ * Initialize the Character queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+MQInit (void)
+{
+ MQHead = 0;
+ MQTail = 0;
+ MQCount = 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQAvailable (void)
+ *
+ * Description:
+ * Return TRUE if there are characters in the queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQAvailable (void)
+{
+ return (MQCount > 0);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQAdd ()
+ *
+ * Description:
+ * Add 'c' to the end of the character queue.
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQAdd (int mevent, int button, int nRow, int nColumn, int keys, int flags)
+{
+ int c;
+ int i = 0;
+ BOOL found = FALSE;
+
+
+ /*
+ * Find a queue insertion point.
+ */
+ if (flags & MSWIN_MF_REPLACING) {
+ /* Search for same event on queue. */
+ for ( i = MQHead, c = MQCount;
+ c > 0;
+ i = (i + 1) % MOUSE_QUEUE_LENGTH, --c) {
+ if (MQBuffer[i].event == mevent) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ if (MQCount == MOUSE_QUEUE_LENGTH)
+ return (FALSE);
+ i = MQTail;
+ MQTail = (MQTail + 1) % MOUSE_QUEUE_LENGTH;
+ ++MQCount;
+ }
+
+
+ /*
+ * Record data.
+ */
+ MQBuffer[i].event = mevent;
+ MQBuffer[i].button = button;
+ MQBuffer[i].nRow = nRow;
+ MQBuffer[i].nColumn = nColumn;
+ MQBuffer[i].keys = keys;
+ MQBuffer[i].flags = flags;
+
+ /*
+ * Keep record of last mouse position.
+ */
+ gMTEvent = MQBuffer[i];
+
+ return (TRUE);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQGet ()
+ *
+ * Description:
+ * Return the next byte from the head of the queue. If there is
+ * no byte available, returns 0, which is indistinquishable from
+ * '\0'. So it is a good idea to call MQAvailable first.
+ *
+ * Parameters:
+ * none.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQGet (MEvent * pMouse)
+{
+ if (MQCount == 0)
+ return (FALSE);
+
+
+ *pMouse = MQBuffer[MQHead];
+ MQHead = (MQHead + 1) % MOUSE_QUEUE_LENGTH;
+ --MQCount;
+ return (TRUE);
+}
+
+
+
+
+
+
+DWORD
+ExplainSystemErr()
+{
+ DWORD status;
+ void *lpMsgBuf = NULL;
+ status = GetLastError();
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ status,
+ MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+
+ if(lpMsgBuf){
+ char *msg;
+
+ msg = lptstr_to_utf8(lpMsgBuf);
+ if(msg){
+ mswin_messagebox(msg, 1);
+ fs_give((void **) &msg);
+ }
+
+ LocalFree(lpMsgBuf);
+ }
+
+ return(status);
+}
+
+
+/*
+ * Called by mswin to scroll text in window in responce to the scrollbar.
+ *
+ * Args: cmd - what type of scroll operation.
+ * scroll_pos - paramter for operation.
+ * used as position for SCROLL_TO operation.
+ *
+ * Returns: TRUE - did the scroll operation.
+ * FALSE - was not able to do the scroll operation.
+ */
+int
+pico_scroll_callback (int cmd, long scroll_pos)
+{
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ scrollupline (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ scrolldownline (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ backpage (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ forwpage (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ scrollto (0, 0);
+ break;
+ }
+
+ update ();
+ return (TRUE);
+}
+
+/*
+ * sleep the given number of seconds
+ */
+int
+sleep(int t)
+{
+ time_t out = (time_t)t + time((long *) 0);
+ while(out > time((long *) 0))
+ ;
+ return(0);
+}
+
+int
+tcsucmp(LPTSTR o, LPTSTR r)
+{
+ return(o ? (r ? _tcsicmp(o, r) : 1) : (r ? -1 : 0));
+}
+
+int
+tcsruncmp(LPTSTR o, LPTSTR r, int n)
+{
+ return(o ? (r ? _tcsnicmp(o, r, n) : 1) : (r ? -1 : 0));
+}
+
+int
+strucmp(char *o, char *r)
+{
+ return(o ? (r ? stricmp(o, r) : 1) : (r ? -1 : 0));
+}
+
+
+int
+struncmp(char *o, char *r, int n)
+{
+ return(o ? (r ? strnicmp(o, r, n) : 1) : (r ? -1 : 0));
+}
+
+
+/*
+ * Returns screen width of len characters of lpText.
+ * The width is in character cells. That is, an ascii "A" has
+ * width 1 but a CJK character probably has width 2.
+ */
+unsigned
+scrwidth(LPTSTR lpText, int len)
+{
+ UCS ubuf[1000];
+ unsigned w;
+ int i, thislen, offset;
+
+ offset = w = 0;
+
+ while(len > 0){
+ thislen = MIN(len, 1000);
+ for(i = 0; i < thislen; i++)
+ ubuf[i] = lpText[offset+i];
+
+ w += ucs4_str_width_ptr_to_ptr(&ubuf[0], &ubuf[thislen]);
+
+ offset += thislen;
+ len -= thislen;
+ }
+
+ return w;
+}
+
+
+/*
+ * Returns the index into pScreen given the row,col location
+ * on the screen, taking into account characters with widths
+ * other than 1 cell.
+ */
+long
+pscreen_offset_from_cord(int row, int col, PTTYINFO pTTYInfo)
+{
+ int offset_due_to_row, offset_due_to_col, width;
+
+ /*
+ * Each row starts at a specific offset into pScreen.
+ */
+ offset_due_to_row = row * pTTYInfo->actNColumn;
+
+ /*
+ * Start with col (all chars single width) and go from there.
+ * We need to find the offset that makes the string col wide.
+ * Hopefully we won't ever get a rectange that causes us to
+ * draw half characters, but in case we do we need to err on the
+ * side of too much width instead of not enough. It's a little
+ * tricky because we want to include following zero-width
+ * characters.
+ * fewer characters it would be <= desired width, one more would
+ * be greater than desired width, this one is >= desired width.
+ */
+
+ /* first go to some offset where width is > col */
+ offset_due_to_col = col;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ while(width <= col && offset_due_to_col < pTTYInfo->actNColumn-1){
+ offset_due_to_col++;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ }
+
+ /* Now back up until width is no longer > col */
+ while(width > col){
+ offset_due_to_col--;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ }
+
+ /* But, if we're now less than col, go forward one again. */
+ if(width < col && offset_due_to_col < pTTYInfo->actNColumn-1)
+ offset_due_to_col++;
+
+ return(offset_due_to_row + offset_due_to_col);
+}
+
+
+/*
+ * Returns:
+ * 1 if store pass prompt is set in the registry to on
+ * 0 if set to off
+ * -1 if not set to anything
+ */
+int
+mswin_store_pass_prompt(void)
+{
+ /*
+ * We shouldn't need to prompt anymore, but always return 1
+ * just in case
+ */
+ return(1);
+}
diff --git a/pico/osdep/mswin.h b/pico/osdep/mswin.h
new file mode 100644
index 00000000..238d5fd7
--- /dev/null
+++ b/pico/osdep/mswin.h
@@ -0,0 +1,404 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef MSWIN_H
+#define MSWIN_H
+
+
+#define __far
+#define _far
+#define __export
+
+/*
+ * Equivalent to a windows handle.
+ */
+typedef void *WINHAND;
+
+/*
+ * Mouse input events.
+ */
+typedef struct {
+ int event;
+ int button;
+ int nRow;
+ int nColumn;
+ int keys;
+ int flags;
+} MEvent;
+
+
+/*
+ * These define how mouse events get queued.
+ */
+#define MSWIN_MF_REPLACING 0x1000
+#define MSWIN_MF_REPEATING 0x2000
+
+
+typedef struct {
+ int ch;
+ int rval;
+ LPTSTR name;
+ LPTSTR label;
+ int id;
+} MDlgButton;
+
+
+
+/*
+ * Struct to tell pico how to build and what to return when
+ * it's asked to build a popup menu.
+ */
+typedef struct _popup {
+ enum {tIndex, tQueue, tMessage, tSubMenu,
+ tSeparator, tTail} type;
+ struct { /* menu's label */
+ char *string;
+ enum {lNormal = 0, lChecked, lDisabled} style;
+ } label;
+ union {
+ UCS val; /* Queue: inserted into input queue */
+ UCS msg; /* Message: gets posted to ghTTYWnd */
+ struct _popup *submenu; /* Submenu: array of submenu entries */
+ } data;
+ struct { /* Internal data */
+ int id;
+ } internal;
+} MPopup;
+
+
+
+/*
+ * Type of function expected by mswin_allowcopy and mswin_allowcopycut
+ * and used in EditDoCopyData
+ */
+typedef int (*getc_t)(int pos);
+
+/*
+ * Callback used to fetch text for display in alternate window. Text
+ * can be returned as either a pointer to a null terminated block of
+ * text (a string), with CRLF deliminating lines. OR as a pointer to
+ * an array of pointers to lines, each line being a null terminated
+ * string.
+ */
+typedef int (*gettext_t)(char *title, void **text, long *len, int *format);
+
+/*
+ * Type used by line up/down event handlers to move the body of the
+ * displayed text, by sort callback to get/set sort order, by
+ * header mode to get/set header state...
+ */
+typedef int (*cbarg_t)(int action, long args);
+
+
+/*
+ * Callback used for periodic callback.
+ */
+typedef void (*cbvoid_t)(void);
+typedef char *(*cbstr_t)(char *);
+
+
+#define GETTEXT_TITLELEN 128
+#define GETTEXT_TEXT 1 /* 'text' is pointer to text. */
+#define GETTEXT_LINES 2 /* 'text' is pointer to array of
+ * pointers to lines. */
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 128 /* Max size of a directory path. */
+#endif
+
+/*
+ * Scroll callback values. Used to be mapped in the MSWIN_RANGE_START..END
+ * range, but now are mapped directly to keydefs.h values. Except two of
+ * them which don't have map values in keydefs.h so they're still in the
+ * MSWIN_RANGE_START..END 0x7000 range.
+ */
+#define MSWIN_KEY_SCROLLUPPAGE 0x7000
+#define MSWIN_KEY_SCROLLDOWNPAGE 0x7001
+#define MSWIN_KEY_SCROLLUPLINE KEY_SCRLUPL
+#define MSWIN_KEY_SCROLLDOWNLINE KEY_SCRLDNL
+#define MSWIN_KEY_SCROLLTO KEY_SCRLTO
+
+#define MSWIN_PASTE_DISABLE 0
+#define MSWIN_PASTE_FULL 1
+#define MSWIN_PASTE_LINE 2
+
+
+#define MSWIN_CURSOR_ARROW 0
+#define MSWIN_CURSOR_BUSY 1
+#define MSWIN_CURSOR_IBEAM 2
+#define MSWIN_CURSOR_HAND 3
+
+/*
+ * Flags for mswin_displaytext
+ */
+#define MSWIN_DT_NODELETE 0x0001 /* Don't delete text when
+ * window closes. */
+#define MSWIN_DT_USEALTWINDOW 0x0002 /* Put text in alt window if already
+ * open. Open if not. */
+#define MSWIN_DT_FILLFROMFILE 0x0004 /* pText_utf8 is a filename. */
+
+/*
+ * functions from mswin.c
+ */
+
+
+WINHAND mswin_gethinstance();
+WINHAND mswin_gethwnd ();
+void mswin_killsplash();
+void mswin_settitle(char *);
+int mswin_getmouseevent (MEvent * pMouse);
+void mswin_mousetrackcallback (cbarg_t cbfunc);
+int mswin_popup(MPopup *members);
+void mswin_keymenu_popup ();
+void mswin_registericon(int row, int id, char *file);
+void mswin_destroyicons();
+void mswin_finishicons();
+void mswin_show_icon (int x, int y, char *file);
+void mswin_setdebug (int debug, FILE *debugfile);
+void mswin_setnewmailwidth (int width);
+int mswin_setdndcallback (int (*cb)());
+int mswin_cleardndcallback (void);
+int mswin_setresizecallback (int (*cb)());
+int mswin_clearresizecallback (int (*cb)());
+void mswin_setdebugoncallback (cbvoid_t cbfunc);
+void mswin_setdebugoffcallback (cbvoid_t cbfunc);
+int mswin_setconfigcallback (cbvoid_t cffunc);
+int mswin_sethelpcallback (cbstr_t cbfunc);
+int mswin_setgenhelpcallback (cbstr_t cbfunc);
+void mswin_setclosetext (char *pCloseText);
+int mswin_setwindow (char *fontName, char *fontSize,
+ char *fontStyle, char *windowPosition,
+ char *cursorStyle, char *fontCharSet);
+int mswin_showwindow();
+int mswin_getwindow (char *fontName, size_t nfontName,
+ char *fontSize, size_t nfontSize,
+ char *fontStyle, size_t nfontStyle,
+ char *windowPosition, size_t nwindowPosition,
+ char *foreColor, size_t nforeColor,
+ char *backColor, size_t nbackColor,
+ char *cursorStyle, size_t ncursorStyle,
+ char *fontCharSet, size_t nfontCharSet);
+void mswin_noscrollupdate (int flag);
+void mswin_setscrollrange (long page, long max);
+void mswin_setscrollpos (long pos);
+long mswin_getscrollpos (void);
+long mswin_getscrollto (void);
+void mswin_setscrollcallback (cbarg_t cbfunc);
+void mswin_setsortcallback (cbarg_t cbfunc);
+void mswin_setflagcallback (cbarg_t cbfunc);
+void mswin_sethdrmodecallback (cbarg_t cbfunc);
+void mswin_setselectedcallback (cbarg_t cbfunc);
+void mswin_setzoomodecallback (cbarg_t cbfunc);
+void mswin_setfkeymodecallback (cbarg_t cbfunc);
+void mswin_setprintfont (char *fontName, char *fontSize,
+ char *fontStyle, char *fontCharSet);
+void mswin_getprintfont (char *, size_t, char *, size_t,
+ char *, size_t, char *, size_t);
+int mswin_yield (void);
+int mswin_charavail (void);
+UCS mswin_getc_fast (void);
+void mswin_flush_input (void);
+int mswin_showcursor (int show);
+int mswin_showcaret (int show);
+void mswin_trayicon (int show);
+int mswin_move (int row, int column);
+int mswin_getpos (int *row, int *column);
+int mswin_getscreensize (int *row, int *column);
+void mswin_minimize (void);
+int mswin_putblock (char *utf8_str, int strLen);
+int mswin_puts (char *utf8_str);
+int mswin_puts_n (char *utf8_str, int n);
+int mswin_putc (UCS c);
+int mswin_outc (char c);
+int mswin_rev (int state);
+int mswin_getrevstate (void);
+int mswin_bold (int state);
+int mswin_uline (int state);
+int mswin_eeol (void);
+int mswin_eeop (void);
+int mswin_beep (void);
+void mswin_pause (int);
+int mswin_flush (void);
+void mswin_setcursor (int);
+void mswin_messagebox (char *msg, int);
+void mswin_allowpaste (int);
+void mswin_allowcopycut (getc_t);
+void mswin_allowcopy (getc_t);
+void mswin_addclipboard (char *s);
+void mswin_allowmousetrack (int);
+int mswin_newmailicon (void);
+void mswin_newmailtext (char *t);
+void mswin_newmaildone (void);
+void mswin_mclosedtext (char *t);
+void mswin_menuitemclear (void);
+void mswin_menuitemadd (UCS key, char *label, int menuitem,
+ int flags);
+int mswin_setwindowmenu (int menu);
+int mswin_print_ready (WINHAND hWnd, LPTSTR docDesc);
+int mswin_print_done (void);
+char * mswin_print_error (int errorcode);
+int mswin_print_char (TCHAR c);
+int mswin_print_char_utf8 (int c);
+int mswin_print_text (LPTSTR text);
+int mswin_print_text_utf8 (char *text);
+int mswin_savefile (char *dir, int dirlen, char *fName, int nMaxFName);
+int mswin_openfile (char *dir, int nMaxDName, char *fName, int nMaxFName, char *extlist);
+int mswin_multopenfile (char *dir, int nMaxDName, char *fName, int nMaxFName, char *extlist);
+char *mswin_rgbchoice(char *pOldRGB);
+void mswin_killbuftoclip (getc_t copyfunc);
+void pico_popup();
+void mswin_paste_popup();
+int mswin_fflush (FILE *f);
+void mswin_setperiodiccallback (cbvoid_t periodiccb, long period);
+WINHAND mswin_inst2task (WINHAND hInst);
+int mswin_ontask_del (WINHAND hTask, char *path);
+int mswin_exec_and_wait (char *whatsit, char *command,
+ char *infile, char *outfile,
+ int *exit_val,
+ unsigned mseaw_flags);
+int mswin_shell_exec (char *command, WINHAND *childproc);
+void mswin_exec_err_msg (char *what, int status, char *buf,
+ size_t buflen);
+int mswin_onexit_del (char *path);
+int mswin_set_quit_confirm (int);
+void mswin_showhelpmsg (WINHAND hWnd, char **helplines);
+
+typedef struct MSWIN_TEXTWINDOW MSWIN_TEXTWINDOW;
+MSWIN_TEXTWINDOW *mswin_displaytext (char *title, char *pText, size_t textLen,
+ char **pLines, MSWIN_TEXTWINDOW *mswin_tw,
+ int flags);
+int mswin_imaptelemetry(char *msg);
+void mswin_enableimaptelemetry(int state);
+int mswin_newmailwin(int is_us, char *from,
+ char *subject, char *folder);
+int mswin_newmailwinon(void);
+
+char *mswin_reg_default_browser(char *url);
+int mswin_reg(int op, int tree, char *data, size_t size);
+int mswin_is_def_client(int type);
+int mswin_set_def_client(int type);
+char **mswin_reg_dump(void);
+int mswin_majorver();
+int mswin_minorver();
+char *mswin_compilation_date();
+char *mswin_compilation_remarks();
+char *mswin_specific_winver();
+
+
+int mswin_usedialog (void);
+int mswin_dialog(UCS *prompt, UCS *string, int field_len,
+ int append_current, int passwd,
+ MDlgButton *button_list, char **help, unsigned flags);
+int mswin_select(char *utf8prompt, MDlgButton *button_list,
+ int dflt, int on_ctrl_C, char **help, unsigned flags);
+int mswin_yesno(UCS *);
+int mswin_yesno_utf8(char *);
+
+BOOL MSWRShellCanOpen(LPTSTR key, char *cmdbuf, int clen, int allow_noreg);
+BOOL MSWRPeek(HKEY hRootKey, LPTSTR subkey, LPTSTR valstr,
+ LPTSTR data, DWORD *dlen);
+int mswin_store_pass_prompt(void);
+void mswin_set_erasecreds_callback(cbvoid_t);
+void mswin_setviewinwindcallback (cbvoid_t);
+int mswin_setgenhelptextcallback(cbstr_t);
+int mswin_caninput(void);
+void mswin_beginupdate(void);
+void mswin_endupdate(void);
+int mswin_sethelptextcallback(cbstr_t);
+int strucmp(char *, char *);
+int struncmp(char *, char *, int);
+
+#ifdef MSC_MALLOC
+/*
+ * These definitions will disable the SEGHEAP allocation routines, in
+ * favor of the compliler libraries usual allocators. This is useful
+ * when external debugging tools and the SEGHEAP debugging routines step
+ * on each other...
+ */
+#define MemAlloc(X) malloc(X)
+#define MemFree(X) free(X)
+#define MemRealloc(X,Y) realloc(X,Y)
+#define MemFreeAll()
+#define MemDebug(X,Y)
+#else
+/*
+ * Memory management stuff, from msmem.c
+ */
+typedef unsigned long MemSize;
+typedef void __far * MemPtr;
+
+#define MemAlloc(s) _MemAlloc (s, __FILE__, __LINE__)
+#define malloc(s) _MemAlloc (s, __FILE__, __LINE__)
+#define MemFree(b) _MemFree (b, __FILE__, __LINE__)
+#define free(b) _MemFree (b, __FILE__, __LINE__)
+#define MemRealloc(b,s) _MemRealloc (b, s, __FILE__, __LINE__)
+#define realloc(b,s) _MemRealloc (b, s, __FILE__, __LINE__)
+#define MEM_BLOCK_SIZE_MAX 0xff00
+
+void MemDebug (int debug, FILE *debugFile);
+void __far * _MemAlloc (MemSize size, char __far * file, int line);
+int _MemFree (void __far *block, char __far *file, int line);
+void __far * _MemRealloc (void __far *block, MemSize size,
+ char __far * file, int line);
+MemSize MemBlkSize (MemPtr block);
+void MemFreeAll (void);
+void MemFailSoon (MemSize);
+#endif
+
+/* functions from win moved to mswin.c */
+int pico_scroll_callback (int, long);
+#undef sleep
+int sleep (int);
+
+/*
+ * Signals that are not defined by MS C
+ */
+#define SIGHUP 1 /* Terminal hangup. */
+#define SIGINT 2 /* Ctrl-C sequence */
+#define SIGILL 4 /* illegal instruction - invalid function image */
+#define SIGSEGV 11 /* segment violation */
+#define SIGALRM 14 /* alarm clock */
+#define SIGTERM 15 /* Software termination signal from kill */
+#define SIGABRT 22 /* abnormal termination triggered by abort call */
+#define SIGWINCH 28 /* Change in window size. */
+
+
+#define fflush mswin_fflush
+
+#define alarm mswin_alarm
+
+/*
+ * Registry setting constants
+ */
+#define MSWR_PINE_RC 1
+#define MSWR_PINE_DIR 2
+#define MSWR_PINE_EXE 3
+#define MSWR_PINE_AUX 4
+#define MSWR_PINE_POS 5
+#define MSWR_PINE_CONF 6
+
+#define MSWR_SDC_MAIL 1
+#define MSWR_SDC_NEWS 2
+#define MSWR_SDC_NNTP 3
+#define MSWR_SDC_IMAP 4
+
+#define MSWR_NULL 0x00
+#define MSWR_OP_SET 0x01
+#define MSWR_OP_FORCE 0x02
+#define MSWR_OP_GET 0x04
+#define MSWR_OP_BLAST 0x08
+
+#endif /* MSWIN_H */
diff --git a/pico/osdep/mswin.ico b/pico/osdep/mswin.ico
new file mode 100644
index 00000000..d6b7af76
--- /dev/null
+++ b/pico/osdep/mswin.ico
Binary files differ
diff --git a/pico/osdep/mswin.rc b/pico/osdep/mswin.rc
new file mode 100644
index 00000000..5462b554
--- /dev/null
+++ b/pico/osdep/mswin.rc
@@ -0,0 +1,236 @@
+//Microsoft App Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+ALPINEICON ICON DISCARDABLE "PICO.ICO"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+PICOHAND CURSOR "MSWINHND.CUR"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+ALPINEMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Save File", IDM_MI_SAVEFILE
+ MENUITEM "&Read File", IDM_MI_READFILE
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_MI_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Cu&t\tCtrl-Shift-X", IDM_EDIT_CUT
+ MENUITEM "&Copy\tCtrl-Shift-C", IDM_EDIT_COPY
+ MENUITEM "Copy &Append\tCtrl-Alt-C", IDM_EDIT_COPY_APPEND
+ MENUITEM "&Paste\tCtrl-Shift-V", IDM_EDIT_PASTE
+ MENUITEM "Cance&l Paste!\tCtrl-Alt-V", IDM_EDIT_CANCEL_PASTE
+ MENUITEM SEPARATOR
+ MENUITEM "Select All", IDM_EDIT_SEL_ALL
+ MENUITEM SEPARATOR
+ MENUITEM "&Find\tCtrl-Shift-F", IDM_MI_WHEREIS
+ MENUITEM SEPARATOR
+ MENUITEM "&Spell", IDM_MI_SPELLCHK
+ END
+ POPUP "&Compose"
+ BEGIN
+ MENUITEM "Show Current &Position", IDM_MI_CURPOSITION
+ MENUITEM "&Where Is", IDM_MI_WHEREIS
+ MENUITEM "&Justify Paragraph", IDM_MI_JUSTIFY
+ END
+ POPUP "C&onfig"
+ BEGIN
+ MENUITEM "Set &Font", IDM_OPT_SETFONT
+ MENUITEM SEPARATOR
+ MENUITEM "&ToolBar", IDM_OPT_TOOLBAR
+ MENUITEM "Tool&bar on Top", IDM_OPT_TOOLBARPOS
+ POPUP "&Cursor"
+ BEGIN
+ MENUITEM "Block", IDM_OPT_CARETBLOCK
+ MENUITEM "Small Block", IDM_OPT_CARETSMALLBLOCK
+ MENUITEM "Underline", IDM_OPT_CARETHBAR
+ MENUITEM "Vertical Bar", IDM_OPT_CARETVBAR
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Use &Dialog Boxes", IDM_OPT_USEDIALOGS
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "Screen Help", IDM_MI_SCREENHELP
+ MENUITEM "About", IDM_ABOUT
+ END
+END
+
+TEXTWINMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Close", IDM_FILE_CLOSE
+ MENUITEM SEPARATOR
+ MENUITEM "&Print", IDM_FILE_PRINT
+ END
+END
+
+
+#include "mswin_spell.dlg"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+ABOUTDLGBOX DIALOG DISCARDABLE 10, 25, 220, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Pico"
+FONT 8, "Helv"
+BEGIN
+ ICON ALPINEICON,IDD_ABOUTICON,10,5,18,20
+ CTEXT "Pico for Windows%S\nVersion %d.%02d%S\nBuild (%S)",
+ IDD_VERSION,46,8,120,26,SS_NOPREFIX | NOT WS_GROUP
+ LTEXT "",IDD_BYLINE,45,40,172,56,SS_NOPREFIX | NOT WS_GROUP
+ DEFPUSHBUTTON "OK",IDD_OK,177,8,40,14
+ PUSHBUTTON "",IDC_BUTTON2,10,5,30,30,BS_ICON | NOT
+ WS_VISIBLE | WS_DISABLED,WS_EX_TRANSPARENT
+END
+
+IDD_TOOLBAR DIALOG DISCARDABLE 0, 0, 302, 12
+STYLE WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Quit",IDM_MI_EXIT,1,0,25,11
+ PUSHBUTTON "Save",IDM_MI_SAVEFILE,26,0,26,11
+ PUSHBUTTON "Find",IDM_MI_WHEREIS,52,0,22,11
+ PUSHBUTTON "Spell",IDM_MI_SPELLCHK,74,0,26,11
+END
+
+#ifdef APSTUDIO_INVOKED
+//////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+0X00020000L TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+/////////////////////////////////////////////////////////////////////////////////////
+#endif // APSTUDIO_INVOKED
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_BYLINE "Copyright 2006-2009 University of Washington"
+ IDS_APPNAME "Pico"
+ IDS_APPIDENT "pico"
+END
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,05,0,0
+ PRODUCTVERSION 5,05,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "see http://www.washington.edu/pine\0"
+ VALUE "CompanyName", "University of Washington\0"
+ VALUE "FileDescription", "Pico\0"
+ VALUE "FileVersion", "5.05\0"
+ VALUE "InternalName", "pico\0"
+ VALUE "LegalCopyright", "Copyright 2006-2009\0"
+ VALUE "OriginalFilename", "pico.exe\0"
+ VALUE "ProductName", " pico\0"
+ VALUE "ProductVersion", "5.05\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+#ifndef APSTUDIO_INVOKED
+////////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/pico/osdep/mswin_aspell.c b/pico/osdep/mswin_aspell.c
new file mode 100644
index 00000000..26e998da
--- /dev/null
+++ b/pico/osdep/mswin_aspell.c
@@ -0,0 +1,427 @@
+/*
+ * ========================================================================
+ * FILE: MSWIN_ASPELL.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include "mswin_aspell.h"
+
+/*
+ * Aspell typedefs
+ */
+typedef struct AspellConfig AspellConfig;
+typedef struct AspellSpeller AspellSpeller;
+typedef struct AspellCanHaveError AspellCanHaveError;
+typedef struct AspellStringEnumeration AspellStringEnumeration;
+typedef struct AspellWordList AspellWordList;
+typedef struct AspellError AspellError;
+
+/*
+ * Aspell function prototypes
+ */
+typedef AspellCanHaveError * (__cdecl NEW_ASPELL_SPELLER)(AspellConfig * config);
+typedef AspellConfig * (__cdecl NEW_ASPELL_CONFIG)();
+typedef AspellSpeller * (__cdecl TO_ASPELL_SPELLER)(AspellCanHaveError * obj);
+typedef int (__cdecl ASPELL_CONFIG_REPLACE)(AspellConfig * ths, const char * key, const char * value);
+typedef void (__cdecl DELETE_ASPELL_CONFIG)(AspellConfig * ths);
+typedef const char * (__cdecl ASPELL_CONFIG_RETRIEVE)(AspellConfig * ths, const char * key);
+typedef int (__cdecl ASPELL_SPELLER_ADD_TO_PERSONAL)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_ADD_TO_SESSION)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_CHECK)(AspellSpeller * ths, const char * word, int word_size);
+typedef const AspellWordList * (__cdecl ASPELL_SPELLER_SUGGEST)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_SAVE_ALL_WORD_LISTS)(AspellSpeller * ths);
+typedef int (__cdecl ASPELL_SPELLER_STORE_REPLACEMENT)(AspellSpeller * ths, const char * mis, int mis_size, const char * cor, int cor_size);
+typedef const char * (__cdecl ASPELL_STRING_ENUMERATION_NEXT)(AspellStringEnumeration * ths);
+typedef AspellStringEnumeration *(__cdecl ASPELL_WORD_LIST_ELEMENTS)(const AspellWordList * ths);
+typedef void (__cdecl DELETE_ASPELL_SPELLER)(AspellSpeller * ths);
+typedef void (__cdecl DELETE_ASPELL_STRING_ENUMERATION)(AspellStringEnumeration * ths);
+typedef void (__cdecl DELETE_ASPELL_CAN_HAVE_ERROR)(AspellCanHaveError * ths);
+typedef const char * (__cdecl ASPELL_ERROR_MESSAGE)(const AspellCanHaveError * ths);
+typedef unsigned int (__cdecl ASPELL_ERROR_NUMBER)(const AspellCanHaveError * ths);
+typedef const AspellError * (__cdecl ASPELL_SPELLER_ERROR)(const AspellSpeller * ths);
+typedef const char * (__cdecl ASPELL_SPELLER_ERROR_MESSAGE)(const struct AspellSpeller * ths);
+typedef AspellConfig * (__cdecl ASPELL_SPELLER_CONFIG)(AspellSpeller * ths);
+
+/*
+ * MsWin speller information structure
+ */
+typedef struct ASPELLINFO
+{
+ AspellSpeller *speller;
+ AspellStringEnumeration *suggestion_elements;
+ const char *error_message;
+
+ AspellCanHaveError *err;
+
+ HMODULE mod_aspell;
+
+ NEW_ASPELL_SPELLER *new_aspell_speller;
+ NEW_ASPELL_CONFIG *new_aspell_config;
+ TO_ASPELL_SPELLER *to_aspell_speller;
+ ASPELL_CONFIG_REPLACE *aspell_config_replace;
+ DELETE_ASPELL_CONFIG *delete_aspell_config;
+ ASPELL_CONFIG_RETRIEVE *aspell_config_retrieve;
+ ASPELL_SPELLER_ADD_TO_PERSONAL *aspell_speller_add_to_personal;
+ ASPELL_SPELLER_ADD_TO_SESSION *aspell_speller_add_to_session;
+ ASPELL_SPELLER_CHECK *aspell_speller_check;
+ ASPELL_SPELLER_SUGGEST *aspell_speller_suggest;
+ ASPELL_SPELLER_SAVE_ALL_WORD_LISTS *aspell_speller_save_all_word_lists;
+ ASPELL_SPELLER_STORE_REPLACEMENT *aspell_speller_store_replacement;
+ ASPELL_STRING_ENUMERATION_NEXT *aspell_string_enumeration_next;
+ ASPELL_WORD_LIST_ELEMENTS *aspell_word_list_elements;
+ DELETE_ASPELL_SPELLER *delete_aspell_speller;
+ DELETE_ASPELL_STRING_ENUMERATION *delete_aspell_string_enumeration;
+ DELETE_ASPELL_CAN_HAVE_ERROR *delete_aspell_can_have_error;
+ ASPELL_ERROR_MESSAGE *aspell_error_message;
+ ASPELL_ERROR_NUMBER *aspell_error_number;
+ ASPELL_SPELLER_ERROR *aspell_speller_error;
+ ASPELL_SPELLER_ERROR_MESSAGE *aspell_speller_error_message;
+ ASPELL_SPELLER_CONFIG *aspell_speller_config;
+} ASPELLINFO;
+
+/*
+ * Find, aspell-15.dll, load it, and attempt to initialize our func pointers.
+ * 1:success, 0:failed
+ */
+static int
+speller_load_aspell_library(ASPELLINFO *aspellinfo)
+{
+ HKEY hKey;
+ int success = 1;
+ HMODULE mod_aspell = NULL;
+ TCHAR aspell_fullname[MAX_PATH + 1];
+ static const TCHAR aspell_name[] = TEXT("aspell-15.dll");
+
+ aspell_fullname[0] = '\0';
+
+ // Try to open the Aspell Registry key.
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Aspell"),
+ 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ DWORD dtype;
+ TCHAR aspell_path[MAX_PATH + 1];
+ DWORD aspell_path_len = sizeof(aspell_path);
+
+ // Query the path.
+ if(RegQueryValueEx(hKey, TEXT("Path"), NULL, &dtype,
+ (LPBYTE)aspell_path, &aspell_path_len) == ERROR_SUCCESS)
+ {
+ _sntprintf(aspell_fullname, ARRAYSIZE(aspell_fullname),
+ TEXT("%s\\%s"), aspell_path, aspell_name);
+ aspell_fullname[ARRAYSIZE(aspell_fullname) - 1] = 0;
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ // If the registry thing didn't work, then just try doing a Loadlibrary
+ // using the standard Windows LoadLibrary search gunk.
+ if(!aspell_fullname[0])
+ {
+ _tcsncpy(aspell_fullname, aspell_name, ARRAYSIZE(aspell_fullname));
+ aspell_fullname[ARRAYSIZE(aspell_fullname) - 1] = 0;
+ }
+
+ // Load the library.
+ mod_aspell = LoadLibrary(aspell_fullname);
+ if(!mod_aspell)
+ {
+ // Failed.
+ success = 0;
+ }
+ else
+ {
+ // Found the library - now try to initialize our function pointers.
+ //
+#define GET_ASPELL_PROC_ADDR(_functype, _func) \
+ aspellinfo->_func = (_functype *)GetProcAddress(mod_aspell, #_func); \
+ if(!aspellinfo->_func) \
+ success = 0
+
+ GET_ASPELL_PROC_ADDR(NEW_ASPELL_SPELLER, new_aspell_speller);
+ GET_ASPELL_PROC_ADDR(NEW_ASPELL_CONFIG, new_aspell_config);
+ GET_ASPELL_PROC_ADDR(TO_ASPELL_SPELLER, to_aspell_speller);
+ GET_ASPELL_PROC_ADDR(ASPELL_CONFIG_REPLACE, aspell_config_replace);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_CONFIG, delete_aspell_config);
+ GET_ASPELL_PROC_ADDR(ASPELL_CONFIG_RETRIEVE, aspell_config_retrieve);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ADD_TO_PERSONAL, aspell_speller_add_to_personal);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ADD_TO_SESSION, aspell_speller_add_to_session);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_CHECK, aspell_speller_check);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_SUGGEST, aspell_speller_suggest);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_SAVE_ALL_WORD_LISTS, aspell_speller_save_all_word_lists);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_STORE_REPLACEMENT, aspell_speller_store_replacement);
+ GET_ASPELL_PROC_ADDR(ASPELL_STRING_ENUMERATION_NEXT, aspell_string_enumeration_next);
+ GET_ASPELL_PROC_ADDR(ASPELL_WORD_LIST_ELEMENTS, aspell_word_list_elements);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_SPELLER, delete_aspell_speller);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_STRING_ENUMERATION, delete_aspell_string_enumeration);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_CAN_HAVE_ERROR, delete_aspell_can_have_error);
+ GET_ASPELL_PROC_ADDR(ASPELL_ERROR_MESSAGE, aspell_error_message);
+ GET_ASPELL_PROC_ADDR(ASPELL_ERROR_NUMBER, aspell_error_number);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ERROR, aspell_speller_error);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ERROR_MESSAGE, aspell_speller_error_message);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_CONFIG, aspell_speller_config);
+
+#undef GET_ASPELL_PROC_ADDR
+
+ if(!success)
+ {
+ FreeLibrary(mod_aspell);
+ mod_aspell = NULL;
+ }
+ }
+
+ aspellinfo->mod_aspell = mod_aspell;
+ return success;
+}
+
+/*
+ * Find, load, and initialize the aspell library.
+ * NULL on failure, otherwise an ASPELLINFO pointer.
+ */
+ASPELLINFO *
+speller_init(char *lang)
+{
+ ASPELLINFO *aspellinfo;
+
+ aspellinfo = (ASPELLINFO *)malloc(sizeof(ASPELLINFO));
+ if(aspellinfo)
+ {
+ AspellConfig *config;
+ AspellSpeller *speller;
+
+ memset(aspellinfo, 0, sizeof(ASPELLINFO));
+
+ // Load the aspell library and set up our function pointers, etc.
+ if(!speller_load_aspell_library(aspellinfo))
+ {
+ speller_close(aspellinfo);
+ return NULL;
+ }
+
+ config = aspellinfo->new_aspell_config();
+
+ if(lang)
+ {
+ aspellinfo->aspell_config_replace(config, "lang", lang);
+ }
+
+ aspellinfo->aspell_config_replace(config, "encoding", "utf-8");
+
+ aspellinfo->err = aspellinfo->new_aspell_speller(config);
+ aspellinfo->delete_aspell_config(config);
+
+ if(aspellinfo->aspell_error_number(aspellinfo->err))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_error_message(aspellinfo->err);
+ }
+ else
+ {
+ aspellinfo->speller = aspellinfo->to_aspell_speller(aspellinfo->err);
+ aspellinfo->err = NULL;
+
+ /*
+ TODO: Log the current speller config somewhere?
+
+ config = aspell_speller_config(speller);
+
+ fputs("Using: ", stdout);
+ fputs(aspell_config_retrieve(config, "lang"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "jargon"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "size"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "module"), stdout);
+ fputs("\n\n", stdout);
+ */
+ }
+ }
+
+ return aspellinfo;
+}
+
+/*
+ * Get the last aspell error message.
+ */
+const char *
+speller_get_error_message(ASPELLINFO *aspellinfo)
+{
+ return aspellinfo ? aspellinfo->error_message : NULL;
+}
+
+/*
+ * Close up shop.
+ */
+void
+speller_close(ASPELLINFO *aspellinfo)
+{
+ if(aspellinfo)
+ {
+ // Make sure someone hasn't missed a speller_suggestion_close
+ assert(!aspellinfo->suggestion_elements);
+
+ if(aspellinfo->err)
+ {
+ aspellinfo->delete_aspell_can_have_error(aspellinfo->err);
+ aspellinfo->err = NULL;
+ }
+
+ if(aspellinfo->speller)
+ {
+ aspellinfo->delete_aspell_speller(aspellinfo->speller);
+ aspellinfo->speller = NULL;
+ }
+
+ if(aspellinfo->mod_aspell)
+ {
+ FreeLibrary(aspellinfo->mod_aspell);
+ aspellinfo->mod_aspell = NULL;
+ }
+
+ free(aspellinfo);
+ }
+}
+
+/*
+ * Check the dictionary for a word.
+ * 1:found, 0:not found, -1:error
+ */
+int
+speller_check_word(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ int have = 1;
+
+ if(!isdigit(*word))
+ {
+ have = aspellinfo->aspell_speller_check(aspellinfo->speller, word, word_size);
+ if(have == -1)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ }
+
+ }
+
+ return have;
+}
+
+/*
+ * Add word to dictionary.
+ * 1:success, 0:failed
+ */
+int
+speller_add_to_dictionary(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ aspellinfo->aspell_speller_add_to_personal(aspellinfo->speller,
+ word, word_size);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ aspellinfo->aspell_speller_save_all_word_lists(aspellinfo->speller);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Add this word to the current spelling session.
+ * 1:success, 0:failed
+ */
+int
+speller_ignore_all(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ aspellinfo->aspell_speller_add_to_session(aspellinfo->speller, word, word_size);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Replace a word.
+ * 1:success, 0:failed
+ */
+int
+speller_replace_word(ASPELLINFO *aspellinfo,
+ const char * mis, int mis_size, const char * cor, int cor_size)
+{
+ int ret;
+
+ ret = aspellinfo->aspell_speller_store_replacement(aspellinfo->speller,
+ mis, mis_size, cor, cor_size);
+ if(ret == -1)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Init the suggestions element array for a specific word.
+ * 1:success, 0:failed
+ */
+int
+speller_suggestion_init(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ const AspellWordList *suggestions;
+
+ // Make sure someone hasn't missed a speller_suggestion_close
+ assert(!aspellinfo->suggestion_elements);
+
+ suggestions = aspellinfo->aspell_speller_suggest(aspellinfo->speller,
+ word, word_size);
+ if(!suggestions)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ aspellinfo->suggestion_elements =
+ aspellinfo->aspell_word_list_elements(suggestions);
+ return 1;
+}
+
+/*
+ * Find next suggestion. Returns NULL when no more are left.
+ */
+const char *
+speller_suggestion_getnext(ASPELLINFO *aspellinfo)
+{
+ return aspellinfo->aspell_string_enumeration_next(
+ aspellinfo->suggestion_elements);
+}
+
+/*
+ * Close the suggestion element array.
+ */
+void
+speller_suggestion_close(ASPELLINFO *aspellinfo)
+{
+ aspellinfo->delete_aspell_string_enumeration(aspellinfo->suggestion_elements);
+ aspellinfo->suggestion_elements = NULL;
+}
diff --git a/pico/osdep/mswin_aspell.h b/pico/osdep/mswin_aspell.h
new file mode 100644
index 00000000..623890ad
--- /dev/null
+++ b/pico/osdep/mswin_aspell.h
@@ -0,0 +1,30 @@
+/*
+ * ========================================================================
+ * FILE: MSWIN_ASPELL.H
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+typedef struct ASPELLINFO ASPELLINFO;
+
+ASPELLINFO *speller_init(char *lang);
+void speller_close(ASPELLINFO *aspellinfo);
+
+const char *speller_get_error_message(ASPELLINFO *aspellinfo);
+
+int speller_check_word(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_add_to_dictionary(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_ignore_all(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_replace_word(ASPELLINFO *aspellinfo, const char * mis, int mis_size,
+ const char * cor, int cor_size);
+
+int speller_suggestion_init(ASPELLINFO *aspellinfo, const char *word, int word_size);
+const char *speller_suggestion_getnext(ASPELLINFO *aspellinfo);
+void speller_suggestion_close(ASPELLINFO *aspellinfo);
+
diff --git a/pico/osdep/mswin_spell.DLG b/pico/osdep/mswin_spell.DLG
new file mode 100644
index 00000000..53e4aa5a
--- /dev/null
+++ b/pico/osdep/mswin_spell.DLG
@@ -0,0 +1,18 @@
+#include "mswin_spell.h"
+
+DLG_SPELL DIALOG 16, 34, 229, 92
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog Title"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ COMBOBOX ID_COMBO_SUGGESTIONS, 4, 5, 79, 80, CBS_SIMPLE | WS_VSCROLL |
+ WS_TABSTOP | CBS_AUTOHSCROLL
+
+ DEFPUSHBUTTON "&Change", ID_BUTTON_CHANGE, 88, 8, 62, 14, WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "Change &All", ID_BUTTON_CHANGEALL, 88, 27, 62, 14, WS_TABSTOP
+ PUSHBUTTON "&Ignore Once", ID_BUTTON_IGNOREONCE, 88, 46, 62, 14, WS_TABSTOP
+ PUSHBUTTON "I&gnore All", ID_BUTTON_IGNOREALL, 88, 65, 62, 14, WS_TABSTOP
+ PUSHBUTTON "Add to &Dictionary", ID_BUTTON_ADD_TO_DICT, 156, 8, 68, 14, WS_TABSTOP
+ PUSHBUTTON "Ca&ncel", ID_BUTTON_CANCEL, 156, 27, 68, 14, WS_TABSTOP
+END
diff --git a/pico/osdep/mswin_spell.c b/pico/osdep/mswin_spell.c
new file mode 100644
index 00000000..1379beb6
--- /dev/null
+++ b/pico/osdep/mswin_spell.c
@@ -0,0 +1,547 @@
+/*
+ * ========================================================================
+ * MSWIN_SPELL.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../headers.h"
+
+#include "mswin_aspell.h"
+#include "mswin_spell.h"
+
+#include <windowsx.h>
+
+typedef struct WORD_INFO
+{
+ ASPELLINFO *aspellinfo; // Aspell information
+
+ const char *utf8_err_message; // Error message, if any
+
+ char *word_utf8; // utf-8
+ LPTSTR word_lptstr;
+
+ int word_doto; // UCS
+ LINE *word_dotp; //
+ int word_size; //
+} WORD_INFO;
+
+extern HINSTANCE ghInstance;
+extern HWND ghTTYWnd;
+
+/*
+ * Function prototypes
+ */
+static int get_next_bad_word(WORD_INFO *word_info);
+static void free_word_info_words(WORD_INFO *word_info);
+static INT_PTR CALLBACK spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+/*
+ * spell() - check for potentially missspelled words and offer them for
+ * correction. Microsoft Windows specific version.
+ */
+int
+spell(int f, int n)
+{
+ int w_marko_bak;
+ LINE *w_markp_bak;
+ WORD_INFO word_info;
+ ASPELLINFO *aspellinfo;
+
+ emlwrite(_("Checking spelling..."), NULL); /* greetings! */
+
+ memset(&word_info, 0, sizeof(WORD_INFO));
+
+ aspellinfo = speller_init(NULL);
+ if(!aspellinfo ||
+ (word_info.utf8_err_message = speller_get_error_message(aspellinfo)))
+ {
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL); /* sad greetings! */
+ else
+ emlwrite(_("Spelling: initializing Aspell failed"), NULL);
+
+ speller_close(aspellinfo);
+ return -1;
+ }
+
+ // Back up current mark.
+ w_marko_bak = curwp->w_marko;
+ w_markp_bak = curwp->w_markp;
+
+ setimark(0, 1);
+ gotobob(0, 1);
+
+ word_info.aspellinfo = aspellinfo;
+
+ if(get_next_bad_word(&word_info))
+ {
+ DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_SPELL), ghTTYWnd,
+ spell_dlg_proc, (LPARAM)&word_info);
+ }
+
+ // Restore original mark if possible.
+ if(curwp->w_markp)
+ {
+ // Clear any set marks.
+ setmark(0, 1);
+
+ if(w_markp_bak && llength(w_markp_bak))
+ {
+ // Make sure we don't set mark off the line.
+ if(w_marko_bak >= llength(w_markp_bak))
+ w_marko_bak = llength(w_markp_bak) - 1;
+
+ curwp->w_marko = w_marko_bak;
+ curwp->w_markp = w_markp_bak;
+ }
+ }
+
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL);
+ else
+ emlwrite(_("Done checking spelling"), NULL);
+
+ speller_close(aspellinfo);
+ free_word_info_words(&word_info);
+
+ swapimark(0, 1);
+ curwp->w_flag |= WFHARD|WFMODE;
+
+ update();
+ return 1;
+}
+
+/*
+ * handle_cb_editchange() - combobox contents have changed so enable/disable
+ * the Change and Change All buttons accordingly.
+ */
+static void
+handle_cb_editchange(HWND hDlg, HWND hwnd_combo)
+{
+ int text_len = ComboBox_GetTextLength(hwnd_combo);
+
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGE), !!text_len);
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGEALL), !!text_len);
+}
+
+/*
+ * spell_dlg_proc_set_word() - Set up the dialog to display spelling
+ * information and suggestions for the current word.
+ */
+static void
+spell_dlg_proc_set_word(HWND hDlg, WORD_INFO *word_info)
+{
+ TCHAR dlg_title[256];
+ HWND hwnd_combo = GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS);
+
+ // Clear the combobox.
+ ComboBox_ResetContent(hwnd_combo);
+
+ // Set the dialog box title
+ _sntprintf(dlg_title, ARRAYSIZE(dlg_title), TEXT("Not in Dictionary: %s"),
+ word_info->word_lptstr);
+ dlg_title[ARRAYSIZE(dlg_title) - 1] = 0;
+ SetWindowText(hDlg, dlg_title);
+
+ // Populate the combobox with suggestions
+ if(speller_suggestion_init(word_info->aspellinfo, word_info->word_utf8, -1))
+ {
+ const char *suggestion_utf8;
+
+ while(suggestion_utf8 = speller_suggestion_getnext(word_info->aspellinfo))
+ {
+ LPTSTR suggestion_lptstr = utf8_to_lptstr((LPSTR)suggestion_utf8);
+
+ if(suggestion_lptstr)
+ {
+ ComboBox_AddString(hwnd_combo, suggestion_lptstr);
+ fs_give((void **)&suggestion_lptstr);
+ }
+ }
+
+ // Select the first one.
+ ComboBox_SetCurSel(hwnd_combo, 0);
+
+ speller_suggestion_close(word_info->aspellinfo);
+ }
+
+ handle_cb_editchange(hDlg, hwnd_combo);
+}
+
+/*
+ * spell_dlg_next_word() - Move to the next misspelled word and display
+ * the information for it. If no more bad words, kill the dialog.
+ */
+static void
+spell_dlg_next_word(HWND hDlg, WORD_INFO *word_info)
+{
+ if(!get_next_bad_word(word_info))
+ {
+ EndDialog(hDlg, 0);
+ return;
+ }
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+}
+
+/*
+ * chword_n() - change the given word_len pointed to by the curwp->w_dot
+ * pointers to the word in cb
+ */
+static void
+chword_n(int wb_len, UCS *cb)
+{
+ ldelete(wb_len, NULL); /* not saved in kill buffer */
+
+ while(*cb != '\0')
+ linsert(1, *cb++);
+
+ curwp->w_flag |= WFEDIT;
+}
+
+/*
+ * replace_all() - replace all instances of oldword with newword from
+ * the current '.' on. Mark is where curwp will get restored to.
+ */
+static void
+replace_all(UCS *oldword, int oldword_len, UCS *newword)
+{
+ int wrap;
+
+ // Mark should be at "." right now - ie the start of the word we're
+ // checking. We're going to use mark below to restore curwp since
+ // chword_n can potentially realloc LINES and it knows about markp.
+ assert(curwp->w_markp);
+ assert(curwp->w_markp == curwp->w_dotp);
+ assert(curwp->w_marko == curwp->w_doto);
+
+ curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
+ while(forscan(&wrap, oldword, NULL, 0, 1))
+ {
+ if(wrap)
+ break; /* wrap NOT allowed! */
+
+ /*
+ * We want to minimize the number of substrings that we report
+ * as matching a misspelled word...
+ */
+ if(lgetc(curwp->w_dotp, 0).c != '>')
+ {
+ LINE *lp = curwp->w_dotp; /* for convenience */
+ int off = curwp->w_doto;
+
+ if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c))
+ {
+ off += oldword_len;
+
+ if(off == llength(lp) || !ucs4_isalpha(lgetc(lp, off).c))
+ {
+ // Replace this word
+ chword_n(oldword_len, newword);
+ }
+ }
+ }
+
+ forwchar(0, 1); /* move on... */
+
+ }
+ curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
+
+ curwp->w_dotp = curwp->w_markp;
+ curwp->w_doto = curwp->w_marko;
+}
+
+/*
+ * handle_button_change() - Someone pressed the Change or Change All button.
+ * So deal with it.
+ */
+static void
+handle_button_change(HWND hDlg, WORD_INFO *word_info, int do_replace_all)
+{
+ int str_lptstr_len;
+ TCHAR str_lptstr[128];
+
+ // Get the length of what they want to change to.
+ str_lptstr_len = ComboBox_GetText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS),
+ str_lptstr, ARRAYSIZE(str_lptstr));
+ if(str_lptstr_len)
+ {
+ UCS *str_ucs4;
+ char *str_utf8;
+
+ // Notify the speller so it'll suggest this word next time around.
+ str_utf8 = lptstr_to_utf8(str_lptstr);
+ if(str_utf8)
+ {
+ speller_replace_word(word_info->aspellinfo,
+ word_info->word_utf8, -1, str_utf8, -1);
+ fs_give((void **)&str_utf8);
+ }
+
+ str_ucs4 = lptstr_to_ucs4(str_lptstr);
+ if(!str_ucs4)
+ {
+ // Uh oh - massive error.
+ word_info->utf8_err_message = _("Spelling: lptstr_to_ucs4() failed");
+ EndDialog(hDlg, -1);
+ return;
+ }
+
+ // Move back to start of this word.
+ curwp->w_doto = word_info->word_doto;
+ curwp->w_dotp = word_info->word_dotp;
+
+ if(do_replace_all)
+ {
+ int i;
+ int word_size;
+ UCS word[128];
+
+ word_size = word_info->word_size;
+ if(word_size >= ARRAYSIZE(word) - 1)
+ word_size = ARRAYSIZE(word) - 1;
+
+ for(i = 0; i < word_size; i++)
+ word[i] = lgetc(curwp->w_dotp, curwp->w_doto + i).c;
+
+ word[i] = 0;
+
+ replace_all(word, word_size, str_ucs4);
+
+ // Skip over the word we just inserted.
+ forwchar(FALSE, (int)ucs4_strlen(str_ucs4));
+ }
+ else
+ {
+ // Swap it with the new improved version.
+ chword_n(word_info->word_size, str_ucs4);
+ }
+ fs_give((void **)&str_ucs4);
+ }
+
+ update();
+ spell_dlg_next_word(hDlg, word_info);
+}
+
+/*
+ * center_dialog() - Center a dialog with respect to its parent.
+ */
+static void
+center_dialog(HWND hDlg)
+{
+ int dx;
+ int dy;
+ RECT rcDialog;
+ RECT rcParent;
+
+ GetWindowRect(GetParent(hDlg), &rcParent);
+ GetWindowRect(hDlg, &rcDialog);
+
+ dx = ((rcParent.right - rcParent.left) -
+ (rcDialog.right - rcDialog.left)) / 2 + rcParent.left - rcDialog.left;
+
+ dy = ((rcParent.bottom - rcParent.top) -
+ (rcDialog.bottom - rcDialog.top)) / 2 + rcParent.top - rcDialog.top;
+
+ OffsetRect(&rcDialog, dx, dy);
+
+ SetWindowPos(hDlg, NULL, rcDialog.left, rcDialog.top, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER);
+}
+
+/*
+ * spell_dlg_on_command() - Handle the WM_COMMAND stuff for the spell dialog.
+ */
+static void
+spell_dlg_on_command(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
+{
+ WORD_INFO *word_info =
+ (WORD_INFO *)(LONG_PTR)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+
+ switch(id)
+ {
+ case ID_COMBO_SUGGESTIONS:
+ if(codeNotify == CBN_EDITCHANGE)
+ handle_cb_editchange(hDlg, hwndCtl);
+ break;
+
+ case ID_BUTTON_CHANGE:
+ handle_button_change(hDlg, word_info, 0);
+ break;
+
+ case ID_BUTTON_CHANGEALL:
+ handle_button_change(hDlg, word_info, 1);
+ break;
+
+ case ID_BUTTON_IGNOREONCE:
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_IGNOREALL:
+ speller_ignore_all(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_ADD_TO_DICT:
+ speller_add_to_dictionary(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_CANCEL:
+ EndDialog(hDlg, 0);
+ break;
+ }
+}
+
+/*
+ * spell_dlg_proc() - Main spell dialog proc.
+ */
+static INT_PTR CALLBACK
+spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ WORD_INFO *word_info;
+
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ center_dialog(hDlg);
+
+ // Limit how much junk they can type in.
+ ComboBox_LimitText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS), 128);
+
+ word_info = (WORD_INFO *)lParam;
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)word_info);
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+ return TRUE;
+
+ case WM_COMMAND:
+ HANDLE_WM_COMMAND(hDlg, wParam, lParam, spell_dlg_on_command);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * get_next_word() - Get the next word from dot. And skip words in
+ * quoted lines.
+ */
+static int
+get_next_word(WORD_INFO *word_info)
+{
+ // Skip quoted lines
+ while(lgetc(curwp->w_dotp, 0).c == '>')
+ {
+ if(curwp->w_dotp == curbp->b_linep)
+ return 0;
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ }
+
+ // Move to a word.
+ while(inword() == FALSE)
+ {
+ if(forwchar(FALSE, 1) == FALSE)
+ {
+ // There is no next word.
+ return 0;
+ }
+ }
+
+ // If mark is currently set, clear it.
+ if(curwp->w_markp)
+ setmark(0, 1);
+
+ // Set mark to beginning of this word.
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ // Get word length
+ word_info->word_doto = curwp->w_doto;
+ word_info->word_dotp = curwp->w_dotp;
+ word_info->word_size = 0;
+
+ while((inword() != FALSE) && (word_info->word_dotp == curwp->w_dotp))
+ {
+ word_info->word_size++;
+
+ if(forwchar(FALSE, 1) == FALSE)
+ break;
+ }
+
+ word_info->word_utf8 = ucs4_to_utf8_cpystr_n(
+ (UCS *)&word_info->word_dotp->l_text[word_info->word_doto],
+ word_info->word_size);
+ return 1;
+}
+
+/*
+ * get_next_word() - free the word_info members word_utf8 and word_lptstr.
+ */
+static void
+free_word_info_words(WORD_INFO *word_info)
+{
+ if(word_info->word_utf8)
+ {
+ fs_give((void **)&word_info->word_utf8);
+ word_info->word_utf8 = NULL;
+ }
+
+ if(word_info->word_lptstr)
+ {
+ fs_give((void **)&word_info->word_lptstr);
+ word_info->word_lptstr = NULL;
+ }
+}
+
+/*
+ * get_next_bad_word() - Search from '.' for the next word we think is
+ * rotten. Mark it and highlight it.
+ */
+static int
+get_next_bad_word(WORD_INFO *word_info)
+{
+ free_word_info_words(word_info);
+
+ while(get_next_word(word_info))
+ {
+ int ret;
+
+ /* pass over words that contain @ */
+ if(strchr(word_info->word_utf8, '@'))
+ ret = 1;
+ else
+ ret = speller_check_word(word_info->aspellinfo, word_info->word_utf8, -1);
+
+ if(ret == -1)
+ {
+ word_info->utf8_err_message =
+ speller_get_error_message(word_info->aspellinfo);
+ if(!word_info->utf8_err_message)
+ word_info->utf8_err_message = _("Spelling: speller_check_word() failed");
+ return 0;
+ }
+ else if(ret == 0)
+ {
+ // Highlight word.
+ update();
+
+ word_info->word_lptstr = utf8_to_lptstr(word_info->word_utf8);
+ return 1;
+ }
+
+ free_word_info_words(word_info);
+ }
+
+ return 0;
+}
diff --git a/pico/osdep/mswin_spell.h b/pico/osdep/mswin_spell.h
new file mode 100644
index 00000000..bfa78116
--- /dev/null
+++ b/pico/osdep/mswin_spell.h
@@ -0,0 +1,9 @@
+#define DLG_SPELL 300
+
+#define ID_COMBO_SUGGESTIONS 100
+#define ID_BUTTON_CHANGE IDOK
+#define ID_BUTTON_CHANGEALL 101
+#define ID_BUTTON_IGNOREONCE 102
+#define ID_BUTTON_IGNOREALL 103
+#define ID_BUTTON_ADD_TO_DICT 104
+#define ID_BUTTON_CANCEL IDCANCEL
diff --git a/pico/osdep/mswin_tw.c b/pico/osdep/mswin_tw.c
new file mode 100644
index 00000000..663c8141
--- /dev/null
+++ b/pico/osdep/mswin_tw.c
@@ -0,0 +1,765 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#define STRICT
+#define UNICODE
+#define _UNICODE
+#include <windows.h>
+#include <windowsx.h>
+#include <tchar.h>
+
+/*
+ article entitled "About Rich Edit Controls" here (remove all spaces from url):
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/
+ platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp
+
+ Rich Edit version DLL
+ 1.0 Riched32.dll
+ 2.0 Riched20.dll
+ 3.0 Riched20.dll
+ 4.1 Msftedit.dll
+
+ The following list describes which versions of Rich Edit are included in
+ which releases of Microsoft Windows.
+ Windows XP SP1 Includes Rich Edit 4.1, Rich Edit 3.0, and a Rich Edit 1.0 emulator.
+ Windows XP Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
+ Windows Me Includes Rich Edit 1.0 and 3.0.
+ Windows 2000 Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
+ Windows NT 4.0 Includes Rich Edit 1.0 and 2.0.
+ Windows 98 Includes Rich Edit 1.0 and 2.0.
+ Windows 95 Includes only Rich Edit 1.0. However, Riched20.dll is
+ compatible with Windows 95 and may be installed by an
+ application that requires it.
+
+ We're using richedit v2 since it is the first to have Unicode support. Does
+ potentially limit us to Win98 unless we install riched20.dll or it's
+ already there.
+ */
+#define _RICHEDIT_VER 0x0200
+#include <richedit.h>
+#include "resource.h"
+
+#include "mswin_tw.h"
+
+/*
+ * Globals
+ */
+static const TCHAR g_mswin_tw_class_name[] = TEXT("PineTWClass");
+
+// Maximum amount of text allowed in these textwindows.
+// Set via a EM_EXLIMITTEXT message.
+static const LPARAM g_max_text = 8 * 1024 * 1024 - 1;
+
+/*
+ * Function prototypes
+ */
+static LRESULT CALLBACK mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
+static UINT Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax);
+static UINT Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags);
+static BOOL Edit_ExIsReadOnly(HWND hwnd_edit);
+
+/*
+ * mswin_tw_create() - Create a mswin textwindow.
+ */
+int
+mswin_tw_create(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR title)
+{
+ int width, height;
+ static int s_mswin_tw_class_registered = 0;
+
+ mswin_tw->hwnd = NULL;
+ mswin_tw->hwnd_edit = NULL;
+
+ if(!s_mswin_tw_class_registered)
+ {
+ WNDCLASS wndclass;
+
+ LoadLibrary(TEXT("riched20.dll"));
+
+ memset(&wndclass, 0, sizeof(wndclass));
+ wndclass.style = CS_BYTEALIGNWINDOW;
+ wndclass.lpfnWndProc = mswin_tw_wndproc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof(ULONG_PTR);
+ wndclass.hInstance = mswin_tw->hInstance ;
+ wndclass.hIcon = LoadIcon (mswin_tw->hInstance, MAKEINTRESOURCE( ALPINEICON));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wndclass.lpszMenuName = MAKEINTRESOURCE(TEXTWINMENU);
+ wndclass.lpszClassName = g_mswin_tw_class_name ;
+
+ RegisterClass(&wndclass);
+
+ s_mswin_tw_class_registered = 1;
+ }
+
+ // If we're passed CW_USEDEFAULT as the position, then use right/bottom
+ // as the width/height. Otherwise use the full rect.
+ width = (mswin_tw->rcSize.left == CW_USEDEFAULT) ?
+ mswin_tw->rcSize.right : mswin_tw->rcSize.right - mswin_tw->rcSize.left;
+ height = (mswin_tw->rcSize.top == CW_USEDEFAULT) ?
+ mswin_tw->rcSize.bottom : mswin_tw->rcSize.bottom - mswin_tw->rcSize.top;
+
+ mswin_tw->hwnd = CreateWindow(
+ g_mswin_tw_class_name, title,
+ WS_OVERLAPPEDWINDOW,
+ mswin_tw->rcSize.left,
+ mswin_tw->rcSize.top,
+ width,
+ height,
+ HWND_DESKTOP, NULL,
+ mswin_tw->hInstance, mswin_tw);
+ if(!mswin_tw->hwnd)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * mswin_tw_close() - close the mswin textwindow.
+ */
+void
+mswin_tw_close(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd)
+ DestroyWindow(mswin_tw->hwnd);
+}
+
+/*
+ * EditStreamOutCallback - callback for mswin_tw_write_to_file.
+ */
+static DWORD CALLBACK
+EditStreamOutCallback(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
+{
+ HANDLE hFile = (HANDLE)dwCookie;
+ return !WriteFile(hFile, lpBuff, cb, (DWORD *)pcb, NULL);
+}
+
+/*
+ * mswin_tw_fill_from_file() - write textwindow contents to a file.
+ */
+BOOL
+mswin_tw_write_to_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file)
+{
+ BOOL fSuccess = FALSE;
+ HANDLE hFile = CreateFile(file, GENERIC_WRITE, 0,
+ 0, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ EDITSTREAM es;
+
+ es.dwCookie = (DWORD_PTR)hFile;
+ es.dwError = 0;
+ es.pfnCallback = EditStreamOutCallback;
+
+ if(SendMessage(mswin_tw->hwnd_edit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es) &&
+ es.dwError == 0)
+ {
+ fSuccess = TRUE;
+ }
+ }
+
+ CloseHandle(hFile);
+ return fSuccess;
+}
+
+
+#ifdef ALTED_DOT
+
+/*
+ * EditStreamCallback - callback for mswin_tw_fill_from_file.
+ */
+static DWORD CALLBACK
+EditStreamInCallback(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
+{
+ HANDLE hFile = (HANDLE)dwCookie;
+ return !ReadFile(hFile, lpBuff, cb, (DWORD *)pcb, NULL);
+}
+
+
+/*
+ * mswin_tw_fill_from_file() - read file contents into the mswin textwindow.
+ */
+BOOL
+mswin_tw_fill_from_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file)
+{
+ BOOL fSuccess = FALSE;
+ HANDLE hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
+ 0, OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ EDITSTREAM es;
+
+ es.dwCookie = (DWORD_PTR)hFile;
+ es.dwError = 0;
+ es.pfnCallback = EditStreamInCallback;
+
+ if(SendMessage(mswin_tw->hwnd_edit, EM_STREAMIN, SF_TEXT, (LPARAM)&es) &&
+ es.dwError == 0)
+ {
+ fSuccess = TRUE;
+ }
+ }
+
+ CloseHandle(hFile);
+ return fSuccess;
+}
+
+#endif /* ALTED_DOT */
+
+/*
+ * mswin_tw_showwindow() - show the main hwnd (SW_SHOWNORMAL, etc.).
+ */
+void
+mswin_tw_showwindow(MSWIN_TEXTWINDOW *mswin_tw, int nCmdShow)
+{
+ if(mswin_tw && mswin_tw->hwnd)
+ ShowWindow(mswin_tw->hwnd, nCmdShow);
+}
+
+/*
+ * mswin_tw_setfont() - Sets the hfont for the entire edit control.
+ */
+void
+mswin_tw_setfont(MSWIN_TEXTWINDOW *mswin_tw, HFONT hfont)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ SetWindowFont(mswin_tw->hwnd_edit, hfont, TRUE);
+}
+
+/*
+ * mswin_tw_setcolor() - Set colors for entire edit control. If we're
+ * passed -1 for mswin_tw, then set the colors for all textwindows.
+ */
+void
+mswin_tw_setcolor(MSWIN_TEXTWINDOW *mswin_tw,
+ COLORREF TextColor, COLORREF BackColor)
+{
+ if(mswin_tw == (MSWIN_TEXTWINDOW *)-1)
+ {
+ HWND hwnd = NULL;
+
+ while(hwnd = FindWindowEx(NULL, hwnd, g_mswin_tw_class_name, NULL))
+ {
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(
+ hwnd, GWLP_USERDATA);
+ if(mswin_tw)
+ {
+ mswin_tw_setcolor(mswin_tw, TextColor, BackColor);
+ }
+ }
+ }
+ else if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ CHARFORMAT2W cf2;
+
+ memset(&cf2, 0, sizeof(cf2));
+ cf2.cbSize = sizeof(cf2);
+ cf2.dwMask = CFM_COLOR;
+ cf2.crTextColor = TextColor;
+
+ SendMessage(mswin_tw->hwnd_edit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
+ SendMessage(mswin_tw->hwnd_edit, EM_SETBKGNDCOLOR, 0, BackColor);
+ }
+}
+
+/*
+ * mswin_tw_puts_lptstr() - Stuffs a LPTSTR string at the end of our edit
+ * control.
+ */
+int
+mswin_tw_puts_lptstr(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR msg)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ TCHAR lf_str[] = TEXT("\n");
+
+ Edit_ExSetSel(mswin_tw->hwnd_edit, -1, -1);
+ Edit_ReplaceSel(mswin_tw->hwnd_edit, msg);
+
+ Edit_ReplaceSel(mswin_tw->hwnd_edit, lf_str);
+
+ Edit_ScrollCaret(mswin_tw->hwnd_edit);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * mswin_tw_printf() - printf version of mswin_tw_puts_lptstr.
+ */
+int
+mswin_tw_printf(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR fmt, ...)
+{
+ TCHAR msg[1024];
+ va_list vlist;
+
+ va_start(vlist, fmt);
+ _vsntprintf(msg, ARRAYSIZE(msg), fmt, vlist);
+ va_end(vlist);
+
+ msg[ARRAYSIZE(msg) - 1] = 0;
+ return mswin_tw_puts_lptstr(mswin_tw, msg);
+}
+
+/*
+ * mswin_tw_gettextlength() - Returns the number of TCHARs in the edit control.
+ */
+UINT
+mswin_tw_gettextlength(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ return (UINT)Edit_ExGetTextLen(mswin_tw->hwnd_edit, GTL_USECRLF);
+
+ return 0;
+}
+
+/*
+ * mswin_tw_gettext() - Return value is the number of TCHARs copied into the
+ * output buffer.
+ */
+UINT
+mswin_tw_gettext(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR lptstr_ret, int lptstr_len)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ GETTEXTEX gt;
+ LRESULT lresult;
+
+ gt.cb = lptstr_len * sizeof(TCHAR);
+ gt.flags = GT_DEFAULT | GT_USECRLF;
+ gt.codepage = 1200;
+ gt.lpDefaultChar = NULL;
+ gt.lpUsedDefChar = NULL;
+
+ lptstr_ret[0] = 0;
+ lresult = SendMessage(mswin_tw->hwnd_edit, EM_GETTEXTEX,
+ (WPARAM)&gt, (LPARAM)lptstr_ret);
+ return (int)lresult;
+ }
+
+ return 0;
+}
+
+/*
+ * mswin_tw_setsel() - Set edit control selection.
+ */
+void
+mswin_tw_setsel(MSWIN_TEXTWINDOW *mswin_tw, LONG min, LONG max)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ Edit_ExSetSel(mswin_tw->hwnd_edit, min, max);
+ }
+}
+
+/*
+ * mswin_set_readonly() - Set readonly status.
+ */
+void
+mswin_set_readonly(MSWIN_TEXTWINDOW *mswin_tw, BOOL read_only)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ Edit_SetReadOnly(mswin_tw->hwnd_edit, read_only);
+}
+
+/*
+ * mswin_tw_clear() - Clear all text from edit control.
+ */
+void
+mswin_tw_clear(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ SETTEXTEX stex;
+
+ stex.flags = ST_DEFAULT;
+ stex.codepage = 1200; // Unicode (see richedit.h)
+
+ SendMessage(mswin_tw->hwnd_edit, EM_SETTEXTEX,
+ (WPARAM)&stex, (LPARAM)TEXT(""));
+
+ if(mswin_tw->clear_callback)
+ mswin_tw->clear_callback(mswin_tw);
+ }
+}
+
+/*
+ * MySetWindowLongPtr() - Little wrapper routine which calls the
+ * Windows SetWindowLongPtr() and removes the stupid warning which is
+ * just seriously lame.
+ */
+static LONG_PTR
+MySetWindowLongPtr(HWND hwnd, int nIndex, void *NewLongPtr)
+{
+// warning C4244: 'function': conversion from 'LONG_PTR' to 'LONG',
+// possible loss of data
+#pragma warning(push)
+#pragma warning(disable: 4244)
+ return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)NewLongPtr);
+#pragma warning(pop)
+}
+
+/*
+ * mswin_tw_wm_command() - WM_CONTEXTMENU handler for textwindows
+ */
+static void
+mswin_tw_wm_contextmenu(MSWIN_TEXTWINDOW *mswin_tw, HWND hwnd, HWND hwndContext,
+ int xPos, int yPos)
+{
+ HMENU hMenu;
+
+ hMenu = CreatePopupMenu();
+ if(hMenu)
+ {
+ int i;
+ CHARRANGE cr;
+ MENUITEMINFO mitem;
+ static const struct
+ {
+ UINT wID;
+ LPTSTR dwTypeData;
+ } s_popup_menu[] =
+ {
+ { IDM_EDIT_COPY, TEXT("&Copy") },
+ { IDM_EDIT_COPY_APPEND, TEXT("Copy &Append") },
+ { IDM_EDIT_CLEAR, TEXT("Clea&r") },
+ };
+
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.fType = MFT_STRING;
+
+ SendMessage(mswin_tw->hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ for(i = 0; i < ARRAYSIZE(s_popup_menu); i++)
+ {
+ switch(s_popup_menu[i].wID)
+ {
+ case IDM_EDIT_CLEAR:
+ // Only enable it if there is a clear callback set.
+ mitem.fState = (mswin_tw->clear_callback) ?
+ MFS_ENABLED : MFS_GRAYED;
+ break;
+ case IDM_EDIT_COPY:
+ case IDM_EDIT_COPY_APPEND:
+ // Only enable if there is a selection.
+ mitem.fState = (cr.cpMax > cr.cpMin) ?
+ MFS_ENABLED : MFS_GRAYED;
+ break;
+ default:
+ mitem.fState = MFS_ENABLED;
+ break;
+ }
+
+ mitem.wID = s_popup_menu[i].wID;
+ mitem.dwTypeData = s_popup_menu[i].dwTypeData;
+ mitem.cch = (UINT)_tcslen(s_popup_menu[i].dwTypeData);
+ InsertMenuItem(hMenu, i, FALSE, &mitem);
+ }
+
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN |
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ xPos, yPos, 0, hwnd, NULL);
+
+ DestroyMenu(hMenu);
+ }
+}
+
+/*
+ * mswin_tw_wm_command() - WM_COMMAND handler for textwindows
+ */
+static void
+mswin_tw_wm_command(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
+{
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch(id)
+ {
+ case IDM_FILE_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+
+ case IDM_FILE_PRINT:
+ if(mswin_tw->print_callback)
+ mswin_tw->print_callback(mswin_tw);
+ break;
+
+ case IDM_EDIT_COPY:
+ SendMessage(mswin_tw->hwnd_edit, WM_COPY, 0, 0);
+ break;
+
+ case IDM_EDIT_CLEAR:
+ mswin_tw_clear(mswin_tw);
+ break;
+
+ case IDM_EDIT_COPY_APPEND:
+ {
+ CHARRANGE cr;
+ int text_len0, text_len1;
+ HWND hwnd_edit = mswin_tw->hwnd_edit;
+ BOOL EditIsReadOnly = Edit_ExIsReadOnly(hwnd_edit);
+
+ SetWindowRedraw(hwnd_edit, FALSE);
+ Edit_SetReadOnly(hwnd_edit, FALSE);
+
+ // Get current selection.
+ SendMessage(hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ // Get current length.
+ text_len0 = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ // Paste current clip right before our new selection.
+ Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMin);
+ SendMessage(hwnd_edit, WM_PASTE, 0, 0);
+
+ // Get new length.
+ text_len1 = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ // Select new and old clip and copy em.
+ Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMax + text_len1 - text_len0);
+ SendMessage(hwnd_edit, WM_COPY, 0, 0);
+
+ // Undo our paste and restore original selection.
+ SendMessage(hwnd_edit, WM_UNDO, 0, 0);
+ SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ // Set back to read only.
+ Edit_SetReadOnly(hwnd_edit, EditIsReadOnly);
+ SetWindowRedraw(hwnd_edit, TRUE);
+ break;
+ }
+
+ case IDM_EDIT_SEL_ALL:
+ Edit_ExSetSel(mswin_tw->hwnd_edit, 0, -1);
+ break;
+ }
+}
+
+/*
+ * mswin_tw_wm_notify() - WM_NOTIFY handler for textwindows
+ */
+static LRESULT
+mswin_tw_wm_notify(HWND hwnd, int idCtrl, NMHDR *nmhdr)
+{
+ HWND hwnd_edit = nmhdr->hwndFrom;
+
+ if(nmhdr->code == EN_MSGFILTER)
+ {
+ MSGFILTER *msg_filter = (MSGFILTER *)nmhdr;
+
+ if(msg_filter->msg == WM_KEYDOWN)
+ {
+ if(msg_filter->wParam == 'E')
+ {
+ int control_down = GetKeyState(VK_CONTROL) < 0;
+ int shift_down = GetKeyState(VK_SHIFT) < 0;
+
+ // Ctrl+Shift+E toggles the readonly attribute on the text
+ // buffer.
+ if(control_down && shift_down)
+ Edit_SetReadOnly(hwnd_edit, !Edit_ExIsReadOnly(hwnd_edit));
+ return TRUE;
+ }
+ }
+ else if(msg_filter->msg == WM_CHAR)
+ {
+ // Only override these keys if this buffer is readonly.
+ if(Edit_ExIsReadOnly(hwnd_edit))
+ {
+ switch(msg_filter->wParam)
+ {
+ case 'k':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_LINEUP, 0);
+ return TRUE;
+ case 'j':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_LINEDOWN, 0);
+ return TRUE;
+ case '-':
+ case 'b':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEUP, 0);
+ return TRUE;
+ case ' ':
+ case 'f':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEDOWN, 0);
+ return TRUE;
+ }
+ }
+ }
+ }
+ else if(nmhdr->code == EN_LINK)
+ {
+ ENLINK *enlink = (ENLINK *)nmhdr;
+
+ if(enlink->msg == WM_LBUTTONDOWN)
+ {
+ TEXTRANGE tr;
+ TCHAR link_buf[1024];
+
+ link_buf[0] = 0;
+ tr.lpstrText = link_buf;
+
+ tr.chrg = enlink->chrg;
+ if(tr.chrg.cpMax - tr.chrg.cpMin > ARRAYSIZE(link_buf))
+ tr.chrg.cpMax = tr.chrg.cpMin + ARRAYSIZE(link_buf);
+
+ SendMessage(hwnd_edit, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ ShellExecute(hwnd, TEXT("Open"), link_buf, NULL, NULL, SW_SHOWNORMAL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * mswin_tw_wndproc() - Main window proc for mswin textwindows.
+ */
+static LRESULT CALLBACK
+mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch(msg)
+ {
+ case WM_CREATE:
+ {
+ CREATESTRUCT *pcs = (CREATESTRUCT *)lp;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)pcs->lpCreateParams;
+
+ MySetWindowLongPtr(hwnd, GWLP_USERDATA, mswin_tw);
+
+ mswin_tw->hwnd_edit = CreateWindowEx(
+ WS_EX_CLIENTEDGE, RICHEDIT_CLASS, 0,
+ WS_VISIBLE | WS_CHILD |
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
+ WS_VSCROLL | WS_HSCROLL |
+ ES_MULTILINE | ES_READONLY | ES_NOHIDESEL,
+ 0, 0, 1, 1,
+ hwnd, 0, mswin_tw->hInstance, 0);
+
+ // We want link and key event notifications.
+ SendMessage(mswin_tw->hwnd_edit, EM_SETEVENTMASK,
+ 0, (ENM_KEYEVENTS | ENM_LINK));
+
+ // Specifies the maximum amount of text that can be entered.
+ SendMessage(mswin_tw->hwnd_edit, EM_EXLIMITTEXT, 0, g_max_text);
+
+ // Enable automatic detection of URLs by our rich edit control.
+ SendMessage(mswin_tw->hwnd_edit, EM_AUTOURLDETECT, TRUE, 0);
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ mswin_tw_wm_contextmenu(mswin_tw, hwnd, (HWND)wp, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
+ break;
+
+ case WM_NOTIFY:
+ return mswin_tw_wm_notify(hwnd, (int)wp, (NMHDR *)lp);
+
+ case WM_COMMAND:
+ HANDLE_WM_COMMAND(hwnd, wp, lp, mswin_tw_wm_command);
+ break;
+
+ case WM_SETFOCUS:
+ SetFocus(mswin_tw->hwnd_edit);
+ return TRUE;
+
+ case WM_SIZE:
+ MoveWindow(mswin_tw->hwnd_edit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE);
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ if(!IsIconic(hwnd))
+ {
+ WINDOWPOS *wpos = (WINDOWPOS *)lp;
+
+ mswin_tw->rcSize.left = wpos->x;
+ mswin_tw->rcSize.top = wpos->y;
+ mswin_tw->rcSize.right = wpos->x + wpos->cx;
+ mswin_tw->rcSize.bottom = wpos->y + wpos->cy;
+ }
+ break;
+
+ case WM_DESTROY:
+ if(mswin_tw->out_file)
+ {
+ mswin_tw->out_file_ret = mswin_tw_write_to_file(mswin_tw,
+ mswin_tw->out_file);
+ }
+
+ mswin_tw->hwnd = NULL;
+ mswin_tw->hwnd_edit = NULL;
+
+ if(mswin_tw->close_callback)
+ mswin_tw->close_callback(mswin_tw);
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wp, lp);
+}
+
+/*
+ * Edit_ExGetTextLen() - Helper routine for getting count of chars.
+ */
+static UINT
+Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags)
+{
+ GETTEXTLENGTHEX gtl;
+
+ gtl.flags = GTL_PRECISE | GTL_NUMCHARS | flags;
+ gtl.codepage = 1200; // Unicode (see richedit.h)
+ return (UINT)SendMessage(hwnd_edit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
+}
+
+/*
+ * Edit_ExSetSel() - Helper routine for setting edit selection.
+ */
+static UINT
+Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax)
+{
+ CHARRANGE cr;
+
+ if(cpMin == -1)
+ cpMin = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ cr.cpMin = cpMin;
+ cr.cpMax = cpMax;
+ return (UINT)SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
+}
+
+/*
+ * Edit_ExIsReadOnly() - TRUE if edit buffer is read only.
+ */
+static BOOL
+Edit_ExIsReadOnly(HWND hwnd_edit)
+{
+ LRESULT edit_opts;
+
+ edit_opts = SendMessage(hwnd_edit, EM_GETOPTIONS, 0, 0);
+ return !!(edit_opts & ECO_READONLY);
+}
+
diff --git a/pico/osdep/mswin_tw.h b/pico/osdep/mswin_tw.h
new file mode 100644
index 00000000..93eb7b76
--- /dev/null
+++ b/pico/osdep/mswin_tw.h
@@ -0,0 +1,58 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+typedef struct MSWIN_TEXTWINDOW MSWIN_TEXTWINDOW;
+typedef void (MSWIN_TW_CALLBACK)(MSWIN_TEXTWINDOW *mswin_tw);
+
+typedef struct MSWIN_TEXTWINDOW
+{
+ // User should set these fields before calling mswin_tw_create()
+ UINT id; // Caller can use to determine which
+ // textwindow this is (Ie: IDM_OPT_NEWMAILWIN)
+ HANDLE hInstance;
+ RECT rcSize; // Window position
+
+ MSWIN_TW_CALLBACK *print_callback; // Print menu selected callback routine.
+ MSWIN_TW_CALLBACK *close_callback; // Callback for when window is closed.
+ MSWIN_TW_CALLBACK *clear_callback; // Callback after text is cleared.
+
+ LPCTSTR out_file; // Save edit contents on close to this file.
+ BOOL out_file_ret; // TRUE - out_file written, FALSE - nope.
+
+ // internal fields
+ HWND hwnd; // hwnd for this mswin textwindow.
+ HWND hwnd_edit;
+} MSWIN_TEXTWINDOW;
+
+int mswin_tw_create(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR title);
+void mswin_tw_close(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_tw_showwindow(MSWIN_TEXTWINDOW *mswin_tw, int nCmdShow);
+
+BOOL mswin_tw_fill_from_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file);
+BOOL mswin_tw_write_to_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file);
+
+void mswin_tw_setfont(MSWIN_TEXTWINDOW *mswin_tw, HFONT hfont);
+void mswin_tw_setcolor(MSWIN_TEXTWINDOW *mswin_tw,
+ COLORREF TextColor, COLORREF BackColor);
+
+int mswin_tw_puts_lptstr(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR msg);
+int mswin_tw_printf(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR fmt, ...);
+
+UINT mswin_tw_gettext(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR lptstr_ret, int lptstr_len);
+UINT mswin_tw_gettextlength(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_tw_setsel(MSWIN_TEXTWINDOW *mswin_tw, LONG min, LONG max);
+void mswin_tw_clear(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_set_readonly(MSWIN_TEXTWINDOW *mswin_tw, BOOL read_only);
diff --git a/pico/osdep/mswinhnd.cur b/pico/osdep/mswinhnd.cur
new file mode 100644
index 00000000..253c3d33
--- /dev/null
+++ b/pico/osdep/mswinhnd.cur
Binary files differ
diff --git a/pico/osdep/newmail.c b/pico/osdep/newmail.c
new file mode 100644
index 00000000..f900a634
--- /dev/null
+++ b/pico/osdep/newmail.c
@@ -0,0 +1,66 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: newmail.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "newmail.h"
+
+
+
+
+/*
+ * pico_new_mail - just checks mtime and atime of mail file and notifies user
+ * if it's possible that they have new mail.
+ */
+int
+pico_new_mail(void)
+{
+#ifndef _WINDOWS
+ int ret = 0;
+ static time_t lastchk = 0;
+ struct stat sbuf;
+ char inbox[256], *p;
+
+ if((p = (char *)getenv("MAIL")) != NULL)
+ /* fix unsafe sprintf - noticed by petter wahlman <petter@bluezone.no> */
+ snprintf(inbox, sizeof(inbox), "%s", p);
+ else
+ snprintf(inbox, sizeof(inbox), "%s/%s", MAILDIR, (char *) getlogin());
+
+ if(our_stat(inbox, &sbuf) == 0){
+ ret = sbuf.st_atime <= sbuf.st_mtime &&
+ (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
+ lastchk = sbuf.st_mtime;
+ return(ret);
+ }
+ else
+ return(ret);
+#else /* _WINDOWS */
+ return(0);
+#endif
+}
diff --git a/pico/osdep/newmail.h b/pico/osdep/newmail.h
new file mode 100644
index 00000000..5af645e1
--- /dev/null
+++ b/pico/osdep/newmail.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: newmail.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_NEWMAIL_INCLUDED
+#define PICO_OSDEP_NEWMAIL_INCLUDED
+
+
+/* exported prototypes */
+int pico_new_mail(void);
+
+
+#endif /* PICO_OSDEP_NEWMAIL_INCLUDED */
diff --git a/pico/osdep/os-win.h b/pico/osdep/os-win.h
new file mode 100644
index 00000000..2ececda1
--- /dev/null
+++ b/pico/osdep/os-win.h
@@ -0,0 +1,183 @@
+#ifndef _PICO_OS_INCLUDED
+#define _PICO_OS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+
+ OS dependencies, WIN version. See also the os-win.c files.
+ The following stuff may need to be changed for a new port, but once
+ the port is done it won't change. Further down in the file are a few
+ constants that you may want to configure differently than they
+ are configured, but probably not.
+
+ ----*/
+
+
+
+/*----------------- Are we ANSI? ---------------------------------------*/
+#define ANSI /* this is an ANSI compiler */
+
+/*------ If our compiler doesn't understand type void ------------------*/
+/* #define void char */
+
+/*-------- Standard ANSI functions usually defined in stdlib.h ---------*/
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <direct.h>
+#include <search.h>
+#undef CTRL
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+/*-------- Standard windows defines and then our window module defines. */
+#include <windows.h>
+#include <limits.h>
+#include <commdlg.h>
+#ifndef WIN32
+#include <print.h>
+#include <toolhelp.h>
+#endif
+#include <cderr.h>
+#include <winsock.h>
+#include <shellapi.h>
+
+#include "mswin.h"
+
+#include <io.h>
+#include <sys/stat.h>
+#include <winsock.h>
+#include <dos.h>
+#include <direct.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+
+
+/* Windows only version and resource defines. */
+#include "resource.h"
+
+
+#undef ERROR
+
+
+/*---- Declare sys_errlist() if not already declared -------------------*/
+/* extern char *sys_errlist[]; */
+
+
+
+/*----------------- locale.h -------------------------------------------*/
+/* #include <locale.h> */ /* To make matching and sorting work right */
+#define collator strucmp
+
+
+
+/*----------------- time.h ---------------------------------------------*/
+#include <time.h>
+/* plain time.h isn't enough on some systems */
+/* #include <sys/time.h> */ /* For struct timeval usually in time.h */
+
+
+
+/*--------------- signal.h ---------------------------------------------*/
+#include <signal.h>
+/* #include <sys/signal.h> */
+
+#define SigType void /* value returned by sig handlers is void */
+/* #define SigType int */ /* value returned by sig handlers is int */
+
+/* #define POSIX_SIGNALS */
+/* #define SYSV_SIGNALS */ /* use System-V signal semantics (ttyin.c) */
+
+#define SIG_PROTO(args) args
+
+
+
+/*-------------- A couple typedef's for integer sizes ------------------*/
+typedef unsigned long usign32_t;
+typedef unsigned short usign16_t;
+
+
+
+/*-------------- qsort argument type -----------------------------------*/
+#define QSType void
+/* #define QSType char */
+
+#define QcompType const void
+
+
+/*-------- Is window resizing available? -------------------------------*/
+/* #define RESIZING */
+ /* Actually, under windows it is, but RESIZING compiles in UNIX
+ * signals code for determining when the window resized. Window's
+ * works differently. */
+
+
+
+
+/*-------- If no vfork, use regular fork -------------------------------*/
+/* #define vfork fork */
+
+
+
+/*---- When no screen size can be discovered this is the size used -----*/
+#define DEFAULT_LINES_ON_TERMINAL (25)
+#define DEFAULT_COLUMNS_ON_TERMINAL (80)
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+
+
+/*
+ * File name separators, char and string
+ */
+#define C_FILESEP '\\'
+#define S_FILESEP "\\"
+
+
+/*
+ * What and where the tool that checks spelling is located. If this is
+ * undefined, then the spelling checker is not compiled into pico.
+ */
+#define SPELLER
+
+
+/*
+ * Mode passed chmod() to make tmp files exclusively user read/write-able
+ */
+/*#define MODE_READONLY (S_IREAD | S_IWRITE) */
+
+
+#ifdef maindef
+/* possible names and paths of help files under different OSs */
+
+char *pathname[] = {
+ "picorc",
+ "pico.hlp",
+ "\\usr\\local\\",
+ "\\usr\\lib\\",
+ ""
+};
+
+#define NPNAMES (sizeof(pathname)/sizeof(char *))
+
+
+#endif
+
+
+/* Define function that mswin.c calls back for scrolling. */
+int pico_scroll_callback ();
+
+
+#include "mswin.h"
+#include "msmenu.h"
+
+/*
+ * Make sys_errlist visible
+ */
+/* extern char *sys_errlist[]; */
+/* extern int sys_nerr; */
+
+
+#endif /* _PICO_OS_INCLUDED */
diff --git a/pico/osdep/os-wnt.h b/pico/osdep/os-wnt.h
new file mode 100644
index 00000000..a1eb9c3d
--- /dev/null
+++ b/pico/osdep/os-wnt.h
@@ -0,0 +1,181 @@
+#ifndef _PICO_OS_INCLUDED
+#define _PICO_OS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+
+ OS dependencies, WIN NT version. See also the os-wnt.c files.
+ The following stuff may need to be changed for a new port, but once
+ the port is done it won't change. Further down in the file are a few
+ constants that you may want to configure differently than they
+ are configured, but probably not.
+
+ ----*/
+
+
+
+/*----------------- Are we ANSI? ---------------------------------------*/
+#define ANSI /* this is an ANSI compiler */
+
+/*------ If our compiler doesn't understand type void ------------------*/
+/* #define void char */
+
+/*-------- Standard ANSI functions usually defined in stdlib.h ---------*/
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <direct.h>
+#include <search.h>
+#undef CTRL
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _WIN32_WINNT WINVER
+
+/*-------- Standard windows defines and then our window module defines. */
+#include <windows.h>
+#include <limits.h>
+#include <commdlg.h>
+#include <cderr.h>
+#include <winsock.h>
+#include <shellapi.h>
+
+#include "mswin.h"
+
+#include <io.h>
+#include <sys/stat.h>
+#include <winsock.h>
+#include <dos.h>
+#include <direct.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+
+
+/* Windows only version and resource defines. */
+#include "resource.h"
+
+
+#undef ERROR
+
+
+/*---- Declare sys_errlist() if not already declared -------------------*/
+/* extern char *sys_errlist[]; */
+
+
+
+/*----------------- locale.h -------------------------------------------*/
+#include <locale.h> /* To make matching and sorting work right */
+#define collator strcoll
+
+
+
+/*----------------- time.h ---------------------------------------------*/
+#include <time.h>
+/* plain time.h isn't enough on some systems */
+/* #include <sys/time.h> */ /* For struct timeval usually in time.h */
+
+
+
+/*--------------- signal.h ---------------------------------------------*/
+#include <signal.h>
+/* #include <sys/signal.h> */
+
+#define SigType void /* value returned by sig handlers is void */
+/* #define SigType int */ /* value returned by sig handlers is int */
+
+/* #define POSIX_SIGNALS */
+/* #define SYSV_SIGNALS */ /* use System-V signal semantics (ttyin.c) */
+
+#define SIG_PROTO(args) args
+
+
+
+/*-------------- A couple typedef's for integer sizes ------------------*/
+typedef unsigned long usign32_t;
+typedef unsigned short usign16_t;
+
+
+
+/*-------------- qsort argument type -----------------------------------*/
+#define QSType void
+/* #define QSType char */
+
+#define QcompType const void
+
+
+/*-------- Is window resizing available? -------------------------------*/
+/* #define RESIZING */
+ /* Actually, under windows it is, but RESIZING compiles in UNIX
+ * signals code for determining when the window resized. Window's
+ * works differently. */
+
+
+
+
+/*-------- If no vfork, use regular fork -------------------------------*/
+/* #define vfork fork */
+
+
+
+/*---- When no screen size can be discovered this is the size used -----*/
+#define DEFAULT_LINES_ON_TERMINAL (25)
+#define DEFAULT_COLUMNS_ON_TERMINAL (80)
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+
+#define ftruncate chsize
+
+
+/*
+ * File name separators, char and string
+ */
+#define C_FILESEP '\\'
+#define S_FILESEP "\\"
+
+
+/*
+ * What and where the tool that checks spelling is located. If this is
+ * undefined, then the spelling checker is not compiled into pico.
+ */
+#define SPELLER
+
+
+/*
+ * Mode passed chmod() to make tmp files exclusively user read/write-able
+ */
+/*#define MODE_READONLY (S_IREAD | S_IWRITE) */
+
+
+#ifdef maindef
+/* possible names and paths of help files under different OSs */
+
+char *pathname[] = {
+ "picorc",
+ "pico.hlp",
+ "\\usr\\local\\",
+ "\\usr\\lib\\",
+ ""
+};
+
+#define NPNAMES (sizeof(pathname)/sizeof(char *))
+
+
+#endif
+
+
+#include "mswin.h"
+#include "msmenu.h"
+
+/* memmove() is a built-in for DOS/Windows */
+#define bcopy(a,b,s) memmove (b, a, s)
+
+/*
+ * Make sys_errlist visible
+ */
+/* extern char *sys_errlist[]; */
+/* extern int sys_nerr; */
+
+
+#endif /* _PICO_OS_INCLUDED */
diff --git a/pico/osdep/pico.ico b/pico/osdep/pico.ico
new file mode 100644
index 00000000..b78a05ad
--- /dev/null
+++ b/pico/osdep/pico.ico
Binary files differ
diff --git a/pico/osdep/popen.c b/pico/osdep/popen.c
new file mode 100644
index 00000000..575929a6
--- /dev/null
+++ b/pico/osdep/popen.c
@@ -0,0 +1,68 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: popen.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+#include "../estruct.h"
+
+
+
+
+/*
+ * P_open - run the given command in a sub-shell returning a file pointer
+ * from which to read the output
+ *
+ * note:
+ * For OS's other than unix, you will have to rewrite this function.
+ * Hopefully it'll be easy to exec the command into a temporary file,
+ * and return a file pointer to that opened file or something.
+ */
+int
+P_open(char *s)
+{
+#if HAVE_POPEN
+ extern FIOINFO g_pico_fio;
+
+ g_pico_fio.flags = FIOINFO_READ;
+ g_pico_fio.name = "pipe";
+
+ if((g_pico_fio.fp = popen(s, "r")) != NULL)
+ return(FIOSUC);
+
+ return(FIOERR);
+#else
+ /* Windows never did this, but piping has been done elsewhere */
+ return(0);
+#endif
+}
+
+
+
+/*
+ * P_close - close the given descriptor
+ *
+ */
+void
+P_close(void)
+{
+#if HAVE_PCLOSE
+ extern FIOINFO g_pico_fio;
+
+ if(g_pico_fio.fp)
+ (void) pclose(g_pico_fio.fp);
+#endif
+}
diff --git a/pico/osdep/popen.h b/pico/osdep/popen.h
new file mode 100644
index 00000000..6e7f686d
--- /dev/null
+++ b/pico/osdep/popen.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: popen.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_POPEN_INCLUDED
+#define PICO_OSDEP_POPEN_INCLUDED
+
+
+/* exported prototypes */
+int P_open(char *);
+void P_close(void);
+
+
+#endif /* PICO_OSDEP_POPEN_INCLUDED */
diff --git a/pico/osdep/raw.c b/pico/osdep/raw.c
new file mode 100644
index 00000000..a886e0b6
--- /dev/null
+++ b/pico/osdep/raw.c
@@ -0,0 +1,449 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: raw.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * TTY setup routines.
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+
+#include "raw.h"
+
+
+#if HAS_TERMIOS
+/*
+ * These are the TERMIOS-style (POSIX) routines.
+ */
+
+static struct termios _raw_tty, _original_tty;
+
+#elif HAS_TERMIO
+/*
+ * These are the TERMIO-style (System V) routines.
+ */
+
+static struct termio _raw_tty, _original_tty;
+
+#else /* RAW_BSD */
+/*
+ * These are for the BSD-style routines.
+ */
+
+static struct sgttyb _raw_tty, _original_tty;
+static struct ltchars _raw_ltchars, _original_ltchars;
+static struct tchars _raw_tchars, _original_tchars;
+static int _raw_lmode, _original_lmode;
+
+#endif
+
+/*
+ * current raw state
+ */
+static short _inraw = 0;
+
+/*
+ * Set up the tty driver
+ *
+ * Args: state -- which state to put it in. 1 means go into raw (cbreak),
+ * 0 out of raw.
+ *
+ * Result: returns 0 if successful and -1 if not.
+ */
+int
+Raw(int state)
+{
+ /** state is either ON or OFF, as indicated by call **/
+ /* Check return code only on first call. If it fails we're done for and
+ if it goes OK the others will probably go OK too. */
+
+ if(state == 0 && _inraw){
+ /*----- restore state to original -----*/
+
+#if HAS_TERMIOS
+
+ if(tcsetattr(STDIN_FD, TCSADRAIN, &_original_tty) < 0)
+ return -1;
+
+#elif HAS_TERMIO
+
+ if(ioctl(STDIN_FD, TCSETAW, &_original_tty) < 0)
+ return(-1);
+
+#else /* RAW_BSD */
+
+ if(ioctl(STDIN_FD, TIOCSETP, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TIOCSLTC, &_original_ltchars);
+ (void)ioctl(STDIN_FD, TIOCSETC, &_original_tchars);
+ (void)ioctl(STDIN_FD, TIOCLSET, &_original_lmode);
+
+#endif
+
+ _inraw = 0;
+ }
+ else if(state == 1 && ! _inraw){
+ /*----- Go into raw mode (cbreak actually) ----*/
+
+#if HAS_TERMIOS
+
+ if(tcgetattr(STDIN_FD, &_original_tty) < 0)
+ return -1;
+
+ tcgetattr(STDIN_FD, &_raw_tty);
+ _raw_tty.c_lflag &= ~(ICANON | ECHO | IEXTEN); /* noecho raw mode */
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ _raw_tty.c_iflag &= ~ICRNL; /* turn off CR->NL on input */
+ _raw_tty.c_oflag &= ~ONLCR; /* turn off NL->CR on output */
+
+ _raw_tty.c_cc[VMIN] = '\01'; /* min # of chars to queue */
+ _raw_tty.c_cc[VTIME] = '\0'; /* min time to wait for input*/
+ _raw_tty.c_cc[VINTR] = ctrl('C'); /* make it our special char */
+ _raw_tty.c_cc[VQUIT] = 0;
+ _raw_tty.c_cc[VSUSP] = 0;
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ if(ioctl(STDIN_FD, TCGETA, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TCGETA, &_raw_tty); /** again! **/
+ _raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ _raw_tty.c_iflag &= ~ICRNL; /* turn off CR->NL on input */
+ _raw_tty.c_oflag &= ~ONLCR; /* turn off NL->CR on output */
+ _raw_tty.c_cc[VMIN] = 1; /* min # of chars to queue */
+ _raw_tty.c_cc[VTIME] = 0; /* min time to wait for input*/
+ _raw_tty.c_cc[VINTR] = ctrl('C'); /* make it our special char */
+ _raw_tty.c_cc[VQUIT] = 0;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+ if(ioctl(STDIN_FD, TIOCGETP, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TIOCGETP, &_raw_tty);
+ (void)ioctl(STDIN_FD, TIOCGETC, &_original_tchars);
+ (void)ioctl(STDIN_FD, TIOCGETC, &_raw_tchars);
+ (void)ioctl(STDIN_FD, TIOCGLTC, &_original_ltchars);
+ (void)ioctl(STDIN_FD, TIOCGLTC, &_raw_ltchars);
+ (void)ioctl(STDIN_FD, TIOCLGET, &_original_lmode);
+ (void)ioctl(STDIN_FD, TIOCLGET, &_raw_lmode);
+
+ _raw_tty.sg_flags &= ~(ECHO); /* echo off */
+ _raw_tty.sg_flags |= CBREAK; /* raw on */
+ _raw_tty.sg_flags &= ~CRMOD; /* Turn off CR -> LF mapping */
+
+ _raw_tchars.t_intrc = -1; /* Turn off ^C and ^D */
+ _raw_tchars.t_eofc = -1;
+
+ _raw_ltchars.t_lnextc = -1; /* Turn off ^V so we can use it */
+ _raw_ltchars.t_dsuspc = -1; /* Turn off ^Y so we can use it */
+ _raw_ltchars.t_suspc = -1; /* Turn off ^Z; we just read 'em */
+ _raw_ltchars.t_werasc = -1; /* Turn off ^w word erase */
+ _raw_ltchars.t_rprntc = -1; /* Turn off ^R reprint line */
+ _raw_ltchars.t_flushc = -1; /* Turn off ^O output flush */
+
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ (void)ioctl(STDIN_FD, TIOCSLTC, &_raw_ltchars);
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ (void)ioctl(STDIN_FD, TIOCLSET, &_raw_lmode);
+#endif
+ _inraw = 1;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Set up the tty driver to use XON/XOFF flow control
+ *
+ * Args: state -- True to make sure XON/XOFF turned on, FALSE off.
+ *
+ * Result: none.
+ */
+void
+xonxoff_proc(int state)
+{
+ if(_inraw){
+ if(state){
+#if HAS_TERMIOS
+
+ if(!(_raw_tty.c_iflag & IXON)){
+ _raw_tty.c_iflag |= IXON;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(!(_raw_tty.c_iflag & IXON)){
+ _raw_tty.c_iflag |= IXON; /* turn ON ^S/^Q on input */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+
+ if(_raw_tchars.t_startc == -1 || _raw_tchars.t_stopc == -1){
+ _raw_tchars.t_startc = 'Q' - '@'; /* Turn ON ^S/^Q */
+ _raw_tchars.t_stopc = 'S' - '@';
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ }
+
+#endif
+ }
+ else{
+#if HAS_TERMIOS
+
+ if(_raw_tty.c_iflag & IXON){
+ _raw_tty.c_iflag &= ~IXON; /* turn off ^S/^Q on input */
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(_raw_tty.c_iflag & IXON){
+ _raw_tty.c_iflag &= ~IXON; /* turn off ^S/^Q on input */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+ if(!(_raw_tchars.t_startc == -1 && _raw_tchars.t_stopc == -1)){
+ _raw_tchars.t_startc = -1; /* Turn off ^S/^Q */
+ _raw_tchars.t_stopc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ }
+#endif
+ }
+ }
+}
+
+
+/*
+ * Set up the tty driver to do LF->CR translation
+ *
+ * Args: state -- True to turn on translation, false to write raw LF's
+ *
+ * Result: none.
+ */
+void
+crlf_proc(int state)
+{
+ if(_inraw){
+ if(state){ /* turn ON NL->CR on output */
+#if HAS_TERMIOS
+
+ if(!(_raw_tty.c_oflag & ONLCR)){
+ _raw_tty.c_oflag |= ONLCR;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(!(_raw_tty.c_oflag & ONLCR)){
+ _raw_tty.c_oflag |= ONLCR;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+ if(!(_raw_tty.sg_flags & CRMOD)){
+ _raw_tty.sg_flags |= CRMOD;
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ }
+#endif
+ }
+ else{ /* turn OFF NL-CR on output */
+#if HAS_TERMIOS
+
+ if(_raw_tty.c_oflag & ONLCR){
+ _raw_tty.c_oflag &= ~ONLCR;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(_raw_tty.c_oflag & ONLCR){
+ _raw_tty.c_oflag &= ~ONLCR;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+
+ if(_raw_tty.sg_flags & CRMOD){
+ _raw_tty.sg_flags &= ~CRMOD;
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ }
+
+#endif
+ }
+ }
+}
+
+
+/*
+ * Set up the tty driver to hanle interrupt char
+ *
+ * Args: state -- True to turn on interrupt char, false to not
+ *
+ * Result: tty driver that'll send us SIGINT or not
+ */
+void
+intr_proc(int state)
+{
+ if(_inraw){
+ if(state){
+#if HAS_TERMIOS
+
+ _raw_tty.c_lflag |= ISIG; /* enable signals */
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ _raw_tty.c_lflag |= ISIG; /* enable signals */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+
+ _raw_tchars.t_intrc = ctrl('C');
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+
+#endif
+ }
+ else{
+#if HAS_TERMIOS
+
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+ _raw_tchars.t_intrc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+#endif
+ }
+ }
+}
+
+
+/*
+ * Discard any pending input characters
+ *
+ * Args: none
+ *
+ * Result: pending input buffer flushed
+ */
+void
+flush_input(void)
+{
+#if HAS_TERMIOS
+
+ tcflush(STDIN_FD, TCIFLUSH);
+
+#elif HAS_TERMIO
+
+ ioctl(STDIN_FD, TCFLSH, 0);
+
+#else /* RAW_BSD */
+
+#ifdef TIOCFLUSH
+#ifdef FREAD
+ int i = FREAD;
+#else
+ int i = 1;
+#endif
+ ioctl(STDIN_FD, TIOCFLUSH, &i);
+#endif /* TIOCFLUSH */
+
+#endif
+}
+
+
+/*
+ * Turn off hi bit stripping
+ */
+void
+bit_strip_off(void)
+{
+#if HAS_TERMIOS
+
+ _raw_tty.c_iflag &= ~ISTRIP;
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ /* no op */
+
+#else /* RAW_BSD */
+
+#ifdef LPASS8
+ _raw_lmode |= LPASS8;
+#endif
+
+#ifdef LPASS8OUT
+ _raw_lmode |= LPASS8OUT;
+#endif
+
+ (void)ioctl(STDIN_FD, TIOCLSET, &_raw_lmode);
+
+#endif
+}
+
+
+/*
+ * Turn off quit character (^\) if possible
+ */
+void
+quit_char_off(void)
+{
+#if HAS_TERMIOS
+
+#elif HAS_TERMIO
+
+#else /* RAW_BSD */
+ _raw_tchars.t_quitc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+#endif
+}
+
+
+/*
+ * Returns TRUE if tty is < 4800, 0 otherwise.
+ */
+int
+ttisslow(void)
+{
+#if HAS_TERMIOS
+
+ return(cfgetospeed(&_raw_tty) < B4800);
+
+#elif HAS_TERMIO
+
+ return((_raw_tty.c_cflag&CBAUD) < (unsigned int)B4800);
+
+#else /* RAW_BSD */
+
+ return((int)_raw_tty.sg_ispeed < B4800);
+
+#endif
+}
diff --git a/pico/osdep/raw.h b/pico/osdep/raw.h
new file mode 100644
index 00000000..cd5fa203
--- /dev/null
+++ b/pico/osdep/raw.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: raw.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_RAW_INCLUDED
+#define PICO_OSDEP_RAW_INCLUDED
+
+
+/* useful definitions */
+#define STDIN_FD 0
+#define STDOUT_FD 1
+#define STDERR_FD 2
+
+
+/* exported prototypes */
+int Raw(int);
+void bit_strip_off(void);
+void quit_char_off(void);
+int ttisslow(void);
+void xonxoff_proc(int);
+void flush_input(void);
+void crlf_proc(int);
+void intr_proc(int);
+
+
+#endif /* PICO_OSDEP_RAW_INCLUDED */
diff --git a/pico/osdep/read.c b/pico/osdep/read.c
new file mode 100644
index 00000000..52fe92e5
--- /dev/null
+++ b/pico/osdep/read.c
@@ -0,0 +1,223 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: read.c 763 2007-10-23 23:37:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Keyboard input test and read functions
+ */
+
+#include <system.h> /* os-dep defs/includes */
+#include <general.h> /* generally useful definitions */
+
+#include "../keydefs.h"
+
+#ifndef _WINDOWS
+#include "raw.h"
+#endif /* !_WINDOWS */
+
+#include "read.h"
+
+
+static time_t _time_of_last_input;
+
+time_t
+time_of_last_input(void)
+{
+ if(_time_of_last_input == 0)
+ _time_of_last_input = time((time_t *) 0);
+
+ return(_time_of_last_input);
+}
+
+#ifndef _WINDOWS
+#if HAVE_SELECT
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+
+/*
+ * Check whether or not a character is ready to be read, or time out.
+ * This version uses a select call.
+ *
+ * Args: time_out -- number of seconds before it will time out.
+ *
+ * Result: NO_OP_IDLE: timed out before any chars ready, time_out > 25
+ * NO_OP_COMMAND: timed out before any chars ready, time_out <= 25
+ * READ_INTR: read was interrupted
+ * READY_TO_READ: input is available
+ * BAIL_OUT: reading input failed, time to bail gracefully
+ * PANIC_NOW: system call error, die now
+ */
+UCS
+input_ready(int time_out)
+{
+ struct timeval tmo;
+ fd_set readfds, errfds;
+ int res;
+
+ fflush(stdout);
+
+ if(time_out > 0){
+ /* Check to see if there are bytes to read with a timeout */
+ FD_ZERO(&readfds);
+ FD_ZERO(&errfds);
+ FD_SET(STDIN_FD, &readfds);
+ FD_SET(STDIN_FD, &errfds);
+ tmo.tv_sec = time_out;
+ tmo.tv_usec = 0;
+ res = select(STDIN_FD+1, &readfds, 0, &errfds, &tmo);
+ if(res < 0){
+ if(errno == EINTR || errno == EAGAIN)
+ return(READ_INTR);
+
+ return(BAIL_OUT);
+ }
+
+ if(res == 0){ /* the select timed out */
+#ifndef __CYGWIN__
+ if(getppid() == 1){
+ /* Parent is init! */
+ return(BAIL_OUT);
+ }
+#endif
+
+ /*
+ * "15" is the minimum allowed mail check interval.
+ * Anything less, and we're being told to cycle thru
+ * the command loop because some task is pending...
+ */
+ return(time_out < IDLE_TIMEOUT ? NO_OP_COMMAND : NO_OP_IDLE);
+ }
+ }
+
+ _time_of_last_input = time((time_t *) 0);
+ return(READY_TO_READ);
+}
+
+
+#elif HAVE_POLL
+
+
+#ifdef HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+
+#ifdef HAVE_SYS_POLL_H
+# include <poll.h>
+#endif
+
+/*
+ * Check whether or not a character is ready to be read, or time out.
+ * This version uses a poll call.
+ *
+ * Args: time_out -- number of seconds before it will time out.
+ *
+ * Result: NO_OP_IDLE: timed out before any chars ready, time_out > 25
+ * NO_OP_COMMAND: timed out before any chars ready, time_out <= 25
+ * READ_INTR: read was interrupted
+ * READY_TO_READ: input is available
+ * BAIL_OUT: reading input failed, time to bail gracefully
+ * PANIC_NOW: system call error, die now
+ */
+UCS
+input_ready(int time_out)
+{
+ struct pollfd pollfd;
+ int res;
+
+ fflush(stdout);
+
+ if(time_out > 0){
+ /* Check to see if there are bytes to read with a timeout */
+ pollfd.fd = STDIN_FD;
+ pollfd.events = POLLIN;
+ res = poll (&pollfd, 1, time_out * 1000);
+ if(res >= 0){ /* status bits OK? */
+ if(pollfd.revents & (POLLERR | POLLNVAL))
+ res = -1; /* bad news, exit below! */
+ else if(pollfd.revents & POLLHUP)
+ return(BAIL_OUT);
+ }
+
+ if(res < 0){
+ if(errno == EINTR || errno == EAGAIN)
+ return(READ_INTR);
+
+ return(PANIC_NOW);
+ }
+
+ if(res == 0){ /* the select timed out */
+ if(getppid() == 1){
+ /* Parent is init! */
+ return(BAIL_OUT);
+ }
+
+ /*
+ * "15" is the minimum allowed mail check interval.
+ * Anything less, and we're being told to cycle thru
+ * the command loop because some task is pending...
+ */
+ return(time_out < IDLE_TIMEOUT ? NO_OP_COMMAND : NO_OP_IDLE);
+ }
+ }
+
+ _time_of_last_input = time((time_t *) 0);
+ return(READY_TO_READ);
+}
+
+#endif /* HAVE_POLL */
+
+
+/*
+ * Read one character from STDIN.
+ *
+ * Result: -- the single character read
+ * READ_INTR -- read was interrupted
+ * BAIL_OUT -- read error of some sort
+ */
+int
+read_one_char(void)
+{
+ int res;
+ unsigned char c;
+
+ res = read(STDIN_FD, &c, 1);
+
+ if(res <= 0){
+ /*
+ * Error reading from terminal!
+ * The only acceptable failure is being interrupted. If so,
+ * return a value indicating such...
+ */
+ if(res < 0 && errno == EINTR)
+ return(READ_INTR);
+ else
+ return(BAIL_OUT);
+ }
+
+ return((int)c);
+}
+
+#else /* _WINDOWS */
+int
+set_time_of_last_input(void)
+{
+ _time_of_last_input = time(0L);
+ return(0);
+}
+#endif /* _WINDOWS */
diff --git a/pico/osdep/read.h b/pico/osdep/read.h
new file mode 100644
index 00000000..2ba6c631
--- /dev/null
+++ b/pico/osdep/read.h
@@ -0,0 +1,35 @@
+/*
+ * $Id: read.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_READ_INCLUDED
+#define PICO_OSDEP_READ_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+#ifndef _WINDOWS
+UCS input_ready(int);
+int read_one_char(void);
+#else /* _WINDOWS */
+int set_time_of_last_input(void);
+#endif /* _WINDOWS */
+
+time_t time_of_last_input(void);
+
+
+#endif /* PICO_OSDEP_READ_INCLUDED */
diff --git a/pico/osdep/resource.h b/pico/osdep/resource.h
new file mode 100644
index 00000000..4796f16a
--- /dev/null
+++ b/pico/osdep/resource.h
@@ -0,0 +1,189 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by mswin.rc
+//
+#define IDD_OK 1
+#define ABOUTDLGBOX 100
+#define SPLASHDLGBOX 101
+#define IDM_OPT_SETFONT 103
+#define IDM_ABOUT 104
+#define IDM_EDIT_CUT 105
+#define IDM_EDIT_COPY 106
+#define IDM_EDIT_PASTE 107
+#define TEXTWINMENU 108
+#define IDM_EDIT_CANCEL_PASTE 109
+#define IDM_HELP 110
+#define IDD_TOOLBAR 110
+#define IDM_EDIT_COPY_APPEND 111
+#define IDD_COMPOSER_TB 111
+#define IDM_FILE_EXIT 112
+#define IDD_OPTIONALYENTER 112
+#define IDM_OPT_FONTSAMEAS 113
+#define IDD_SELECT 113
+#define IDM_OPT_SETPRINTFONT 114
+#define IDD_SELECTSORT 114
+#define IDM_FILE_CLOSE 115
+#define IDR_ACCEL_PINE 115
+#define IDD_SELECTFLAG 115
+#define IDM_FILE_PRINT 116
+#define IDD_LOGINDLG 116
+#define IDM_OPT_TOOLBAR 117
+#define IDD_CONFIGDLG 117
+#define IDM_OPT_TOOLBARPOS 118
+#define IDD_CFVARSDLG 118
+#define IDM_OPT_USEDIALOGS 119
+#define IDD_LOGINDLG2 119
+#define IDM_OPT_USEACCEL 120
+#define IDM_OPT_IMAPTELEM 121
+#define IDM_EDIT_SEL_ALL 122
+#define IDM_MI_SORTSUBJECT 123
+#define IDM_MI_SORTARRIVAL 124
+#define IDM_MI_SORTFROM 125
+#define IDM_MI_SORTTO 126
+#define IDM_MI_SORTCC 127
+#define IDM_MI_SORTDATE 128
+#define IDM_MI_SORTSIZE 129
+#define IDM_MI_SORTORDERSUB 130
+#define IDM_MI_SORTSCORE 131
+#define IDM_MI_SORTTHREAD 132
+#define IDM_MI_SORTREVERSE 133
+#define IDM_MI_FLAGIMPORTANT 134
+#define IDM_MI_FLAGNEW 135
+#define IDM_MI_FLAGANSWERED 136
+#define IDM_MI_FLAGDELETED 137
+
+// This group must all be consecutive.
+// These are all regular commands that end
+// up in menus at the top of the screen and
+// there are a couple checks for the range (see msmenu.h).
+#define IDM_MI_VIEW 150
+#define IDM_MI_EXPUNGE 151
+#define IDM_MI_ZOOM 152
+#define IDM_MI_SORT 153
+#define IDM_MI_HDRMODE 154
+#define IDM_MI_MAINMENU 155
+#define IDM_MI_FLDRLIST 156
+#define IDM_MI_FLDRINDEX 157
+#define IDM_MI_COMPOSER 158
+#define IDM_MI_PREVPAGE 159
+#define IDM_MI_PREVMSG 160
+#define IDM_MI_NEXTMSG 161
+#define IDM_MI_ADDRBOOK 162
+#define IDM_MI_WHEREIS 163
+#define IDM_MI_PRINT 164
+#define IDM_MI_REPLY 165
+#define IDM_MI_FORWARD 166
+#define IDM_MI_BOUNCE 167
+#define IDM_MI_DELETE 168
+#define IDM_MI_UNDELETE 169
+#define IDM_MI_FLAG 170
+#define IDM_MI_SAVE 171
+#define IDM_MI_EXPORT 172
+#define IDM_MI_TAKEADDR 173
+#define IDM_MI_SELECT 174
+#define IDM_MI_APPLY 175
+#define IDM_MI_POSTPONE 176
+#define IDM_MI_SEND 177
+#define IDM_MI_CANCEL 178
+#define IDM_MI_ATTACH 179
+#define IDM_MI_TOADDRBOOK 180
+#define IDM_MI_READFILE 181
+#define IDM_MI_JUSTIFY 182
+#define IDM_MI_ALTEDITOR 183
+#define IDM_MI_GENERALHELP 184
+#define IDM_MI_SCREENHELP 185
+#define IDM_MI_EXIT 186
+#define IDM_MI_NEXTPAGE 187
+#define IDM_MI_SAVEFILE 188
+#define IDM_MI_CURPOSITION 189
+#define IDM_MI_GOTOFLDR 190
+#define IDM_MI_JUMPTOMSG 191
+#define IDM_MI_RICHHDR 192
+#define IDM_MI_EXITMODE 193
+#define IDM_MI_REVIEW 194
+#define IDM_MI_KEYMENU 195
+#define IDM_MI_SELECTCUR 196
+#define IDM_MI_UNDO 197
+#define IDM_MI_SPELLCHK 198
+
+#define IDD_ARGLIST 199
+#define IDM_OPT_CARETBLOCK 200
+#define IDM_OPT_CARETSMALLBLOCK 201
+#define IDM_OPT_CARETHBAR 202
+#define IDM_OPT_CARETVBAR 203
+#define IDM_OPT_NEWMAILWIN 204
+#define IDM_EDIT_CLEAR 205
+#define IDM_OPT_ERASE_CREDENTIALS 205
+#define IDM_MI_VIEWINWIND 206
+#define ALPINEMENU 300
+#define COMPOSERMENU 301
+#define ALPINEICON 400
+#define NEWMAILICON 401
+#define PICOHAND 402
+#define MCLOSEDICON 403
+#define ALPINESPLASH 501
+#define IDD_ABOUTICON 0x210
+#define IDD_VERSION 0x212
+#define IDD_BYLINE 0x213
+#define IDS_BYLINE 773
+#define IDS_APPNAME 774
+#define IDS_APPIDENT 775
+#define IDC_RESPONCE 1002
+#define IDC_PROMPT 1003
+#define IDC_GETHELP 1004
+#define IDC_SORTARRIVAL 1005
+#define IDC_SORTFIRSTBUTTON IDC_SORTARRIVAL
+#define IDC_SORTORDERSUB 1006
+#define IDC_SORTSUBJECT 1007
+#define IDC_SORTSIZE 1008
+#define IDC_SORTCC 1009
+#define IDC_SORTTO 1010
+#define IDC_SORTFROM 1011
+#define IDC_SORTDATE 1012
+#define IDC_SORTTHREAD 1013
+#define IDC_SORTSCORE 1014
+#define IDC_SORTLASTBUTTON IDC_SORTSCORE
+#define IDC_SORTREVERSE 1015
+#define IDC_FLAGCOL1 1016
+#define IDC_FLAGCOL2 1017
+#define IDC_BUTTON2 1018
+#define IDC_ARGTEXT 1019
+#define IDC_RLOGINE 1020
+#define IDC_RPASSWORD 1021
+#define IDC_RPWTEXT 1022
+#define IDC_PRESPASS 1023
+#define IDC_CONFRRADIO 1023
+#define IDC_CFV_PNAME 1023
+#define IDC_CONFLRADIO 1024
+#define IDC_CFV_EMAILADR 1024
+#define IDC_CONFSRVRTXT 1025
+#define IDC_CFV_MSERVER 1025
+#define IDC_CONFESERVER 1026
+#define IDC_CFV_IMAP 1026
+#define IDC_CONFUNTXT 1027
+#define IDC_CFV_POP3 1027
+#define IDC_CONFEUSERNAME 1028
+#define IDC_CFV_LOGIN 1028
+#define IDC_CONFDFLTFLDR 1029
+#define IDC_CFV_SMTPSERVER 1029
+#define IDC_CONFFLDRTXT 1030
+#define IDC_CFV_DEFMAILER 1030
+#define IDC_CONFEFLDRNAME 1031
+#define IDC_CFV_DEFNEWSRDR 1031
+#define IDC_CONFFNTXT 1032
+#define IDC_CONFEFN 1033
+#define IDC_CONFBROWSE 1034
+#define IDC_CONFDFLTSET 1035
+#define IDC_CONFTEXT 1036
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 116
+#define _APS_NEXT_COMMAND_VALUE 122
+#define _APS_NEXT_CONTROL_VALUE 1023
+#define _APS_NEXT_SYMED_VALUE 103
+#endif
+#endif
diff --git a/pico/osdep/shell.c b/pico/osdep/shell.c
new file mode 100644
index 00000000..cdb701dd
--- /dev/null
+++ b/pico/osdep/shell.c
@@ -0,0 +1,171 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: shell.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "tty.h"
+#include "signals.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "shell.h"
+
+
+
+/* internal prototypes */
+#ifdef SIGCONT
+RETSIGTYPE rtfrmshell(int);
+#endif
+
+
+
+#ifdef SIGTSTP
+/*
+ * bktoshell - suspend and wait to be woken up
+ */
+int
+bktoshell(int f, int n)
+{
+ UCS z = CTRL | 'Z';
+
+ if(!(gmode&MDSSPD)){
+ unknown_command(z);
+ return(FALSE);
+ }
+
+ if(Pmaster){
+ if(!Pmaster->suspend){
+ unknown_command(z);
+ return(FALSE);
+ }
+
+ if((*Pmaster->suspend)() == NO_OP_COMMAND){
+ int rv;
+
+ if(km_popped){
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)('x');
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the display? */
+ pico_refresh(0, 1); /* Yes, repaint */
+
+ mpresf = 1;
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+ }
+ else{
+ ttresize();
+ pclear(0, term.t_nrow);
+ pico_refresh(0, 1);
+ }
+
+ return(TRUE);
+ }
+
+ if(gmode&MDSPWN){
+ char *shell;
+ int dummy;
+
+ vttidy();
+ movecursor(0, 0);
+ (*term.t_eeop)();
+ printf("\n\n\nUse \"exit\" to return to Pi%s\n",
+ (gmode & MDBRONLY) ? "lot" : "co");
+ system((shell = (char *)getenv("SHELL")) ? shell : "/bin/csh");
+ rtfrmshell(dummy); /* fixup tty */
+ }
+ else {
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ printf("\n\n\nUse \"fg\" to return to Pi%s\n",
+ (gmode & MDBRONLY) ? "lot" : "co");
+ ttclose();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ (*term.t_flush)();
+
+ signal(SIGCONT, rtfrmshell); /* prepare to restart */
+ signal(SIGTSTP, SIG_DFL); /* prepare to stop */
+ kill(0, SIGTSTP);
+ }
+
+ return(TRUE);
+}
+#else
+#ifdef _WINDOWS
+int
+bktoshell(int f, int n)
+{
+ UCS z = CTRL | 'Z';
+
+ if(!(gmode&MDSSPD)){
+ unknown_command(z);
+ return(FALSE);
+ }
+ if(Pmaster){
+ if(!Pmaster->suspend){
+ unknown_command(z);
+ return(FALSE);
+ }
+ (*Pmaster->suspend)();
+ return(TRUE);
+ }
+
+ mswin_minimize();
+ return(TRUE);
+}
+#endif /* _WINDOWS */
+
+#endif /* SIGTSTP */
+
+#ifdef SIGCONT
+/*
+ * rtfrmshell - back from shell, fix modes and return
+ */
+RETSIGTYPE
+rtfrmshell(int sig)
+{
+ signal(SIGCONT, SIG_DFL);
+ ttopen();
+ ttresize();
+ pclear(0, term.t_nrow);
+ pico_refresh(0, 1);
+}
+#endif /* SIGCONT */
diff --git a/pico/osdep/shell.h b/pico/osdep/shell.h
new file mode 100644
index 00000000..f356485f
--- /dev/null
+++ b/pico/osdep/shell.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: shell.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SHELL_INCLUDED
+#define PICO_OSDEP_SHELL_INCLUDED
+
+
+/* exported prototypes */
+#if defined(SIGTSTP) || defined(_WINDOWS)
+int bktoshell(int, int);
+#endif
+
+
+#endif /* PICO_OSDEP_SHELL_INCLUDED */
diff --git a/pico/osdep/signals.c b/pico/osdep/signals.c
new file mode 100644
index 00000000..10f02ba1
--- /dev/null
+++ b/pico/osdep/signals.c
@@ -0,0 +1,181 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signals.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "tty.h"
+
+#include "signals.h"
+
+
+/* internal prototypes */
+#ifndef _WINDOWS
+RETSIGTYPE do_hup_signal(int);
+#endif
+
+
+/*
+ * picosigs - Install any handlers for the signals we're interested
+ * in catching.
+ */
+void
+picosigs(void)
+{
+#ifndef _WINDOWS
+ signal(SIGHUP, do_hup_signal); /* deal with SIGHUP */
+ signal(SIGTERM, do_hup_signal); /* deal with SIGTERM */
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_DFL);
+#endif
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, winch_handler); /* window size changes */
+#endif
+#endif /* !_WINDOWS */
+}
+
+
+
+
+#ifndef _WINDOWS
+/*
+ * do_hup_signal - jump back in the stack to where we can handle this
+ */
+RETSIGTYPE
+do_hup_signal(int sig)
+{
+ signal(SIGHUP, SIG_IGN); /* ignore further SIGHUP's */
+ signal(SIGTERM, SIG_IGN); /* ignore further SIGTERM's */
+ if(Pmaster){
+ extern jmp_buf finstate;
+
+ longjmp(finstate, COMP_GOTHUP);
+ }
+ else{
+ /*
+ * if we've been interrupted and the buffer is changed,
+ * save it...
+ */
+ if(anycb() == TRUE){ /* time to save */
+ if(curbp->b_fname[0] == '\0'){ /* name it */
+ strncpy(curbp->b_fname, "pico.save", sizeof(curbp->b_fname));
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+ else{
+ strncat(curbp->b_fname, ".save", sizeof(curbp->b_fname)-strlen(curbp->b_fname)-1);
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+
+ our_unlink(curbp->b_fname);
+ writeout(curbp->b_fname, TRUE);
+ }
+
+ vttidy();
+ exit(1);
+ }
+}
+#endif /* !_WINDOWS */
+
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+/*
+ * winch_handler - handle window change signal
+ */
+RETSIGTYPE
+winch_handler(int sig)
+{
+ int i;
+ signal(SIGWINCH, winch_handler);
+ if(wheadp != NULL)
+ ttresize();
+ else if (Pmaster){
+ i = Pmaster->arm_winch_cleanup;
+ Pmaster->arm_winch_cleanup = 1;
+ }
+ if(Pmaster && Pmaster->winch_cleanup && Pmaster->arm_winch_cleanup)
+ (*Pmaster->winch_cleanup)();
+ if(wheadp == NULL && Pmaster != NULL)
+ Pmaster->arm_winch_cleanup = i;
+}
+#endif /* SIGWINCH and friends */
+
+
+
+#ifdef POSIX_SIGNALS
+/*----------------------------------------------------------------------
+ Reset signals after critical imap code
+ ----*/
+void
+(*posix_signal(int sig_num, RETSIGTYPE (*action)(int)))(int)
+{
+ struct sigaction new_action, old_action;
+
+ memset((void *)&new_action, 0, sizeof(struct sigaction));
+ sigemptyset (&new_action.sa_mask);
+ new_action.sa_handler = action;
+#ifdef SA_RESTART
+ new_action.sa_flags = SA_RESTART;
+#else
+ new_action.sa_flags = 0;
+#endif
+ sigaction(sig_num, &new_action, &old_action);
+ return(old_action.sa_handler);
+}
+
+int
+posix_sigunblock(int mask)
+{
+ sigset_t sig_mask;
+
+ sigemptyset(&sig_mask);
+ sigaddset(&sig_mask, mask);
+#if HAVE_PTHREAD
+# define sigprocmask pthread_sigmask
+#endif
+ return(sigprocmask(SIG_UNBLOCK, &sig_mask, NULL));
+}
+#endif /* POSIX_SIGNALS */
+
+
+#if defined(sv3) || defined(ct)
+/* Placed by rll to handle the rename function not found in AT&T */
+int
+rename(char *oldname, char *newname)
+{
+ int rtn;
+ char b[NLINE];
+
+ strncpy(b, fname_to_locale(oldname), sizeof(b));
+ b[sizeof(b)-1] = '\0';
+
+ if ((rtn = link(b, fname_to_locale(newname))) != 0) {
+ perror("Was not able to rename file.");
+ return(rtn);
+ }
+
+ if ((rtn = our_unlink(b)) != 0)
+ perror("Was not able to unlink file.");
+
+ return(rtn);
+}
+#endif
+
diff --git a/pico/osdep/signals.h b/pico/osdep/signals.h
new file mode 100644
index 00000000..2ea33cc4
--- /dev/null
+++ b/pico/osdep/signals.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: signals.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SIGNALS_INCLUDED
+#define PICO_OSDEP_SIGNALS_INCLUDED
+
+
+/*
+ * [Re]Define signal functions as needed...
+ */
+#ifdef POSIX_SIGNALS
+/*
+ * Redefine signal call to our wrapper of POSIX sigaction
+ */
+#define signal(SIG,ACT) posix_signal(SIG,ACT)
+#define our_sigunblock(SIG) posix_sigunblock(SIG)
+#else /* !POSIX_SIGNALS */
+#ifdef SYSV_SIGNALS
+/*
+ * Redefine signal calls to SYSV style call.
+ */
+#define signal(SIG,ACT) sigset(SIG,ACT)
+#define our_sigunblock(SIG) sigrelse(SIG)
+#else /* !SYSV_SIGNALS */
+#ifdef _WINDOWS
+#define our_sigunblock(SIG)
+#else /* !_WINDOWS */
+/*
+ * Good ol' BSD signals.
+ */
+#define our_sigunblock(SIG)
+#endif /* !_WINDOWS */
+#endif /* !SYSV_SIGNALS */
+#endif /* !POSIX_SIGNALS */
+
+
+
+/* exported prototypes */
+void picosigs(void);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+RETSIGTYPE winch_handler(int);
+#endif
+
+#ifdef POSIX_SIGNALS
+void (*posix_signal(int, RETSIGTYPE (*)()))(int);
+int posix_sigunblock(int);
+#endif
+
+#endif /* PICO_OSDEP_SIGNALS_INCLUDED */
diff --git a/pico/osdep/spell.c b/pico/osdep/spell.c
new file mode 100644
index 00000000..87b345eb
--- /dev/null
+++ b/pico/osdep/spell.c
@@ -0,0 +1,323 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: spell.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: spell.c
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+/*
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+*/
+
+#include "../../pith/charconv/filesys.h"
+
+#include "popen.h"
+#include "altedit.h"
+#include "spell.h"
+
+
+#ifdef SPELLER
+
+int movetoword(UCS *);
+
+
+static char *spellhelp[] = {
+/* TRANSLATORS: Some help text. The ~ characters cause
+ the characters they are in front of to be bold. */
+ N_("Spell Check Help"),
+ " ",
+ N_(" The spell checker examines all words in the text. It then"),
+ N_(" offers each misspelled word for correction while simultaneously"),
+ N_(" highlighting it in the text. To leave a word unchanged simply"),
+ N_("~ hit ~R~e~t~u~r~n at the edit prompt. If a word has been corrected,"),
+ N_(" each occurrence of the incorrect word is offered for replacement."),
+ " ",
+ N_("~ Spell checking can be cancelled at any time by typing ~^~C (~F~3)"),
+ N_(" after exiting help."),
+ " ",
+ N_("End of Spell Check Help"),
+ " ",
+ NULL
+};
+
+
+static char *pinespellhelp[] = {
+ N_("Spell Check Help"),
+ " ",
+ N_("\tThe spell checker examines all words in the text. It then"),
+ N_("\toffers each misspelled word for correction while simultaneously"),
+ N_("\thighlighting it in the text. To leave a word unchanged simply"),
+ N_("\thit Return at the edit prompt. If a word has been corrected,"),
+ N_("\teach occurrence of the incorrect word is offered for replacement."),
+ " ",
+ N_("\tSpell checking can be cancelled at any time by typing ^C (F3)"),
+ N_("\tafter exiting help."),
+ " ",
+ N_("End of Spell Check Help"),
+ " ",
+ NULL
+};
+
+#ifndef _WINDOWS
+/*
+ * spell() - check for potentially missspelled words and offer them for
+ * correction
+ */
+int
+spell(int f, int n)
+{
+ int status, next, ret;
+ char ccb[NLINE], *sp, *fn, *lp, *wsp, c, spc[NLINE];
+ UCS *b;
+ UCS wb[NLINE], cb[NLINE];
+ EML eml;
+
+ setimark(0, 1);
+ emlwrite(_("Checking spelling..."), NULL); /* greetings */
+
+ if(alt_speller)
+ return(alt_editor(1, 0)); /* f == 1 means fork speller */
+
+ if((fn = writetmp(0, NULL)) == NULL){
+ emlwrite(_("Can't write temp file for spell checker"), NULL);
+ return(-1);
+ }
+
+ if((sp = (char *)getenv("SPELL")) == NULL)
+ sp = SPELLER;
+
+ /* exists? */
+ ret = (strlen(sp) + 1);
+ snprintf(spc, sizeof(spc), "%s", sp);
+
+ for(lp = spc, ret = FIOERR; *lp; lp++){
+ if((wsp = strpbrk(lp, " \t")) != NULL){
+ c = *wsp;
+ *wsp = '\0';
+ }
+
+ if(strchr(lp, '/')){
+ ret = fexist(lp, "x", (off_t *)NULL);
+ }
+ else{
+ char *path, fname[MAXPATH+1];
+
+ if(!(path = getenv("PATH")))
+ path = ":/bin:/usr/bin";
+
+ ret = ~FIOSUC;
+ while(ret != FIOSUC && *path && pathcat(fname, &path, lp))
+ ret = fexist(fname, "x", (off_t *)NULL);
+ }
+
+ if(wsp)
+ *wsp = c;
+
+ if(ret == FIOSUC)
+ break;
+ }
+
+ if(ret != FIOSUC){
+ eml.s = sp;
+ emlwrite(_("\007Spell-checking file \"%s\" not found"), &eml);
+ return(-1);
+ }
+
+ snprintf(ccb, sizeof(ccb), "( %s ) < %s", sp, fn);
+ if(P_open(ccb) != FIOSUC){ /* read output from command */
+ our_unlink(fn);
+ emlwrite(_("Can't fork spell checker"), NULL);
+ return(-1);
+ }
+
+ ret = 1;
+ while(ffgetline(wb, NLINE, NULL, 0) == FIOSUC && ret){
+ if((b = ucs4_strchr(wb, (UCS) '\n')) != NULL)
+ *b = '\0';
+
+ ucs4_strncpy(cb, wb, NLINE);
+ cb[NLINE-1] = '\0';
+
+ gotobob(0, 1);
+
+ status = TRUE;
+ next = 1;
+
+ while(status){
+ if(next++)
+ if(movetoword(wb) != TRUE)
+ break;
+
+ update();
+ (*term.t_rev)(1);
+ pputs(wb, 1); /* highlight word */
+ (*term.t_rev)(0);
+
+ if(ucs4_strcmp(cb, wb)){
+ char prompt[2*NLINE + 32];
+ char *wbu, *cbu;
+
+ wbu = ucs4_to_utf8_cpystr(wb);
+ cbu = ucs4_to_utf8_cpystr(cb);
+
+ snprintf(prompt, sizeof(prompt), _("Replace \"%s\" with \"%s\""), wbu, cbu);
+ status=mlyesno_utf8(prompt, TRUE);
+ if(wbu)
+ fs_give((void **) &wbu);
+ if(cbu)
+ fs_give((void **) &cbu);
+ }
+ else{
+ UCS *p;
+
+ p = utf8_to_ucs4_cpystr(_("Edit a replacement: "));
+ status=mlreplyd(p, cb, NLINE, QDEFLT, NULL);
+ if(p)
+ fs_give((void **) &p);
+ }
+
+
+ curwp->w_flag |= WFMOVE; /* put cursor back */
+ sgarbk = 0; /* fake no-keymenu-change! */
+ update();
+ pputs(wb, 0); /* un-highlight */
+
+ switch(status){
+ case TRUE:
+ chword(wb, cb); /* correct word */
+ case FALSE:
+ update(); /* place cursor */
+ break;
+ case ABORT:
+ emlwrite(_("Spell Checking Cancelled"), NULL);
+ ret = FALSE;
+ status = FALSE;
+ break;
+ case HELPCH:
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(pinespellhelp,
+ _("Help with Spelling Checker"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(spellhelp, _("Help with Spelling Checker"), 1);
+
+ case (CTRL|'L'):
+ next = 0; /* don't get next word */
+ sgarbf = TRUE; /* repaint full screen */
+ update();
+ status = TRUE;
+ continue;
+ default:
+ emlwrite("Huh?", NULL); /* shouldn't get here, but.. */
+ status = TRUE;
+ sleep(1);
+ break;
+ }
+
+ forwword(0, 1); /* goto next word */
+ }
+ }
+
+ P_close(); /* clean up */
+ our_unlink(fn);
+ swapimark(0, 1);
+ curwp->w_flag |= WFHARD|WFMODE;
+ sgarbk = TRUE;
+
+ if(ret)
+ emlwrite(_("Done checking spelling"), NULL);
+
+ return(ret);
+}
+
+
+#endif /* UNIX */
+
+
+/*
+ * movetoword() - move to the first occurance of the word w
+ *
+ * returns:
+ * TRUE upon success
+ * FALSE otherwise
+ */
+int
+movetoword(UCS *w)
+{
+ int i;
+ int ret = FALSE;
+ int olddoto;
+ LINE *olddotp;
+ register int off; /* curwp offset */
+ register LINE *lp; /* curwp line */
+
+ olddoto = curwp->w_doto; /* save where we are */
+ olddotp = curwp->w_dotp;
+
+ curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
+ while(forscan(&i, w, NULL, 0, 1) == TRUE){
+ if(i)
+ break; /* wrap NOT allowed! */
+
+ lp = curwp->w_dotp; /* for convenience */
+ off = curwp->w_doto;
+
+ /*
+ * We want to minimize the number of substrings that we report
+ * as matching a misspelled word...
+ */
+ if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c)){
+ off += ucs4_strlen(w);
+ if((!ucs4_isalpha(lgetc(lp, off).c) || off == llength(lp))
+ && lgetc(lp, 0).c != '>'){
+ ret = TRUE;
+ break;
+ }
+ }
+
+ forwchar(0, 1); /* move on... */
+
+ }
+ curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
+
+ if(ret == FALSE){
+ curwp->w_dotp = olddotp;
+ curwp->w_doto = olddoto;
+ }
+ else
+ curwp->w_flag |= WFHARD;
+
+ return(ret);
+}
+
+#endif /* SPELLER */
diff --git a/pico/osdep/spell.h b/pico/osdep/spell.h
new file mode 100644
index 00000000..ed7a4428
--- /dev/null
+++ b/pico/osdep/spell.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: spell.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SPELL_INCLUDED
+#define PICO_OSDEP_SPELL_INCLUDED
+
+
+/* exported prototypes */
+int spell(int, int);
+
+
+#endif /* PICO_OSDEP_SPELL_INCLUDED */
diff --git a/pico/osdep/terminal.c b/pico/osdep/terminal.c
new file mode 100644
index 00000000..72206c01
--- /dev/null
+++ b/pico/osdep/terminal.c
@@ -0,0 +1,1762 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: terminal.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Display routines
+ */
+
+#include <system.h>
+#include <general.h>
+
+
+#include "../estruct.h"
+#include "../keydefs.h"
+#include "../pico.h"
+#include "../mode.h"
+
+#include "raw.h"
+#include "color.h"
+#include "tty.h"
+#include "terminal.h"
+#include "getkey.h"
+
+#ifndef _WINDOWS
+extern long gmode;
+
+
+/*
+ * Useful Defaults
+ */
+#define MARGIN 8 /* size of minimim margin and */
+#define MROW 2 /* rows in menu */
+
+
+/* any special properties of the current terminal */
+unsigned term_capabilities = (TT_EOLEXIST | TT_SCROLLEXIST | TT_INSCHAR | TT_DELCHAR);
+
+
+/*
+ *
+ */
+unsigned
+tthascap(void)
+{
+ return(term_capabilities);
+}
+
+
+#if HAS_TERMINFO
+
+/*
+ * terminfo-based terminal i/o and control routines
+ */
+
+
+/* internal prototypes */
+static int tinfomove(int, int);
+static int tinfoeeol(void);
+static int tinfoeeop(void);
+static int tinfobeep(void);
+static int tinforev(int);
+static int tinfoopen(void);
+static int tinfoterminalinfo(int);
+static int tinfoclose(void);
+static void setup_dflt_esc_seq(void);
+static void tinfoinsert(UCS);
+static void tinfodelete(void);
+
+extern int tput();
+extern int tputs(char *, int, int (*)(int));
+extern char *tgoto(char *, int, int);
+extern char *tigetstr ();
+extern int setupterm(char *, int, int *);
+extern int tigetnum(char *);
+extern int tigetflag(char *);
+
+/**
+ ** Note: The tgoto calls should really be replaced by tparm calls for
+ ** modern terminfo. tgoto(s, x, y) == tparm(s, y, x).
+ **/
+
+int _tlines, _tcolumns;
+char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
+ *_setinverse, *_clearinverse,
+ *_setunderline, *_clearunderline,
+ *_setbold, *_clearallattr, /* there is no clear only bold! */
+ *_cleartoeoln, *_cleartoeos,
+ *_deleteline, /* delete line */
+ *_insertline, /* insert line */
+ *_scrollregion, /* define a scrolling region, vt100 */
+ *_insertchar, /* insert character, preferable to : */
+ *_startinsert, /* set insert mode and, */
+ *_endinsert, /* end insert mode */
+ *_deletechar, /* delete character */
+ *_startdelete, /* set delete mode and, */
+ *_enddelete, /* end delete mode */
+ *_scrolldown, /* scroll down */
+ *_scrollup, /* scroll up */
+ *_termcap_init, /* string to start termcap */
+ *_termcap_end, /* string to end termcap */
+ *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+int _colors, _pairs, _bce;
+char term_name[40];
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ tinfoopen,
+ tinfoterminalinfo,
+ tinfoclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ tinfomove,
+ tinfoeeol,
+ tinfoeeop,
+ tinfobeep,
+ tinforev
+};
+
+
+/*
+ * Add default keypad sequences to the trie.
+ */
+static void
+setup_dflt_esc_seq(void)
+{
+ /*
+ * this is sort of a hack [no kidding], but it allows us to use
+ * the function keys on pc's running telnet
+ */
+
+ /*
+ * UW-NDC/UCS vt10[02] application mode.
+ */
+ kpinsert("\033OP", F1, 1);
+ kpinsert("\033OQ", F2, 1);
+ kpinsert("\033OR", F3, 1);
+ kpinsert("\033OS", F4, 1);
+ kpinsert("\033Op", F5, 1);
+ kpinsert("\033Oq", F6, 1);
+ kpinsert("\033Or", F7, 1);
+ kpinsert("\033Os", F8, 1);
+ kpinsert("\033Ot", F9, 1);
+ kpinsert("\033Ou", F10, 1);
+ kpinsert("\033Ov", F11, 1);
+ kpinsert("\033Ow", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode.
+ */
+ kpinsert("\033OA", KEY_UP, 1);
+ kpinsert("\033OB", KEY_DOWN, 1);
+ kpinsert("\033OC", KEY_RIGHT, 1);
+ kpinsert("\033OD", KEY_LEFT, 1);
+
+ /*
+ * special keypad functions
+ */
+ kpinsert("\033[4J", KEY_PGUP, 1);
+ kpinsert("\033[3J", KEY_PGDN, 1);
+ kpinsert("\033[2J", KEY_HOME, 1);
+ kpinsert("\033[N", KEY_END, 1);
+
+ /*
+ * vt220?
+ */
+ kpinsert("\033[5~", KEY_PGUP, 1);
+ kpinsert("\033[6~", KEY_PGDN, 1);
+ kpinsert("\033[1~", KEY_HOME, 1);
+ kpinsert("\033[4~", KEY_END, 1);
+
+ /*
+ * konsole, XTerm (XFree 4.x.x) keyboard setting
+ */
+ kpinsert("\033[H", KEY_HOME, 1);
+ kpinsert("\033[F", KEY_END, 1);
+
+ /*
+ * gnome-terminal 2.6.0, don't know why it
+ * changed from 2.2.1
+ */
+ kpinsert("\033OH", KEY_HOME, 1);
+ kpinsert("\033OF", KEY_END, 1);
+
+ /*
+ * "\033[2~" was common for KEY_HOME in a quick survey
+ * of terminals (though typically the Insert key).
+ * Teraterm 2.33 sends the following escape sequences,
+ * which is quite incompatible with everything
+ * else:
+ * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
+ * PgDn: "\033[6~"
+ * The best thing to do would be to fix TeraTerm
+ * keymappings or to tweak terminfo.
+ */
+
+ /*
+ * ANSI mode.
+ */
+ kpinsert("\033[=a", F1, 1);
+ kpinsert("\033[=b", F2, 1);
+ kpinsert("\033[=c", F3, 1);
+ kpinsert("\033[=d", F4, 1);
+ kpinsert("\033[=e", F5, 1);
+ kpinsert("\033[=f", F6, 1);
+ kpinsert("\033[=g", F7, 1);
+ kpinsert("\033[=h", F8, 1);
+ kpinsert("\033[=i", F9, 1);
+ kpinsert("\033[=j", F10, 1);
+ kpinsert("\033[=k", F11, 1);
+ kpinsert("\033[=l", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode reset.
+ */
+ kpinsert("\033[A", KEY_UP, 1);
+ kpinsert("\033[B", KEY_DOWN, 1);
+ kpinsert("\033[C", KEY_RIGHT, 1);
+ kpinsert("\033[D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 mode.
+ */
+ kpinsert("\033A", KEY_UP, 1);
+ kpinsert("\033B", KEY_DOWN, 1);
+ kpinsert("\033C", KEY_RIGHT, 1);
+ kpinsert("\033D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 application keys, and some Zenith 19.
+ */
+ kpinsert("\033?r", KEY_DOWN, 1);
+ kpinsert("\033?t", KEY_LEFT, 1);
+ kpinsert("\033?v", KEY_RIGHT, 1);
+ kpinsert("\033?x", KEY_UP, 1);
+
+ /*
+ * Some Ctrl-Arrow keys
+ */
+ kpinsert("\033[5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[5D", CTRL_KEY_LEFT, 1);
+
+ kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);
+
+ /*
+ * Map some shift+up/down/left/right to their shiftless counterparts
+ */
+ kpinsert("\033[1;2A", KEY_UP, 1);
+ kpinsert("\033[1;2B", KEY_DOWN, 1);
+ kpinsert("\033[1;2C", KEY_RIGHT, 1);
+ kpinsert("\033[1;2D", KEY_LEFT, 1);
+ kpinsert("\033[a", KEY_UP, 1);
+ kpinsert("\033[b", KEY_DOWN, 1);
+ kpinsert("\033[c", KEY_RIGHT, 1);
+ kpinsert("\033[d", KEY_LEFT, 1);
+
+ /*
+ * Sun Console sequences.
+ */
+ kpinsert("\033[1", KEY_SWALLOW_Z, 1);
+ kpinsert("\033[215", KEY_SWAL_UP, 1);
+ kpinsert("\033[217", KEY_SWAL_LEFT, 1);
+ kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
+ kpinsert("\033[221", KEY_SWAL_DOWN, 1);
+
+ /*
+ * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
+ */
+ kpinsert("\033_", KEY_KERMIT, 1);
+
+ /*
+ * Fake a control character.
+ */
+ kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
+}
+
+
+static int
+tinfoterminalinfo(int termcap_wins)
+{
+ char *_ku, *_kd, *_kl, *_kr,
+ *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
+ *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
+ *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;
+ char *ttnm;
+
+ if (Pmaster) {
+ /*
+ * setupterm() automatically retrieves the value
+ * of the TERM variable.
+ */
+ int err;
+ ttnm = getenv("TERM");
+ if(!ttnm)
+ return(-1);
+
+ strncpy(term_name, ttnm, sizeof(term_name));
+ term_name[sizeof(term_name)-1] = '\0';
+ setupterm ((char *) 0, 1, &err);
+ if (err != 1) return(err-2);
+ }
+ else {
+ /*
+ * setupterm() issues a message and exits, if the
+ * terminfo data base is gone or the term type is
+ * unknown, if arg2 is 0.
+ */
+ setupterm ((char *) 0, 1, (int *) 0);
+ }
+
+ _clearscreen = tigetstr("clear");
+ _moveto = tigetstr("cup");
+ _up = tigetstr("cuu1");
+ _down = tigetstr("cud1");
+ _right = tigetstr("cuf1");
+ _left = tigetstr("cub1");
+ _setinverse = tigetstr("smso");
+ _clearinverse = tigetstr("rmso");
+ _setunderline = tigetstr("smul");
+ _clearunderline = tigetstr("rmul");
+ _setbold = tigetstr("bold");
+ _clearallattr = tigetstr("sgr0");
+ _cleartoeoln = tigetstr("el");
+ _cleartoeos = tigetstr("ed");
+ _deletechar = tigetstr("dch1");
+ _insertchar = tigetstr("ich1");
+ _startinsert = tigetstr("smir");
+ _endinsert = tigetstr("rmir");
+ _deleteline = tigetstr("dl1");
+ _insertline = tigetstr("il1");
+ _scrollregion = tigetstr("csr");
+ _scrolldown = tigetstr("ind");
+ _scrollup = tigetstr("ri");
+ _termcap_init = tigetstr("smcup");
+ _termcap_end = tigetstr("rmcup");
+ _startdelete = tigetstr("smdc");
+ _enddelete = tigetstr("rmdc");
+ _ku = tigetstr("kcuu1");
+ _kd = tigetstr("kcud1");
+ _kl = tigetstr("kcub1");
+ _kr = tigetstr("kcuf1");
+ _kppu = tigetstr("kpp");
+ _kppd = tigetstr("knp");
+ _kphome = tigetstr("khome");
+ _kpend = tigetstr("kend");
+ _kpdel = tigetstr("kdch1");
+ _kf1 = tigetstr("kf1");
+ _kf2 = tigetstr("kf2");
+ _kf3 = tigetstr("kf3");
+ _kf4 = tigetstr("kf4");
+ _kf5 = tigetstr("kf5");
+ _kf6 = tigetstr("kf6");
+ _kf7 = tigetstr("kf7");
+ _kf8 = tigetstr("kf8");
+ _kf9 = tigetstr("kf9");
+ _kf10 = tigetstr("kf10");
+ _kf11 = tigetstr("kf11");
+ _kf12 = tigetstr("kf12");
+
+ _colors = tigetnum("colors");
+ _pairs = tigetnum("pairs");
+ _setaf = tigetstr("setaf");
+ _setab = tigetstr("setab");
+ _setf = tigetstr("setf");
+ _setb = tigetstr("setb");
+ _scp = tigetstr("scp");
+ _op = tigetstr("op");
+ _oc = tigetstr("oc");
+ _bce = tigetflag("bce");
+
+ _tlines = tigetnum("lines");
+ if(_tlines == -1){
+ char *er;
+ int rr;
+
+ /* tigetnum failed, try $LINES */
+ er = getenv("LINES");
+ if(er && (rr = atoi(er)) > 0)
+ _tlines = rr;
+ }
+
+ _tcolumns = tigetnum("cols");
+ if(_tcolumns == -1){
+ char *ec;
+ int cc;
+
+ /* tigetnum failed, try $COLUMNS */
+ ec = getenv("COLUMNS");
+ if(ec && (cc = atoi(ec)) > 0)
+ _tcolumns = cc;
+ }
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come first, they will override any conflicting termcap
+ * or terminfo escape sequences defined below. An escape sequence is
+ * considered conflicting if one is a prefix of the other.
+ * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
+ * escape sequences that don't work, because they conflict with default
+ * sequences defined here.
+ */
+ if(!termcap_wins)
+ setup_dflt_esc_seq();
+
+ /*
+ * add termcap/info escape sequences to the trie...
+ */
+
+ if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
+ kpinsert(_ku, KEY_UP, termcap_wins);
+ kpinsert(_kd, KEY_DOWN, termcap_wins);
+ kpinsert(_kl, KEY_LEFT, termcap_wins);
+ kpinsert(_kr, KEY_RIGHT, termcap_wins);
+ }
+
+ if(_kppu != NULL && _kppd != NULL){
+ kpinsert(_kppu, KEY_PGUP, termcap_wins);
+ kpinsert(_kppd, KEY_PGDN, termcap_wins);
+ }
+
+ kpinsert(_kphome, KEY_HOME, termcap_wins);
+ kpinsert(_kpend, KEY_END, termcap_wins);
+ kpinsert(_kpdel, KEY_DEL, termcap_wins);
+
+ kpinsert(_kf1, F1, termcap_wins);
+ kpinsert(_kf2, F2, termcap_wins);
+ kpinsert(_kf3, F3, termcap_wins);
+ kpinsert(_kf4, F4, termcap_wins);
+ kpinsert(_kf5, F5, termcap_wins);
+ kpinsert(_kf6, F6, termcap_wins);
+ kpinsert(_kf7, F7, termcap_wins);
+ kpinsert(_kf8, F8, termcap_wins);
+ kpinsert(_kf9, F9, termcap_wins);
+ kpinsert(_kf10, F10, termcap_wins);
+ kpinsert(_kf11, F11, termcap_wins);
+ kpinsert(_kf12, F12, termcap_wins);
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come after the termcap/terminfo escape sequences above,
+ * the termcap/info sequences will override any conflicting default
+ * escape sequences defined here.
+ * So, with TERMCAP_WINS, some of the default sequences will be missing.
+ * This means that you'd better get all of your termcap/terminfo entries
+ * correct if you define TERMCAP_WINS.
+ */
+ if(termcap_wins)
+ setup_dflt_esc_seq();
+
+ if(Pmaster)
+ return(0);
+ else
+ return(TRUE);
+}
+
+static int
+tinfoopen(void)
+{
+ int row, col;
+
+ /*
+ * determine the terminal's communication speed and decide
+ * if we need to do optimization ...
+ */
+ if(ttisslow())
+ term_capabilities |= TT_OPTIMIZE;
+
+ col = _tcolumns;
+ row = _tlines;
+ if(row >= 0)
+ row--;
+
+ ttgetwinsz(&row, &col);
+ term.t_nrow = (short) row;
+ term.t_ncol = (short) col;
+
+ if(_cleartoeoln != NULL) /* able to use clear to EOL? */
+ term_capabilities |= TT_EOLEXIST;
+ else
+ term_capabilities &= ~TT_EOLEXIST;
+
+ if(_setinverse != NULL)
+ term_capabilities |= TT_REVEXIST;
+ else
+ term_capabilities &= ~TT_REVEXIST;
+
+ if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
+ term_capabilities &= ~TT_DELCHAR;
+
+ if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
+ term_capabilities &= ~TT_INSCHAR;
+
+ if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
+ && (_deleteline == NULL || _insertline == NULL))
+ term_capabilities &= ~TT_SCROLLEXIST;
+
+ if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
+ if(Pmaster == NULL){
+ puts("Incomplete terminfo entry\n");
+ exit(1);
+ }
+ }
+
+ ttopen();
+
+ if(_termcap_init && !Pmaster) {
+ putpad(_termcap_init); /* any init terminfo requires */
+ if (_scrollregion)
+ putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
+ }
+
+ /*
+ * Initialize UW-modified NCSA telnet to use its functionkeys
+ */
+ if((gmode & MDFKEY) && Pmaster == NULL)
+ puts("\033[99h");
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoclose(void)
+{
+ if(!Pmaster){
+ if(gmode&MDFKEY)
+ puts("\033[99l"); /* reset UW-NCSA telnet keys */
+
+ if(_termcap_end) /* any clean up terminfo requires */
+ putpad(_termcap_end);
+ }
+
+ ttclose();
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * tinfoinsert - insert a character at the current character position.
+ * _insertchar takes precedence.
+ */
+static void
+tinfoinsert(UCS ch)
+{
+ if(_insertchar != NULL){
+ putpad(_insertchar);
+ ttputc(ch);
+ }
+ else{
+ putpad(_startinsert);
+ ttputc(ch);
+ putpad(_endinsert);
+ }
+}
+
+
+/*
+ * tinfodelete - delete a character at the current character position.
+ */
+static void
+tinfodelete(void)
+{
+ if(_startdelete == NULL && _enddelete == NULL)
+ putpad(_deletechar);
+ else{
+ putpad(_startdelete);
+ putpad(_deletechar);
+ putpad(_enddelete);
+ }
+}
+
+
+/*
+ * o_scrolldown() - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrolldown(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad((_scrollup != NULL && *_scrollup != '\0') ? _scrollup : "\n" );
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tinfomove(row, 0);
+ }
+ else{
+ /*
+ * this code causes a jiggly motion of the keymenu when scrolling
+ */
+ for(i = 0; i < n; i++){
+ tinfomove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_deleteline);
+ tinfomove(row, 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /*
+ * this code causes a sweeping motion up and down the display
+ */
+ tinfomove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_scrollup() - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrollup(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ /* setting scrolling region moves cursor to home */
+ tinfomove(term.t_nrow-(term.t_mrow+1), 0);
+ for(i = 0;i < n; i++)
+ putpad((_scrolldown == NULL || _scrolldown[0] == '\0') ? "\n"
+ : _scrolldown);
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tinfomove(2, 0);
+ }
+ else{
+ for(i = 0; i < n; i++){
+ tinfomove(row, 0);
+ putpad(_deleteline);
+ tinfomove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /* see note above */
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tinfomove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0;i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+
+/*
+ * o_insert - use terminfo to optimized character insert
+ * returns: true if it optimized output, false otherwise
+ */
+int
+o_insert(UCS c)
+{
+ if(term_capabilities & TT_INSCHAR){
+ tinfoinsert(c);
+ return(1); /* no problems! */
+ }
+
+ return(0); /* can't do it. */
+}
+
+
+/*
+ * o_delete - use terminfo to optimized character insert
+ * returns true if it optimized output, false otherwise
+ */
+int
+o_delete(void)
+{
+ if(term_capabilities & TT_DELCHAR){
+ tinfodelete();
+ return(1); /* deleted, no problem! */
+ }
+
+ return(0); /* no dice. */
+}
+
+
+static int
+tinfomove(int row, int col)
+{
+ putpad(tgoto(_moveto, col, row));
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoeeol(void)
+{
+ int c, starting_col, starting_line;
+ char *last_bg_color;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_line = ttrow;
+ last_bg_color = pico_get_last_bg_color();
+ pico_set_nbg_color();
+ for(c = ttcol; c < term.t_ncol; c++)
+ ttputc(' ');
+
+ tinfomove(starting_line, starting_col);
+ if(last_bg_color){
+ pico_set_bg_color(last_bg_color);
+ free(last_bg_color);
+ }
+ }
+ else if(_cleartoeoln)
+ putpad(_cleartoeoln);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoeeop(void)
+{
+ int i, starting_col, starting_row;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeos)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_row = ttrow;
+ tinfoeeol(); /* rest of this line */
+ for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */
+ tinfomove(i, 0);
+ tinfoeeol();
+ }
+
+ tinfomove(starting_row, starting_col);
+ }
+ else if(_cleartoeos)
+ putpad(_cleartoeos);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinforev(int state) /* change reverse video status */
+{ /* FALSE = normal video, TRUE = rev video */
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+
+ return(1);
+}
+
+
+static int
+tinfobeep(void)
+{
+ ttputc(BELL);
+
+ /* return ignored */
+ return(0);
+}
+
+
+void
+putpad(char *str)
+{
+ tputs(str, 1, putchar);
+}
+
+#elif HAS_TERMCAP
+
+/*
+ * termcap-based terminal i/o and control routines
+ */
+
+
+/* internal prototypes */
+static int tcapmove(int, int);
+static int tcapeeol(void);
+static int tcapeeop(void);
+static int tcapbeep(void);
+static int tcaprev(int);
+static int tcapopen(void);
+static int tcapterminalinfo(int);
+static int tcapclose(void);
+static void setup_dflt_esc_seq(void);
+static void tcapinsert(UCS);
+static void tcapdelete(void);
+
+extern int tput();
+extern char *tgoto(char *, int, int);
+
+/*
+ * This number used to be 315. No doubt there was a reason for that but we
+ * don't know what it was. It's a bit of a hassle to make it dynamic, and most
+ * modern systems seem to be using terminfo, so we'll just change it to 800.
+ * We weren't stopping on overflow before, so we'll do that, too.
+ */
+#define TCAPSLEN 800
+char tcapbuf[TCAPSLEN];
+int _tlines, _tcolumns;
+char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
+ *_setinverse, *_clearinverse,
+ *_setunderline, *_clearunderline,
+ *_setbold, *_clearallattr, /* there is no clear only bold! */
+ *_cleartoeoln, *_cleartoeos,
+ *_deleteline, /* delete line */
+ *_insertline, /* insert line */
+ *_scrollregion, /* define a scrolling region, vt100 */
+ *_insertchar, /* insert character, preferable to : */
+ *_startinsert, /* set insert mode and, */
+ *_endinsert, /* end insert mode */
+ *_deletechar, /* delete character */
+ *_startdelete, /* set delete mode and, */
+ *_enddelete, /* end delete mode */
+ *_scrolldown, /* scroll down */
+ *_scrollup, /* scroll up */
+ *_termcap_init, /* string to start termcap */
+ *_termcap_end, /* string to end termcap */
+ *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+int _colors, _pairs, _bce;
+char term_name[40];
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ tcapopen,
+ tcapterminalinfo,
+ tcapclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ tcapmove,
+ tcapeeol,
+ tcapeeop,
+ tcapbeep,
+ tcaprev
+};
+
+
+/*
+ * Add default keypad sequences to the trie.
+ */
+static void
+setup_dflt_esc_seq(void)
+{
+ /*
+ * this is sort of a hack, but it allows us to use
+ * the function keys on pc's running telnet
+ */
+
+ /*
+ * UW-NDC/UCS vt10[02] application mode.
+ */
+ kpinsert("\033OP", F1, 1);
+ kpinsert("\033OQ", F2, 1);
+ kpinsert("\033OR", F3, 1);
+ kpinsert("\033OS", F4, 1);
+ kpinsert("\033Op", F5, 1);
+ kpinsert("\033Oq", F6, 1);
+ kpinsert("\033Or", F7, 1);
+ kpinsert("\033Os", F8, 1);
+ kpinsert("\033Ot", F9, 1);
+ kpinsert("\033Ou", F10, 1);
+ kpinsert("\033Ov", F11, 1);
+ kpinsert("\033Ow", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode.
+ */
+ kpinsert("\033OA", KEY_UP, 1);
+ kpinsert("\033OB", KEY_DOWN, 1);
+ kpinsert("\033OC", KEY_RIGHT, 1);
+ kpinsert("\033OD", KEY_LEFT, 1);
+
+ /*
+ * special keypad functions
+ */
+ kpinsert("\033[4J", KEY_PGUP, 1);
+ kpinsert("\033[3J", KEY_PGDN, 1);
+ kpinsert("\033[2J", KEY_HOME, 1);
+ kpinsert("\033[N", KEY_END, 1);
+
+ /*
+ * vt220?
+ */
+ kpinsert("\033[5~", KEY_PGUP, 1);
+ kpinsert("\033[6~", KEY_PGDN, 1);
+ kpinsert("\033[1~", KEY_HOME, 1);
+ kpinsert("\033[4~", KEY_END, 1);
+
+ /*
+ * konsole, XTerm (XFree 4.x.x) keyboard setting
+ */
+ kpinsert("\033[H", KEY_HOME, 1);
+ kpinsert("\033[F", KEY_END, 1);
+
+ /*
+ * gnome-terminal 2.6.0, don't know why it
+ * changed from 2.2.1
+ */
+ kpinsert("\033OH", KEY_HOME, 1);
+ kpinsert("\033OF", KEY_END, 1);
+
+ /*
+ * "\033[2~" was common for KEY_HOME in a quick survey
+ * of terminals (though typically the Insert key).
+ * Teraterm 2.33 sends the following escape sequences,
+ * which is quite incompatible with everything
+ * else:
+ * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
+ * PgDn: "\033[6~"
+ * The best thing to do would be to fix TeraTerm
+ * keymappings or to tweak terminfo.
+ */
+
+ /*
+ * ANSI mode.
+ */
+ kpinsert("\033[=a", F1, 1);
+ kpinsert("\033[=b", F2, 1);
+ kpinsert("\033[=c", F3, 1);
+ kpinsert("\033[=d", F4, 1);
+ kpinsert("\033[=e", F5, 1);
+ kpinsert("\033[=f", F6, 1);
+ kpinsert("\033[=g", F7, 1);
+ kpinsert("\033[=h", F8, 1);
+ kpinsert("\033[=i", F9, 1);
+ kpinsert("\033[=j", F10, 1);
+ kpinsert("\033[=k", F11, 1);
+ kpinsert("\033[=l", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode reset.
+ */
+ kpinsert("\033[A", KEY_UP, 1);
+ kpinsert("\033[B", KEY_DOWN, 1);
+ kpinsert("\033[C", KEY_RIGHT, 1);
+ kpinsert("\033[D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 mode.
+ */
+ kpinsert("\033A", KEY_UP, 1);
+ kpinsert("\033B", KEY_DOWN, 1);
+ kpinsert("\033C", KEY_RIGHT, 1);
+ kpinsert("\033D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 application keys, and some Zenith 19.
+ */
+ kpinsert("\033?r", KEY_DOWN, 1);
+ kpinsert("\033?t", KEY_LEFT, 1);
+ kpinsert("\033?v", KEY_RIGHT, 1);
+ kpinsert("\033?x", KEY_UP, 1);
+
+ /*
+ * Some Ctrl-Arrow keys
+ */
+ kpinsert("\033[5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[5D", CTRL_KEY_LEFT, 1);
+
+ kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);
+
+ /*
+ * Map some shift+up/down/left/right to their shiftless counterparts
+ */
+ kpinsert("\033[1;2A", KEY_UP, 1);
+ kpinsert("\033[1;2B", KEY_DOWN, 1);
+ kpinsert("\033[1;2C", KEY_RIGHT, 1);
+ kpinsert("\033[1;2D", KEY_LEFT, 1);
+ kpinsert("\033[a", KEY_UP, 1);
+ kpinsert("\033[b", KEY_DOWN, 1);
+ kpinsert("\033[c", KEY_RIGHT, 1);
+ kpinsert("\033[d", KEY_LEFT, 1);
+
+ /*
+ * Sun Console sequences.
+ */
+ kpinsert("\033[1", KEY_SWALLOW_Z, 1);
+ kpinsert("\033[215", KEY_SWAL_UP, 1);
+ kpinsert("\033[217", KEY_SWAL_LEFT, 1);
+ kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
+ kpinsert("\033[221", KEY_SWAL_DOWN, 1);
+
+ /*
+ * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
+ */
+ kpinsert("\033_", KEY_KERMIT, 1);
+
+ /*
+ * Fake a control character.
+ */
+ kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
+}
+
+
+/*
+ * Read termcap and set some global variables. Initialize input trie to
+ * decode escape sequences.
+ */
+static int
+tcapterminalinfo(int termcap_wins)
+{
+ char *p, *tgetstr();
+ char tcbuf[2*1024];
+ char *tv_stype;
+ char err_str[72];
+ int err;
+ char *_ku, *_kd, *_kl, *_kr,
+ *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
+ *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
+ *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;
+
+ if (!(tv_stype = getenv("TERM")) || !strncpy(term_name, tv_stype, sizeof(term_name))){
+ if(Pmaster){
+ return(-1);
+ }
+ else{
+ puts("Environment variable TERM not defined!");
+ exit(1);
+ }
+ }
+
+ term_name[sizeof(term_name)-1] = '\0';
+
+ if((err = tgetent(tcbuf, tv_stype)) != 1){
+ if(Pmaster){
+ return(err - 2);
+ }
+ else{
+ snprintf(err_str, sizeof(err_str), "Unknown terminal type %s!", tv_stype);
+ puts(err_str);
+ exit(1);
+ }
+ }
+
+ p = tcapbuf;
+
+ _clearscreen = tgetstr("cl", &p);
+ _moveto = tgetstr("cm", &p);
+ _up = tgetstr("up", &p);
+ _down = tgetstr("do", &p);
+ _right = tgetstr("nd", &p);
+ _left = tgetstr("bs", &p);
+ _setinverse = tgetstr("so", &p);
+ _clearinverse = tgetstr("se", &p);
+ _setunderline = tgetstr("us", &p);
+ _clearunderline = tgetstr("ue", &p);
+ _setbold = tgetstr("md", &p);
+ _clearallattr = tgetstr("me", &p);
+ _cleartoeoln = tgetstr("ce", &p);
+ _cleartoeos = tgetstr("cd", &p);
+ _deletechar = tgetstr("dc", &p);
+ _insertchar = tgetstr("ic", &p);
+ _startinsert = tgetstr("im", &p);
+ _endinsert = tgetstr("ei", &p);
+ _deleteline = tgetstr("dl", &p);
+ _insertline = tgetstr("al", &p);
+ _scrollregion = tgetstr("cs", &p);
+ _scrolldown = tgetstr("sf", &p);
+ _scrollup = tgetstr("sr", &p);
+ _termcap_init = tgetstr("ti", &p);
+ _termcap_end = tgetstr("te", &p);
+ _startdelete = tgetstr("dm", &p);
+ _enddelete = tgetstr("ed", &p);
+ _ku = tgetstr("ku", &p);
+ _kd = tgetstr("kd", &p);
+ _kl = tgetstr("kl", &p);
+ _kr = tgetstr("kr", &p);
+ _kppu = tgetstr("kP", &p);
+ _kppd = tgetstr("kN", &p);
+ _kphome = tgetstr("kh", &p);
+ _kpend = tgetstr("kH", &p);
+ _kpdel = tgetstr("kD", &p);
+ _kf1 = tgetstr("k1", &p);
+ _kf2 = tgetstr("k2", &p);
+ _kf3 = tgetstr("k3", &p);
+ _kf4 = tgetstr("k4", &p);
+ _kf5 = tgetstr("k5", &p);
+ _kf6 = tgetstr("k6", &p);
+ _kf7 = tgetstr("k7", &p);
+ _kf8 = tgetstr("k8", &p);
+ _kf9 = tgetstr("k9", &p);
+ if((_kf10 = tgetstr("k;", &p)) == NULL)
+ _kf10 = tgetstr("k0", &p);
+ _kf11 = tgetstr("F1", &p);
+ _kf12 = tgetstr("F2", &p);
+
+ _colors = tgetnum("Co");
+ _pairs = tgetnum("pa");
+ _setaf = tgetstr("AF", &p);
+ _setab = tgetstr("AB", &p);
+ _setf = tgetstr("Sf", &p);
+ _setb = tgetstr("Sb", &p);
+ _scp = tgetstr("sp", &p);
+ _op = tgetstr("op", &p);
+ _oc = tgetstr("oc", &p);
+ _bce = tgetflag("ut");
+
+ if (p >= &tcapbuf[TCAPSLEN]){
+ puts("Terminal description too big!\n");
+ if(Pmaster)
+ return(-3);
+ else
+ exit(1);
+ }
+
+ _tlines = tgetnum("li");
+ if(_tlines == -1){
+ char *er;
+ int rr;
+
+ /* tgetnum failed, try $LINES */
+ er = getenv("LINES");
+ if(er && (rr = atoi(er)) > 0)
+ _tlines = rr;
+ }
+
+ _tcolumns = tgetnum("co");
+ if(_tcolumns == -1){
+ char *ec;
+ int cc;
+
+ /* tgetnum failed, try $COLUMNS */
+ ec = getenv("COLUMNS");
+ if(ec && (cc = atoi(ec)) > 0)
+ _tcolumns = cc;
+ }
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come first, they will override any conflicting termcap
+ * or terminfo escape sequences defined below. An escape sequence is
+ * considered conflicting if one is a prefix of the other.
+ * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
+ * escape sequences that don't work, because they conflict with default
+ * sequences defined here.
+ */
+ if(!termcap_wins)
+ setup_dflt_esc_seq();
+
+ /*
+ * add termcap/info escape sequences to the trie...
+ */
+
+ if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
+ kpinsert(_ku, KEY_UP, termcap_wins);
+ kpinsert(_kd, KEY_DOWN, termcap_wins);
+ kpinsert(_kl, KEY_LEFT, termcap_wins);
+ kpinsert(_kr, KEY_RIGHT, termcap_wins);
+ }
+
+ if(_kppu != NULL && _kppd != NULL){
+ kpinsert(_kppu, KEY_PGUP, termcap_wins);
+ kpinsert(_kppd, KEY_PGDN, termcap_wins);
+ }
+
+ kpinsert(_kphome, KEY_HOME, termcap_wins);
+ kpinsert(_kpend, KEY_END, termcap_wins);
+ kpinsert(_kpdel, KEY_DEL, termcap_wins);
+
+ kpinsert(_kf1, F1, termcap_wins);
+ kpinsert(_kf2, F2, termcap_wins);
+ kpinsert(_kf3, F3, termcap_wins);
+ kpinsert(_kf4, F4, termcap_wins);
+ kpinsert(_kf5, F5, termcap_wins);
+ kpinsert(_kf6, F6, termcap_wins);
+ kpinsert(_kf7, F7, termcap_wins);
+ kpinsert(_kf8, F8, termcap_wins);
+ kpinsert(_kf9, F9, termcap_wins);
+ kpinsert(_kf10, F10, termcap_wins);
+ kpinsert(_kf11, F11, termcap_wins);
+ kpinsert(_kf12, F12, termcap_wins);
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come after the termcap/terminfo escape sequences above,
+ * the termcap/info sequences will override any conflicting default
+ * escape sequences defined here.
+ * So, with TERMCAP_WINS, some of the default sequences will be missing.
+ * This means that you'd better get all of your termcap/terminfo entries
+ * correct if you define TERMCAP_WINS.
+ */
+ if(termcap_wins)
+ setup_dflt_esc_seq();
+
+ if(Pmaster)
+ return(0);
+ else
+ return(TRUE);
+}
+
+static int
+tcapopen(void)
+{
+ int row, col;
+
+ /*
+ * determine the terminal's communication speed and decide
+ * if we need to do optimization ...
+ */
+ if(ttisslow())
+ term_capabilities |= TT_OPTIMIZE;
+
+ col = _tcolumns;
+ row = _tlines;
+ if(row >= 0)
+ row--;
+
+ ttgetwinsz(&row, &col);
+ term.t_nrow = (short) row;
+ term.t_ncol = (short) col;
+
+ if(_cleartoeoln != NULL) /* able to use clear to EOL? */
+ term_capabilities |= TT_EOLEXIST;
+ else
+ term_capabilities &= ~TT_EOLEXIST;
+
+ if(_setinverse != NULL)
+ term_capabilities |= TT_REVEXIST;
+ else
+ term_capabilities &= ~TT_REVEXIST;
+
+ if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
+ term_capabilities &= ~TT_DELCHAR;
+
+ if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
+ term_capabilities &= ~TT_INSCHAR;
+
+ if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
+ && (_deleteline == NULL || _insertline == NULL))
+ term_capabilities &= ~TT_SCROLLEXIST;
+
+ if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
+ if(Pmaster == NULL){
+ puts("Incomplete termcap entry\n");
+ exit(1);
+ }
+ }
+
+ ttopen();
+
+ if(_termcap_init && !Pmaster) {
+ putpad(_termcap_init); /* any init termcap requires */
+ if (_scrollregion)
+ putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
+ }
+
+ /*
+ * Initialize UW-modified NCSA telnet to use it's functionkeys
+ */
+ if(gmode&MDFKEY && Pmaster == NULL)
+ puts("\033[99h");
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapclose(void)
+{
+ if(!Pmaster){
+ if(gmode&MDFKEY)
+ puts("\033[99l"); /* reset UW-NCSA telnet keys */
+
+ if(_termcap_end) /* any cleanup termcap requires */
+ putpad(_termcap_end);
+ }
+
+ ttclose();
+
+ /* return ignored */
+ return(0);
+}
+
+
+
+/*
+ * tcapinsert - insert a character at the current character position.
+ * _insertchar takes precedence.
+ */
+static void
+tcapinsert(UCS ch)
+{
+ if(_insertchar != NULL){
+ putpad(_insertchar);
+ ttputc(ch);
+ }
+ else{
+ putpad(_startinsert);
+ ttputc(ch);
+ putpad(_endinsert);
+ }
+}
+
+
+/*
+ * tcapdelete - delete a character at the current character position.
+ */
+static void
+tcapdelete(void)
+{
+ if(_startdelete == NULL && _enddelete == NULL)
+ putpad(_deletechar);
+ else{
+ putpad(_startdelete);
+ putpad(_deletechar);
+ putpad(_enddelete);
+ }
+}
+
+
+/*
+ * o_scrolldown - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrolldown(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad( (_scrollup != NULL && *_scrollup != '\0')
+ ? _scrollup : "\n" );
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tcapmove(row, 0);
+ }
+ else{
+ /*
+ * this code causes a jiggly motion of the keymenu when scrolling
+ */
+ for(i = 0; i < n; i++){
+ tcapmove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_deleteline);
+ tcapmove(row, 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /*
+ * this code causes a sweeping motion up and down the display
+ */
+ tcapmove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_scrollup - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrollup(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ /* setting scrolling region moves cursor to home */
+ tcapmove(term.t_nrow-(term.t_mrow+1), 0);
+ for(i = 0;i < n; i++)
+ putpad((_scrolldown == NULL || _scrolldown[0] == '\0')
+ ? "\n" : _scrolldown);
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tcapmove(2, 0);
+ }
+ else{
+ for(i = 0; i < n; i++){
+ tcapmove(row, 0);
+ putpad(_deleteline);
+ tcapmove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /* see note above */
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tcapmove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0;i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_insert - use termcap info to optimized character insert
+ * returns: true if it optimized output, false otherwise
+ */
+int
+o_insert(UCS c)
+{
+ if(term_capabilities & TT_INSCHAR){
+ tcapinsert(c);
+ return(1); /* no problems! */
+ }
+
+ return(0); /* can't do it. */
+}
+
+
+/*
+ * o_delete - use termcap info to optimized character insert
+ * returns true if it optimized output, false otherwise
+ */
+int
+o_delete(void)
+{
+ if(term_capabilities & TT_DELCHAR){
+ tcapdelete();
+ return(1); /* deleted, no problem! */
+ }
+
+ return(0); /* no dice. */
+}
+
+
+static int
+tcapmove(int row, int col)
+{
+ putpad(tgoto(_moveto, col, row));
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapeeol(void)
+{
+ int c, starting_col, starting_line;
+ char *last_bg_color;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_line = ttrow;
+ last_bg_color = pico_get_last_bg_color();
+ pico_set_nbg_color();
+ for(c = ttcol; c < term.t_ncol; c++)
+ ttputc(' ');
+
+ tcapmove(starting_line, starting_col);
+ if(last_bg_color){
+ pico_set_bg_color(last_bg_color);
+ free(last_bg_color);
+ }
+ }
+ else if(_cleartoeoln)
+ putpad(_cleartoeoln);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapeeop(void)
+{
+ int i, starting_col, starting_row;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeos)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_row = ttrow;
+ tcapeeol(); /* rest of this line */
+ for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */
+ tcapmove(i, 0);
+ tcapeeol();
+ }
+
+ tcapmove(starting_row, starting_col);
+ }
+ else if(_cleartoeos)
+ putpad(_cleartoeos);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcaprev(int state) /* change reverse video status */
+{ /* FALSE = normal video, TRUE = reverse video */
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+
+ return(1);
+}
+
+
+static int
+tcapbeep(void)
+{
+ ttputc(BELL);
+
+ /* return ignored */
+ return(0);
+}
+
+
+void
+putpad(char *str)
+{
+ tputs(str, 1, putchar);
+}
+
+#else /* HARD_CODED_ANSI_TERMINAL */
+
+/*
+ * ANSI-specific terminal i/o and control routines
+ */
+
+
+#define BEL 0x07 /* BEL character. */
+#define ESC 0x1B /* ESC character. */
+
+
+extern int ttflush();
+
+extern int ansimove(int, int);
+extern int ansieeol(void);
+extern int ansieeop(void);
+extern int ansibeep(void);
+extern int ansiparm(int);
+extern int ansiopen(void);
+extern int ansiterminalinfo(int);
+extern int ansirev(int);
+
+/*
+ * Standard terminal interface dispatch table. Most of the fields point into
+ * "termio" code.
+ */
+#if defined(VAX) && !defined(__ALPHA)
+globaldef
+#endif
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ ansiopen,
+ ansiterminalinfo,
+ ttclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ ansimove,
+ ansieeol,
+ ansieeop,
+ ansibeep,
+ ansirev
+};
+
+int
+ansimove(int row, int col)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ansiparm(row+1);
+ ttputc(';');
+ ansiparm(col+1);
+ ttputc('H');
+}
+
+int
+ansieeol(void)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ttputc('K');
+}
+
+int
+ansieeop(void)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ttputc('J');
+}
+
+int
+ansirev(int state) /* change reverse video state */
+{ /* TRUE = reverse, FALSE = normal */
+ static int PrevState = 0;
+
+ if(state != PrevState) {
+ PrevState = state ;
+ ttputc(ESC);
+ ttputc('[');
+ ttputc(state ? '7': '0');
+ ttputc('m');
+ }
+}
+
+int
+ansibeep(void)
+{
+ ttputc(BEL);
+ ttflush();
+}
+
+int
+ansiparm(int n)
+{
+ register int q;
+
+ q = n/10;
+ if (q != 0)
+ ansiparm(q);
+ ttputc((n%10) + '0');
+}
+
+
+int
+ansiterminalinfo(int termcap_wins)
+{
+#if V7
+ register char *cp;
+ char *getenv();
+
+ if ((cp = getenv("TERM")) == NULL) {
+ puts("Shell variable TERM not defined!");
+ exit(1);
+ }
+ if (strcmp(cp, "vt100") != 0) {
+ puts("Terminal type not 'vt100'!");
+ exit(1);
+ }
+#endif
+ /* revexist = TRUE; dead code? */
+}
+
+int
+ansiopen(void)
+{
+ ttopen();
+}
+
+
+#endif /* HARD_CODED_ANSI_TERMINAL */
+#else /* _WINDOWS */
+
+/* These are all just noops in Windows */
+
+unsigned
+tthascap(void)
+{
+ return(0);
+}
+
+/*
+ * o_insert - optimize screen insert of char c
+ */
+int
+o_insert(UCS c)
+{
+ return(0);
+}
+
+
+/*
+ * o_delete - optimized character deletion
+ */
+int
+o_delete(void)
+{
+ return(0);
+}
+
+
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/terminal.h b/pico/osdep/terminal.h
new file mode 100644
index 00000000..6449c32c
--- /dev/null
+++ b/pico/osdep/terminal.h
@@ -0,0 +1,50 @@
+/*
+ * $Id: terminal.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TERMINAL_INCLUDED
+#define PICO_OSDEP_TERMINAL_INCLUDED
+
+
+/*
+ * Useful definitions
+ */
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+#define TT_OPTIMIZE 0x01 /* optimize flag(cf line speed) */
+#define TT_EOLEXIST 0x02 /* does clear to EOL exist */
+#define TT_SCROLLEXIST 0x04 /* does insert line exist */
+#define TT_REVEXIST 0x08 /* does reverse video exist? */
+#define TT_INSCHAR 0x10 /* does insert character exist */
+#define TT_DELCHAR 0x20 /* does delete character exist */
+
+
+#define TERM_OPTIMIZE (tthascap() & TT_OPTIMIZE)
+#define TERM_EOLEXIST (tthascap() & TT_EOLEXIST)
+#define TERM_SCROLLEXIST (tthascap() & TT_SCROLLEXIST)
+#define TERM_REVEXIST (tthascap() & TT_REVEXIST)
+#define TERM_INSCHAR (tthascap() & TT_INSCHAR)
+#define TERM_DELCHAR (tthascap() & TT_DELCHAR)
+
+
+/* exported prototypes */
+unsigned tthascap(void);
+#if HAS_TERMINFO || HAS_TERMCAP
+void putpad(char *);
+#endif
+
+
+#endif /* PICO_OSDEP_TERMINAL_INCLUDED */
diff --git a/pico/osdep/truncate.c b/pico/osdep/truncate.c
new file mode 100644
index 00000000..8330338e
--- /dev/null
+++ b/pico/osdep/truncate.c
@@ -0,0 +1,20 @@
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ *
+ * Tim Rice tim@trr.metro.net Mon Jun 3 16:57:26 PDT 1996
+ *
+ * a quick and dirty trancate()
+ * Altos System V (5.3.1) does not have one
+ * neither does SCO Open Server Enterprise 3.0
+ *
+ */
diff --git a/pico/osdep/truncate.h b/pico/osdep/truncate.h
new file mode 100644
index 00000000..73c2decf
--- /dev/null
+++ b/pico/osdep/truncate.h
@@ -0,0 +1,21 @@
+/*
+ * $Id: truncate.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TRUNCATE_INCLUDED
+#define PICO_OSDEP_TRUNCATE_INCLUDED
+
+
+#endif /* PICO_OSDEP_TRUNCATE_INCLUDED */
diff --git a/pico/osdep/tty.c b/pico/osdep/tty.c
new file mode 100644
index 00000000..100b37ea
--- /dev/null
+++ b/pico/osdep/tty.c
@@ -0,0 +1,372 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: tty.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: tty routines
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "signals.h"
+#ifndef _WINDOWS
+#include "terminal.h"
+#include "raw.h"
+#include "read.h"
+#else
+#include "mswin.h"
+#endif /* _WINDOWS */
+
+#ifdef MOUSE
+#include "mouse.h"
+#endif /* MOUSE */
+
+#include "tty.h"
+
+
+#ifndef _WINDOWS
+/*
+ * ttopen - this function is called once to set up the terminal device
+ * streams. if called as pine composer, don't mess with
+ * tty modes, but set signal handlers.
+ */
+int
+ttopen(void)
+{
+ if(Pmaster == NULL){
+ Raw(1);
+#ifdef MOUSE
+ if(gmode & MDMOUSE)
+ init_mouse();
+#endif /* MOUSE */
+ xonxoff_proc(preserve_start_stop);
+ }
+
+ picosigs();
+
+ return(1);
+}
+
+
+/*
+ * ttclose - this function gets called just before we go back home to
+ * the command interpreter. If called as pine composer, don't
+ * worry about modes, but set signals to default, pine will
+ * rewire things as needed.
+ */
+int
+ttclose(void)
+{
+ if(Pmaster){
+ signal(SIGHUP, SIG_DFL);
+#ifdef SIGCONT
+ signal(SIGCONT, SIG_DFL);
+#endif
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, SIG_DFL);
+#endif
+ }
+ else{
+ Raw(0);
+#ifdef MOUSE
+ end_mouse();
+#endif
+ }
+
+ return(1);
+}
+
+
+/*
+ * ttgetc - Read a character from the terminal, performing no editing
+ * and doing no echo at all.
+ *
+ * Args: return_on_intr -- Function to get a single character from stdin,
+ * recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ *
+ * Returns: The character read from stdin.
+ * Return_on_intr is returned if read is interrupted.
+ * If read error, BAIL_OUT is returned unless bail_handler is
+ * non-NULL, in which case it is called (and usually it exits).
+ *
+ * If recorder is non-null, it is used to record the keystroke.
+ */
+int
+ttgetc(int return_on_intr, int (*recorder)(int), void (*bail_handler)(void))
+{
+ int c;
+
+ switch(c = read_one_char()){
+ case READ_INTR:
+ return(return_on_intr);
+
+ case BAIL_OUT:
+ if(bail_handler)
+ (*bail_handler)();
+ else
+ return(BAIL_OUT);
+
+ default:
+ return(recorder ? (*recorder)(c) : c);
+ }
+}
+
+
+/*
+ * Simple version of ttgetc with simple error handling
+ *
+ * Args: recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ *
+ * Returns: The character read from stdin.
+ * If read error, BAIL_OUT is returned unless bail_handler is
+ * non-NULL, in which case it is called (and usually it exits).
+ *
+ * If recorder is non-null, it is used to record the keystroke.
+ * Retries if interrupted.
+ */
+int
+simple_ttgetc(int (*recorder)(int), void (*bail_handler)(void))
+{
+ int res;
+ unsigned char c;
+
+ while((res = read(STDIN_FD, &c, 1)) <= 0)
+ if(!(res < 0 && errno == EINTR))
+ (*bail_handler)();
+
+ return(recorder ? (*recorder)((int)c) : (int)c);
+}
+
+
+/*
+ * ttputc - Write a character to the display.
+ */
+int
+ttputc(UCS ucs)
+{
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+ int r, i, width = 0, outchars = 0;
+ int ret = 0;
+
+ if(ucs < 0x80)
+ return(putchar((unsigned char) ucs));
+
+ width = wcellwidth(ucs);
+
+ if(width < 0){
+ width = 1;
+ obuf[outchars++] = '?';
+ }
+ else{
+ /*
+ * Convert the ucs into the multibyte
+ * character that corresponds to the
+ * ucs in the users locale.
+ */
+ outchars = wtomb((char *) obuf, ucs);
+ if(outchars < 0){
+ width = 1;
+ obuf[0] = '?';
+ outchars = 1;
+ }
+ }
+
+ for(i = 0; i < outchars; i++){
+ r = putchar(obuf[i]);
+ ret = (ret == EOF) ? EOF : r;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * ttflush - flush terminal buffer. Does real work where the terminal
+ * output is buffered up. A no-operation on systems where byte
+ * at a time terminal I/O is done.
+ */
+int
+ttflush(void)
+{
+ return(fflush(stdout));
+}
+
+
+/*
+ * ttresize - recompute the screen dimensions if necessary, and then
+ * adjust pico's internal buffers accordingly.
+ */
+void
+ttresize(void)
+{
+ int row = -1, col = -1;
+
+ ttgetwinsz(&row, &col);
+ resize_pico(row, col);
+}
+
+
+
+/*
+ * ttgetwinsz - set global row and column values (if we can get them)
+ * and return.
+ */
+void
+ttgetwinsz(int *row, int *col)
+{
+ extern int _tlines, _tcolumns;
+
+ if(*row < 0)
+ *row = (_tlines > 0) ? _tlines - 1 : NROW - 1;
+ if(*col <= 0)
+ *col = (_tcolumns > 0) ? _tcolumns : NCOL;
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ {
+ struct winsize win;
+
+ if (ioctl(0, TIOCGWINSZ, &win) == 0) { /* set to anything useful.. */
+ if(win.ws_row) /* ... the tty drivers says */
+ *row = win.ws_row - 1;
+
+ if(win.ws_col)
+ *col = win.ws_col;
+ }
+
+ signal(SIGWINCH, winch_handler); /* window size changes */
+ }
+#endif
+
+ if(*col > NLINE-1)
+ *col = NLINE-1;
+}
+
+#else /* _WINDOWS */
+
+#define MARGIN 8 /* size of minimim margin and */
+#define SCRSIZ 64 /* scroll size for extended lines */
+#define MROW 2 /* rows in menu */
+
+/* internal prototypes */
+int mswin_resize (int, int);
+
+/*
+ * Standard terminal interface dispatch table. Fields point to functions
+ * that operate the terminal. All these functions live in mswin.c, but
+ * this structure is defined here because it is specific to pico.
+ */
+TERM term = {
+ 0,
+ 0,
+ MARGIN,
+ MROW,
+ ttopen,
+ NULL,
+ ttclose,
+ NULL, /* was mswin_getc, but not used? */
+ mswin_putc,
+ mswin_flush,
+ mswin_move,
+ mswin_eeol,
+ mswin_eeop,
+ mswin_beep,
+ mswin_rev
+};
+
+/*
+ * This function is called once to set up the terminal device streams.
+ */
+int
+ttopen(void)
+{
+ int rows, columns;
+
+
+ mswin_getscreensize (&rows, &columns);
+ term.t_nrow = rows - 1;
+ term.t_ncol = columns;
+ /* term.t_scrsiz = (columns * 2) / 3; */
+
+ /*
+ * Do we implement optimized character insertion and deletion?
+ * o_insert() and o_delete()
+ */
+ /* inschar = delchar = FALSE; */
+ /* revexist = TRUE; dead code? */
+
+ mswin_setresizecallback (mswin_resize);
+
+ init_mouse();
+
+ return(1);
+}
+
+/*
+ * This function gets called just before we go back home to the command
+ * interpreter.
+ */
+int
+ttclose(void)
+{
+ mswin_clearresizecallback (mswin_resize);
+ return(1);
+}
+
+/*
+ * Flush terminal buffer. Does real work where the terminal output is buffered
+ * up. A no-operation on systems where byte at a time terminal I/O is done.
+ */
+int
+ttflush(void)
+{
+ return(1);
+}
+
+/*
+ * ttresize - recompute the screen dimensions if necessary, and then
+ * adjust pico's internal buffers accordingly.
+ */
+void
+ttresize(void)
+{
+ int row, col;
+
+ mswin_getscreensize(&row, &col);
+ resize_pico (row-1, col);
+}
+
+
+/*
+ * mswin_resize - windows specific callback to set pico's internal tables
+ * to new screen dimensions.
+ */
+int
+mswin_resize(int row, int col)
+{
+ if (wheadp)
+ resize_pico (row-1, col);
+ return (0);
+}
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/tty.h b/pico/osdep/tty.h
new file mode 100644
index 00000000..ca0a7570
--- /dev/null
+++ b/pico/osdep/tty.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: tty.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TTY_INCLUDED
+#define PICO_OSDEP_TTY_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+int ttopen(void);
+int ttclose(void);
+int ttflush(void);
+void ttresize(void);
+#ifndef _WINDOWS
+int ttgetc(int, int (*recorder)(int), void (*bail_handler)(void));
+int simple_ttgetc(int (*recorder)(int), void (*bail_handler)(void));
+int ttputc(UCS);
+void ttgetwinsz(int *, int *);
+#endif /* !_WINDOWS */
+
+#endif /* PICO_OSDEP_TTY_INCLUDED */