diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-01-18 12:04:32 +0000 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-01-18 12:04:32 +0000 |
commit | e2d147e01aa8bd00b79c8288fa1c490d187c9e82 (patch) | |
tree | 0e50849418106307cd3b6ae5346d11cdcec8756c /progs | |
download | ncurses-e2d147e01aa8bd00b79c8288fa1c490d187c9e82.tar.gz |
Tarball conversion
Diffstat (limited to 'progs')
-rwxr-xr-x | progs/MKtermsort.sh | 164 | ||||
-rw-r--r-- | progs/Makefile.in | 320 | ||||
-rwxr-xr-x | progs/capconvert | 256 | ||||
-rw-r--r-- | progs/clear.c | 59 | ||||
-rwxr-xr-x | progs/clear.sh | 29 | ||||
-rw-r--r-- | progs/dump_entry.c | 1273 | ||||
-rw-r--r-- | progs/dump_entry.h | 80 | ||||
-rw-r--r-- | progs/infocmp.c | 1657 | ||||
-rw-r--r-- | progs/modules | 45 | ||||
-rw-r--r-- | progs/progs.priv.h | 192 | ||||
-rw-r--r-- | progs/tabs.c | 510 | ||||
-rw-r--r-- | progs/tic.c | 1714 | ||||
-rw-r--r-- | progs/toe.c | 525 | ||||
-rw-r--r-- | progs/tput.c | 447 | ||||
-rw-r--r-- | progs/transform.c | 79 | ||||
-rw-r--r-- | progs/tset.c | 1349 |
16 files changed, 8699 insertions, 0 deletions
diff --git a/progs/MKtermsort.sh b/progs/MKtermsort.sh new file mode 100755 index 0000000..2247f14 --- /dev/null +++ b/progs/MKtermsort.sh @@ -0,0 +1,164 @@ +#!/bin/sh +# $Id: MKtermsort.sh,v 1.10 2008/07/12 20:22:54 tom Exp $ +# +# MKtermsort.sh -- generate indirection vectors for the various sort methods +# +############################################################################## +# Copyright (c) 1998-2003,2008 Free Software Foundation, Inc. # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, distribute # +# with modifications, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the # +# following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # +# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +# Except as contained in this notice, the name(s) of the above copyright # +# holders shall not be used in advertising or otherwise to promote the sale, # +# use or other dealings in this Software without prior written # +# authorization. # +############################################################################## +# +# The output of this script is C source for nine arrays that list three sort +# orders for each of the three different classes of terminfo capabilities. +# +# keep the order independent of locale: +if test "${LANGUAGE+set}" = set; then LANGUAGE=C; export LANGUAGE; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi +if test "${LC_COLLATE+set}" = set; then LC_COLLATE=C; export LC_COLLATE; fi +# +AWK=${1-awk} +DATA=${2-../include/Caps} + +data=data$$ +trap 'rm -f $data' 1 2 5 15 +sed -e 's/[ ][ ]*/ /g' < $DATA >$data +DATA=$data + +echo "/*"; +echo " * termsort.c --- sort order arrays for use by infocmp."; +echo " *"; +echo " * Note: this file is generated using MKtermsort.sh, do not edit by hand."; +echo " */"; + +echo "static const PredIdx bool_terminfo_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "bool" {printf("%s\t%d\n", $2, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx num_terminfo_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "num" {printf("%s\t%d\n", $2, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx str_terminfo_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "str" {printf("%s\t%d\n", $2, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx bool_variable_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "bool" {printf("%s\t%d\n", $1, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx num_variable_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "num" {printf("%s\t%d\n", $1, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx str_variable_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "str" {printf("%s\t%d\n", $1, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx bool_termcap_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "bool" {printf("%s\t%d\n", $4, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx num_termcap_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "num" {printf("%s\t%d\n", $4, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const PredIdx str_termcap_sort[] = {"; +$AWK <$DATA ' +BEGIN {i = 0;} +/^#/ {next;} +$3 == "str" {printf("%s\t%d\n", $4, i++);} +' | sort | $AWK '{print "\t", $2, ",\t/* ", $1, " */";}'; +echo "};"; +echo ""; + +echo "static const bool bool_from_termcap[] = {"; +$AWK <$DATA ' +$3 == "bool" && substr($7, 1, 1) == "-" {print "\tFALSE,\t/* ", $2, " */";} +$3 == "bool" && substr($7, 1, 1) == "Y" {print "\tTRUE,\t/* ", $2, " */";} +' +echo "};"; +echo ""; + +echo "static const bool num_from_termcap[] = {"; +$AWK <$DATA ' +$3 == "num" && substr($7, 1, 1) == "-" {print "\tFALSE,\t/* ", $2, " */";} +$3 == "num" && substr($7, 1, 1) == "Y" {print "\tTRUE,\t/* ", $2, " */";} +' +echo "};"; +echo ""; + +echo "static const bool str_from_termcap[] = {"; +$AWK <$DATA ' +$3 == "str" && substr($7, 1, 1) == "-" {print "\tFALSE,\t/* ", $2, " */";} +$3 == "str" && substr($7, 1, 1) == "Y" {print "\tTRUE,\t/* ", $2, " */";} +' +echo "};"; +echo ""; + +rm -f $data diff --git a/progs/Makefile.in b/progs/Makefile.in new file mode 100644 index 0000000..bdb8a0f --- /dev/null +++ b/progs/Makefile.in @@ -0,0 +1,320 @@ +# $Id: Makefile.in,v 1.85 2010/11/27 21:45:27 tom Exp $ +############################################################################## +# Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, distribute # +# with modifications, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the # +# following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # +# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +# Except as contained in this notice, the name(s) of the above copyright # +# holders shall not be used in advertising or otherwise to promote the sale, # +# use or other dealings in this Software without prior written # +# authorization. # +############################################################################## +# +# Author: Thomas E. Dickey 1996-on +# +# Makefile for ncurses source code. +# +# This makes the ncurses utility programs. +# +# The variable 'srcdir' refers to the source-distribution, and can be set with +# the configure script by "--srcdir=DIR". +# +# The rules are organized to produce the libraries for the configured models, +# and the programs with the configured default model. + +# turn off _all_ suffix rules; we'll generate our own +.SUFFIXES: + +SHELL = /bin/sh +VPATH = @srcdir@ +THIS = Makefile + +CF_MFLAGS = @cf_cv_makeflags@ +@SET_MAKE@ + +x = @EXEEXT@ +o = .@OBJEXT@ + +MODEL = ../@DFT_OBJ_SUBDIR@ +DESTDIR = @DESTDIR@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +includedir = @includedir@ +datadir = @datadir@ + +LIBTOOL = @LIBTOOL@ +LIBTOOL_CLEAN = @LIB_CLEAN@ +LIBTOOL_COMPILE = @LIB_COMPILE@ +LIBTOOL_LINK = @LIB_LINK@ +LIBTOOL_INSTALL = @LIB_INSTALL@ +LIBTOOL_UNINSTALL = @LIB_UNINSTALL@ + +INSTALL = @INSTALL@ +INSTALL_PROG = @INSTALL_PROGRAM@ +transform = @program_transform_name@ + +AWK = @AWK@ +LN_S = @LN_S@ + +CTAGS = @CTAGS@ +ETAGS = @ETAGS@ + +CC = @CC@ +CPP = @CPP@ +CFLAGS = @CFLAGS@ + +INCDIR = $(top_srcdir)/include +CPPFLAGS = -I../progs -I$(srcdir) -DHAVE_CONFIG_H @CPPFLAGS@ + +CCFLAGS = $(CPPFLAGS) $(CFLAGS) + +CFLAGS_LIBTOOL = $(CCFLAGS) +CFLAGS_NORMAL = $(CCFLAGS) +CFLAGS_DEBUG = $(CCFLAGS) @CC_G_OPT@ -DTRACE +CFLAGS_PROFILE = $(CCFLAGS) -pg +CFLAGS_SHARED = $(CCFLAGS) @CC_SHARED_OPTS@ + +CFLAGS_DEFAULT = $(CFLAGS_@DFT_UPR_MODEL@) + +REL_VERSION = @cf_cv_rel_version@ +ABI_VERSION = @cf_cv_abi_version@ +LOCAL_LIBDIR = @top_builddir@/lib + +LD = @LD@ +LINK = @LINK_PROGS@ $(LIBTOOL_LINK) +LDFLAGS = @EXTRA_LDFLAGS@ @LDFLAGS@ + +LDFLAGS_LIBTOOL = $(LDFLAGS) $(CFLAGS_LIBTOOL) +LDFLAGS_NORMAL = $(LDFLAGS) $(CFLAGS_NORMAL) +LDFLAGS_DEBUG = $(LDFLAGS) $(CFLAGS_DEBUG) +LDFLAGS_PROFILE = $(LDFLAGS) $(CFLAGS_PROFILE) +LDFLAGS_SHARED = $(LDFLAGS) $(CFLAGS_SHARED) @LD_SHARED_OPTS@ + +LDFLAGS_DEFAULT = $(LDFLAGS_@DFT_UPR_MODEL@) + +LIBS_TIC = @LDFLAGS_STATIC@ @TICS_ARGS@ @TINFO_ARGS@ @LDFLAGS_SHARED@ @LD_MODEL@ @LIBS@ +LDFLAGS_TIC = $(LDFLAGS_@DFT_UPR_MODEL@) $(LIBS_TIC) + +LIBS_TINFO = @LDFLAGS_STATIC@ @TINFO_ARGS@ @LDFLAGS_SHARED@ @LD_MODEL@ @LIBS@ +LDFLAGS_TINFO = $(LDFLAGS_@DFT_UPR_MODEL@) $(LIBS_TINFO) + +LINT = @LINT@ +LINT_OPTS = @LINT_OPTS@ +LINT_LIBS = -lncurses @LIBS@ + +AUTO_SRC = \ + termsort.c \ + transform.h + +# tic relies on direct access to the terminfo database +GET_PROGS = infocmp$x clear$x tabs$x tput$x tset$x toe$x +PUT_PROGS = @MAKE_TERMINFO@ tic$x +PROGS = $(PUT_PROGS) $(GET_PROGS) + +# Default library, for linking applications +DEPS_CURSES = ../lib/@LIB_PREFIX@ncurses@DFT_DEP_SUFFIX@ + +HEADER_DEPS = \ + ../include/curses.h \ + $(INCDIR)/term_entry.h \ + $(INCDIR)/tic.h \ + $(INCDIR)/nc_alloc.h + +################################################################################ +all: $(AUTO_SRC) $(PROGS) + +sources: $(AUTO_SRC) + +install: $(AUTO_SRC) install.progs +uninstall: uninstall.progs + +# this line simplifies the configure-script +libs \ +install.libs \ +uninstall.libs: + +TRANSFORM = sed 's/$x$$//'|sed '$(transform)'|sed 's/$$/$x/' + +# transformed names for installing files +actual_captoinfo = `echo captoinfo$x| $(TRANSFORM)` +actual_clear = `echo clear$x| $(TRANSFORM)` +actual_infocmp = `echo infocmp$x| $(TRANSFORM)` +actual_infotocap = `echo infotocap$x| $(TRANSFORM)` +actual_init = `echo init$x| $(TRANSFORM)` +actual_reset = `echo reset$x| $(TRANSFORM)` +actual_tabs = `echo tabs$x| $(TRANSFORM)` +actual_tic = `echo tic$x| $(TRANSFORM)` +actual_toe = `echo toe$x| $(TRANSFORM)` +actual_tput = `echo tput$x| $(TRANSFORM)` +actual_tset = `echo tset$x| $(TRANSFORM)` + +# transformed names for comparing at runtime +define_captoinfo = `echo captoinfo| $(TRANSFORM)` +define_infotocap = `echo infotocap| $(TRANSFORM)` +define_init = `echo init| $(TRANSFORM)` +define_reset = `echo reset| $(TRANSFORM)` + +transform.h : + echo "#ifndef __TRANSFORM_H" >$@ + echo "#define __TRANSFORM_H 1" >>$@ + echo "#include <progs.priv.h>" >>$@ + echo "extern bool same_program(const char *, const char *);" >>$@ + -sh -c 'if test -n "$x" ; then echo "#define SUFFIX_IGNORED \"$x\"">>$@; fi' + echo "#define PROG_CAPTOINFO \"$(define_captoinfo)\"" >>$@ + echo "#define PROG_INFOTOCAP \"$(define_infotocap)\"" >>$@ + echo "#define PROG_RESET \"$(define_reset)\"" >>$@ + echo "#define PROG_INIT \"$(define_init)\"" >>$@ + echo "#endif /* __TRANSFORM_H */" >>$@ + +install.progs: $(AUTO_SRC) $(PROGS) $(DESTDIR)$(bindir) +@MAKE_TERMINFO@ $(LIBTOOL_INSTALL) $(INSTALL_PROG) tic$x $(DESTDIR)$(bindir)/$(actual_tic) +@MAKE_TERMINFO@ $(LIBTOOL_INSTALL) $(INSTALL_PROG) toe$x $(DESTDIR)$(bindir)/$(actual_toe) +@MAKE_TERMINFO@ @echo "linking $(actual_infotocap) to $(actual_tic)" +@MAKE_TERMINFO@ -@rm -f $(DESTDIR)$(bindir)/$(actual_infotocap) +@MAKE_TERMINFO@ (cd $(DESTDIR)$(bindir) && $(LN_S) $(actual_tic) $(actual_infotocap)) +@MAKE_TERMINFO@ @echo "linking $(actual_captoinfo) to $(actual_tic)" +@MAKE_TERMINFO@ -@rm -f $(DESTDIR)$(bindir)/$(actual_captoinfo) +@MAKE_TERMINFO@ (cd $(DESTDIR)$(bindir) && $(LN_S) $(actual_tic) $(actual_captoinfo)) + $(LIBTOOL_INSTALL) $(INSTALL_PROG) infocmp$x $(DESTDIR)$(bindir)/$(actual_infocmp) + $(LIBTOOL_INSTALL) $(INSTALL_PROG) clear$x $(DESTDIR)$(bindir)/$(actual_clear) + $(LIBTOOL_INSTALL) $(INSTALL_PROG) tabs$x $(DESTDIR)$(bindir)/$(actual_tabs) + $(LIBTOOL_INSTALL) $(INSTALL_PROG) tput$x $(DESTDIR)$(bindir)/$(actual_tput) + $(LIBTOOL_INSTALL) $(INSTALL_PROG) tset$x $(DESTDIR)$(bindir)/$(actual_tset) + @echo "linking $(actual_reset) to $(actual_tset)" + -@rm -f $(DESTDIR)$(bindir)/$(actual_reset) + (cd $(DESTDIR)$(bindir) && $(LN_S) $(actual_tset) $(actual_reset)) + +uninstall.progs: +@MAKE_TERMINFO@ -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_tic) +@MAKE_TERMINFO@ -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_toe) +@MAKE_TERMINFO@ -@rm -f $(DESTDIR)$(bindir)/$(actual_captoinfo) +@MAKE_TERMINFO@ -@rm -f $(DESTDIR)$(bindir)/$(actual_infotocap) + -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_infocmp) + -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_clear) + -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_tabs) + -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_tput) + -@$(LIBTOOL_CLEAN) rm -f $(DESTDIR)$(bindir)/$(actual_tset) + -@rm -f $(DESTDIR)$(bindir)/$(actual_reset) + +$(DESTDIR)$(bindir) : + mkdir -p $@ + +# +# Utilities normally built by make all start here +# + +DEPS_TIC = \ + $(MODEL)/tic$o \ + $(MODEL)/dump_entry$o \ + $(MODEL)/transform$o + +tic$x: $(DEPS_TIC) $(DEPS_CURSES) transform.h + @ECHO_LINK@ $(LINK) $(DEPS_TIC) $(LDFLAGS_TIC) -o $@ + +DEPS_TOE = \ + $(MODEL)/toe$o + +toe$x: $(DEPS_TOE) $(DEPS_CURSES) + @ECHO_LINK@ $(LINK) $(DEPS_TOE) $(LDFLAGS_TIC) -o $@ + +DEPS_CLEAR = \ + $(MODEL)/clear$o + +clear$x: $(DEPS_CLEAR) $(DEPS_CURSES) + @ECHO_LINK@ $(LINK) $(DEPS_CLEAR) $(LDFLAGS_TINFO) -o $@ + +DEPS_TABS = \ + $(MODEL)/tabs$o + +tabs$x: $(DEPS_TABS) $(DEPS_TABS) + @ECHO_LINK@ $(LINK) $(DEPS_TABS) $(LDFLAGS_TINFO) -o $@ + +DEPS_TPUT = \ + $(MODEL)/tput$o \ + $(MODEL)/transform$o + +tput$x: $(DEPS_TPUT) $(DEPS_CURSES) transform.h + @ECHO_LINK@ $(LINK) $(DEPS_TPUT) $(LDFLAGS_TINFO) -o $@ + +DEPS_INFOCMP = \ + $(MODEL)/infocmp$o \ + $(MODEL)/dump_entry$o + +infocmp$x: $(DEPS_INFOCMP) $(DEPS_CURSES) + @ECHO_LINK@ $(LINK) $(DEPS_INFOCMP) $(LDFLAGS_TIC) -o $@ + +DEPS_TSET = \ + $(MODEL)/tset$o \ + $(MODEL)/transform$o + +tset$x: $(DEPS_TSET) $(DEPS_CURSES) transform.h + @ECHO_LINK@ $(LINK) $(DEPS_TSET) $(LDFLAGS_TINFO) -o $@ + +termsort.c: $(srcdir)/MKtermsort.sh + sh $(srcdir)/MKtermsort.sh $(AWK) $(srcdir)/../include/@TERMINFO_CAPS@ >$@ + +# +# Utility productions start here +# + +tags: + $(CTAGS) *.[ch] + +@MAKE_UPPER_TAGS@TAGS: +@MAKE_UPPER_TAGS@ $(ETAGS) *.[ch] + +mostlyclean :: + -rm -f core tags TAGS *~ *.bak *.i *.ln *.atac trace + +clean :: mostlyclean + -sh -c "if test -n '$x' ; then $(MAKE) clean x=''; fi" + -rm -f $(AUTO_SRC) + -rm -f $(PROGS) + -rm -rf .libs + +distclean :: clean + -rm -f Makefile + +realclean :: distclean + +# These rules are used to allow "make -n" to work on a clean directory-tree +../include/hashsize.h \ +../include/parametrized.h \ +../include/term.h : + cd ../include; $(MAKE) $(CF_MFLAGS) +$(DEPS_CURSES) : + cd ../ncurses; $(MAKE) $(CF_MFLAGS) + +lint: +@MAKE_TERMINFO@ $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/tic.c $(srcdir)/dump_entry.c $(LINT_LIBS) +@MAKE_TERMINFO@ $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/toe.c $(srcdir)/dump_entry.c $(LINT_LIBS) + $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/clear.c $(LINT_LIBS) + $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/infocmp.c $(srcdir)/dump_entry.c $(LINT_LIBS) + $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/tabs.c $(LINT_LIBS) + $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/tput.c $(LINT_LIBS) + $(LINT) $(LINT_OPTS) $(CPPFLAGS) $(srcdir)/tset.c $(srcdir)/dump_entry.c $(LINT_LIBS) + +############################################################################### +# The remainder of this file is automatically generated during configuration +############################################################################### diff --git a/progs/capconvert b/progs/capconvert new file mode 100755 index 0000000..8199bbf --- /dev/null +++ b/progs/capconvert @@ -0,0 +1,256 @@ +#!/bin/sh +############################################################################## +# Copyright (c) 1998,2006 Free Software Foundation, Inc. # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, distribute # +# with modifications, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the # +# following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # +# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +# Except as contained in this notice, the name(s) of the above copyright # +# holders shall not be used in advertising or otherwise to promote the sale, # +# use or other dealings in this Software without prior written # +# authorization. # +############################################################################## +# $Id: capconvert,v 1.4 2006/04/22 21:46:17 tom Exp $ +# +# capconvert -- automated conversion from termcap to terminfo +# + +echo "This script tries to automatically set you up so that your applications" +echo "that now use termcap can use terminfo and the ncurses library." +echo "" + +# Note, except for telling if we're running under xterm we don't use TERM at +# all. This is because BSD users not infrequently have multiple termtypes +# selected by conditionals in tset -- unless they're xterm users, in which +# case they're on a workstation and probably don't. + +# Check to make sure TERMINFO is not already defined +if test -n "$TERMINFO" +then + echo "TERMINFO is already defined in your environment. This means" + echo "you already have a local terminfo tree, so you do not need any" + echo "conversion." + if test ! -d $TERMINFO ; then + echo "Caution: TERMINFO does not point to a directory!" + fi + exit; +fi + +# Check to see if terminfo is present in one of the standard locations. +terminfo=no +for p in $TERMINFO \ + /usr/lib/terminfo \ + /usr/share/lib/terminfo \ + /usr/share/terminfo \ + /usr/local/lib/terminfo \ + /usr/local/share/terminfo +do + if test -d $p ; then + terminfo=yes + break + fi +done + +if test $terminfo = yes +then + echo "Your system already has a system-wide terminfo tree." + echo "" + if test -z "$TERMCAP" + then + echo "You have no TERMCAP variable set, so we are done." + # Assumes the terminfo master covers all canned terminal types + exit; + fi + if test "$TERM" = "xterm" + then + echo "You are running xterm, which usually sets TERMCAP itself." + echo "We can ignore this, because terminfo knows about xterm." + echo "So you will just use the system-wide terminfo tree." + exit; + else + echo "We will have to make a local one for you anyway, to capture the effect" + echo "of your TERMCAP variable." + fi +else + echo "No system-wide terminfo tree. We will make you a local one." +fi +echo ""; + +# Check if test -x works (it's not portable, but useful) +OPT="-x" +TMP=test$$; touch $TMP && chmod 755 $TMP +if test $OPT $TMP ; then + chmod 644 $TMP + test $OPT $TMP && OPT="-f" +else + OPT="-f" +fi +rm -f $TMP + +# First step -- go find tic +TIC= +IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}:" +for x in $PATH . +do + if test $OPT $x/tic + then + TIC=$x/tic + break + fi +done +IFS="$ac_save_ifs" + +if test -n "$TIC" +then + echo "I see tic at $TIC." + case $TIC in # (vi + ./tic) + if test $OPT ../misc/shlib ; then + TIC="../misc/shlib $TIC" + fi + ;; + esac +else + echo "You do not have tic installed anywhere I can see, please fix that." + exit; +fi +echo ""; + +# We have tic. Either there's no system terminfo tree or there is one but +# the user has a TERMCAP variable that may modify a stock description. +# + +# Make the user a terminfo directory +if test -d $HOME/.terminfo +then + echo "It appears you already have a private terminfo directory" + echo "at $HOME/.terminfo; this seems odd, because TERMINFO" + echo "is not defined. I am not going to second-guess this -- if you" + echo "really want me to try auto-configuring for you, remove or" + echo "rename $HOME/terminfo and run me again." + exit; +else + echo "I am creating your private terminfo directory at $HOME/.terminfo" + mkdir $HOME/.terminfo + # Ensure that that's where tic's compilation results. + # This isn't strictly necessary with a 1.9.7 or later tic. + TERMINFO="$HOME/.terminfo"; export TERMINFO +fi +echo ""; + +# Find a terminfo source to work from +if test -f ../misc/terminfo.src +then + echo "I see the terminfo master source is handy; I will use that." + master=../misc/terminfo.src +else + # Ooops...looks like we're running from somewhere other than the + # progs directory of an ncurses source tree. + master=`find $HOME -name "*terminfo.src" -print` + mcount=`echo $master | wc -l` + case $mcount in + 0) + echo "I can not find a terminfo source file anywhere under your home directory." + echo "There should be a file called terminfo.src somewhere in your" + echo "ncurses distribution; please put it in your home directotry" + echo "and run me again (it does not have to live there permanently)." + exit; + ;; + 1) + echo "I see a file called $master." + echo "I am going to assume this is the terminfo source included with" + echo "the ncurses distribution. If this assumption is wrong, please" + echo "interrupt me now! OK to continue?" + read ans; + ;; + 2) + echo "I see more than one possible terminfo source. Here they are:" + echo $master | sed "/^/s// /"; + while : + do + echo "Please tell me which one to use:" + read master; + if test -f $master + then + break + else + echo "That file does not exist. Try again?"; + fi + done + ;; + esac +fi +echo ""; + +# Now that we have a master, compile it into the local tree +echo "OK, now I will make your private terminfo tree. This may take a bit..." +# +# Kluge alert: we compile terminfo.src in two pieces because a lot of machines +# with < 16MB RAM choke on tic's core-hog habits. +trap "rm -f tsplit$$.*" 0 1 2 5 15 +sed -n $master \ + -e '1,/SPLIT HERE/w 'tsplit$$.01 \ + -e '/SPLIT HERE/,$w 'tsplit$$.02 \ + 2>/dev/null +for x in tsplit$$.*; do eval $TIC $x; done +rm tsplit$$.* +trap 0 1 2 5 15 +# +echo "You now have a private tree under $HOME/.terminfo;" +echo "the ncurses library will automatically read from it," +echo "and ncurses tic will automatically compile entries to it." + +# We're done unless user has a .termcap file or equivalent named by TERMCAP +if test -z "$TERMCAP" +then + echo "You have no TERMCAP set, so we are done." +fi + +# OK, here comes the nasty case...user has a TERMCAP. Instead of +# trying to follow all the convolutions of the relationship between +# TERM and TERMCAP (partly because it's too painful, and partly because +# we don't actually know what TERM will be nor even if it always has +# the same value for this user) we do the following three steps... + +if test -f $HOME/.termcap +then + echo 'I see you have a $HOME/.termcap file. I will compile that.' + eval $TIC $HOME/.termcap + echo "Done." + echo "Note that editing $HOME/.termcap will no longer change the data curses sees." +elif test -f "$TERMCAP" +then + echo "Your TERMCAP names the file $TERMCAP. I will compile that." + eval $TIC $TERMCAP + echo "Done." + echo "Note that editing $TERMCAP will no longer change the data curses sees." +else + echo "Your TERMCAP value appears to be an entry in termcap format." + echo "I will compile it." + echo $TERMCAP >myterm$$ + eval $TIC myterm$$ + rm myterm$$ + echo "Done." + echo "Note that editing TERMCAP will no longer change the data curses sees." +fi +echo "To do that, decompile the terminal decription you want with infocmp(1)," +echo "edit to taste, and recompile using tic(1)." + +# capconvert ends here + diff --git a/progs/clear.c b/progs/clear.c new file mode 100644 index 0000000..9f5a543 --- /dev/null +++ b/progs/clear.c @@ -0,0 +1,59 @@ +/**************************************************************************** + * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996-on * + ****************************************************************************/ + +/* + * clear.c -- clears the terminal's screen + */ + +#define USE_LIBTINFO +#include <progs.priv.h> + +MODULE_ID("$Id: clear.c,v 1.11 2007/10/13 22:16:02 tom Exp $") + +static int +putch(int c) +{ + return putchar(c); +} + +int +main( + int argc GCC_UNUSED, + char *argv[]GCC_UNUSED) +{ + setupterm((char *) 0, STDOUT_FILENO, (int *) 0); + ExitProgram((tputs(clear_screen, lines > 0 ? lines : 1, putch) == ERR) + ? EXIT_FAILURE + : EXIT_SUCCESS); +} diff --git a/progs/clear.sh b/progs/clear.sh new file mode 100755 index 0000000..f26112b --- /dev/null +++ b/progs/clear.sh @@ -0,0 +1,29 @@ +#!/bin/sh +############################################################################## +# Copyright (c) 1998,2006 Free Software Foundation, Inc. # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, distribute # +# with modifications, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the # +# following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # +# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +# Except as contained in this notice, the name(s) of the above copyright # +# holders shall not be used in advertising or otherwise to promote the sale, # +# use or other dealings in this Software without prior written # +# authorization. # +############################################################################## +exec tput clear diff --git a/progs/dump_entry.c b/progs/dump_entry.c new file mode 100644 index 0000000..485bbbd --- /dev/null +++ b/progs/dump_entry.c @@ -0,0 +1,1273 @@ +/**************************************************************************** + * Copyright (c) 1998-2008,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996 on * + ****************************************************************************/ + +#define __INTERNAL_CAPS_VISIBLE +#include <progs.priv.h> + +#include "dump_entry.h" +#include "termsort.c" /* this C file is generated */ +#include <parametrized.h> /* so is this */ + +MODULE_ID("$Id: dump_entry.c,v 1.89 2010/05/01 22:04:08 tom Exp $") + +#define INDENT 8 +#define DISCARD(string) string = ABSENT_STRING +#define PRINTF (void) printf + +#define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array)) + +typedef struct { + char *text; + size_t used; + size_t size; +} DYNBUF; + +static int tversion; /* terminfo version */ +static int outform; /* output format to use */ +static int sortmode; /* sort mode to use */ +static int width = 60; /* max line width for listings */ +static int column; /* current column, limited by 'width' */ +static int oldcol; /* last value of column before wrap */ +static bool pretty; /* true if we format if-then-else strings */ + +static char *save_sgr; + +static DYNBUF outbuf; +static DYNBUF tmpbuf; + +/* indirection pointers for implementing sort and display modes */ +static const PredIdx *bool_indirect, *num_indirect, *str_indirect; +static NCURSES_CONST char *const *bool_names; +static NCURSES_CONST char *const *num_names; +static NCURSES_CONST char *const *str_names; + +static const char *separator, *trailer; + +/* cover various ports and variants of terminfo */ +#define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */ +#define V_SVR1 1 /* SVR1, Ultrix */ +#define V_HPUX 2 /* HP/UX */ +#define V_AIX 3 /* AIX */ +#define V_BSD 4 /* BSD */ + +#if NCURSES_XNAMES +#define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T')) +#else +#define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T') +#endif + +#define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n)) + +#if NCURSES_XNAMES +#define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j])) +#define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j])) +#define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j])) +#else +#define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j]) +#define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j]) +#define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j]) +#endif + +static void +strncpy_DYN(DYNBUF * dst, const char *src, size_t need) +{ + size_t want = need + dst->used + 1; + if (want > dst->size) { + dst->size += (want + 1024); /* be generous */ + dst->text = typeRealloc(char, dst->size, dst->text); + } + (void) strncpy(dst->text + dst->used, src, need); + dst->used += need; + dst->text[dst->used] = 0; +} + +static void +strcpy_DYN(DYNBUF * dst, const char *src) +{ + if (src == 0) { + dst->used = 0; + strcpy_DYN(dst, ""); + } else { + strncpy_DYN(dst, src, strlen(src)); + } +} + +#if NO_LEAKS +static void +free_DYN(DYNBUF * p) +{ + if (p->text != 0) + free(p->text); + p->text = 0; + p->size = 0; + p->used = 0; +} + +void +_nc_leaks_dump_entry(void) +{ + free_DYN(&outbuf); + free_DYN(&tmpbuf); +} +#endif + +#define NameTrans(check,result) \ + if (OkIndex(np->nte_index, check) \ + && check[np->nte_index]) \ + return (result[np->nte_index]) + +NCURSES_CONST char * +nametrans(const char *name) +/* translate a capability name from termcap to terminfo */ +{ + const struct name_table_entry *np; + + if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) + switch (np->nte_type) { + case BOOLEAN: + NameTrans(bool_from_termcap, boolcodes); + break; + + case NUMBER: + NameTrans(num_from_termcap, numcodes); + break; + + case STRING: + NameTrans(str_from_termcap, strcodes); + break; + } + + return (0); +} + +void +dump_init(const char *version, int mode, int sort, int twidth, int traceval, + bool formatted) +/* set up for entry display */ +{ + width = twidth; + pretty = formatted; + + /* versions */ + if (version == 0) + tversion = V_ALLCAPS; + else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1") + || !strcmp(version, "Ultrix")) + tversion = V_SVR1; + else if (!strcmp(version, "HP")) + tversion = V_HPUX; + else if (!strcmp(version, "AIX")) + tversion = V_AIX; + else if (!strcmp(version, "BSD")) + tversion = V_BSD; + else + tversion = V_ALLCAPS; + + /* implement display modes */ + switch (outform = mode) { + case F_LITERAL: + case F_TERMINFO: + bool_names = boolnames; + num_names = numnames; + str_names = strnames; + separator = twidth ? ", " : ","; + trailer = "\n\t"; + break; + + case F_VARIABLE: + bool_names = boolfnames; + num_names = numfnames; + str_names = strfnames; + separator = twidth ? ", " : ","; + trailer = "\n\t"; + break; + + case F_TERMCAP: + case F_TCONVERR: + bool_names = boolcodes; + num_names = numcodes; + str_names = strcodes; + separator = ":"; + trailer = "\\\n\t:"; + break; + } + + /* implement sort modes */ + switch (sortmode = sort) { + case S_NOSORT: + if (traceval) + (void) fprintf(stderr, + "%s: sorting by term structure order\n", _nc_progname); + break; + + case S_TERMINFO: + if (traceval) + (void) fprintf(stderr, + "%s: sorting by terminfo name order\n", _nc_progname); + bool_indirect = bool_terminfo_sort; + num_indirect = num_terminfo_sort; + str_indirect = str_terminfo_sort; + break; + + case S_VARIABLE: + if (traceval) + (void) fprintf(stderr, + "%s: sorting by C variable order\n", _nc_progname); + bool_indirect = bool_variable_sort; + num_indirect = num_variable_sort; + str_indirect = str_variable_sort; + break; + + case S_TERMCAP: + if (traceval) + (void) fprintf(stderr, + "%s: sorting by termcap name order\n", _nc_progname); + bool_indirect = bool_termcap_sort; + num_indirect = num_termcap_sort; + str_indirect = str_termcap_sort; + break; + } + + if (traceval) + (void) fprintf(stderr, + "%s: width = %d, tversion = %d, outform = %d\n", + _nc_progname, width, tversion, outform); +} + +static TERMTYPE *cur_type; + +static int +dump_predicate(PredType type, PredIdx idx) +/* predicate function to use for ordinary decompilation */ +{ + switch (type) { + case BOOLEAN: + return (cur_type->Booleans[idx] == FALSE) + ? FAIL : cur_type->Booleans[idx]; + + case NUMBER: + return (cur_type->Numbers[idx] == ABSENT_NUMERIC) + ? FAIL : cur_type->Numbers[idx]; + + case STRING: + return (cur_type->Strings[idx] != ABSENT_STRING) + ? (int) TRUE : FAIL; + } + + return (FALSE); /* pacify compiler */ +} + +static void set_obsolete_termcaps(TERMTYPE *tp); + +/* is this the index of a function key string? */ +#define FNKEY(i) (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268)) + +/* + * If we configure with a different Caps file, the offsets into the arrays + * will change. So we use an address expression. + */ +#define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0])) +#define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0])) +#define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0])) + +static bool +version_filter(PredType type, PredIdx idx) +/* filter out capabilities we may want to suppress */ +{ + switch (tversion) { + case V_ALLCAPS: /* SVr4, XSI Curses */ + return (TRUE); + + case V_SVR1: /* System V Release 1, Ultrix */ + switch (type) { + case BOOLEAN: + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); + case NUMBER: + return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); + case STRING: + return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE); + } + break; + + case V_HPUX: /* Hewlett-Packard */ + switch (type) { + case BOOLEAN: + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); + case NUMBER: + return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE); + case STRING: + if (idx <= STR_IDX(prtr_non)) + return (TRUE); + else if (FNKEY(idx)) /* function keys */ + return (TRUE); + else if (idx == STR_IDX(plab_norm) + || idx == STR_IDX(label_on) + || idx == STR_IDX(label_off)) + return (TRUE); + else + return (FALSE); + } + break; + + case V_AIX: /* AIX */ + switch (type) { + case BOOLEAN: + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); + case NUMBER: + return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); + case STRING: + if (idx <= STR_IDX(prtr_non)) + return (TRUE); + else if (FNKEY(idx)) /* function keys */ + return (TRUE); + else + return (FALSE); + } + break; + +#define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \ + type##_from_termcap[idx]) + + case V_BSD: /* BSD */ + switch (type) { + case BOOLEAN: + return is_termcap(bool); + case NUMBER: + return is_termcap(num); + case STRING: + return is_termcap(str); + } + break; + } + + return (FALSE); /* pacify the compiler */ +} + +static void +trim_trailing(void) +{ + while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ') + outbuf.text[--outbuf.used] = '\0'; +} + +static void +force_wrap(void) +{ + oldcol = column; + trim_trailing(); + strcpy_DYN(&outbuf, trailer); + column = INDENT; +} + +static void +wrap_concat(const char *src) +{ + unsigned need = strlen(src); + unsigned want = strlen(separator) + need; + + if (column > INDENT + && column + (int) want > width) { + force_wrap(); + } + strcpy_DYN(&outbuf, src); + strcpy_DYN(&outbuf, separator); + column += (int) need; +} + +#define IGNORE_SEP_TRAIL(first,last,sep_trail) \ + if ((size_t)(last - first) > sizeof(sep_trail)-1 \ + && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \ + first += sizeof(sep_trail)-2 + +/* Returns the nominal length of the buffer assuming it is termcap format, + * i.e., the continuation sequence is treated as a single character ":". + * + * There are several implementations of termcap which read the text into a + * fixed-size buffer. Generally they strip the newlines from the text, but may + * not do it until after the buffer is read. Also, "tc=" resolution may be + * expanded in the same buffer. This function is useful for measuring the size + * of the best fixed-buffer implementation; the worst case may be much worse. + */ +#ifdef TEST_TERMCAP_LENGTH +static int +termcap_length(const char *src) +{ + static const char pattern[] = ":\\\n\t:"; + + int len = 0; + const char *const t = src + strlen(src); + + while (*src != '\0') { + IGNORE_SEP_TRAIL(src, t, pattern); + src++; + len++; + } + return len; +} +#else +#define termcap_length(src) strlen(src) +#endif + +static void +indent_DYN(DYNBUF * buffer, int level) +{ + int n; + + for (n = 0; n < level; n++) + strncpy_DYN(buffer, "\t", 1); +} + +static bool +has_params(const char *src) +{ + bool result = FALSE; + int len = (int) strlen(src); + int n; + bool ifthen = FALSE; + bool params = FALSE; + + for (n = 0; n < len - 1; ++n) { + if (!strncmp(src + n, "%p", 2)) { + params = TRUE; + } else if (!strncmp(src + n, "%;", 2)) { + ifthen = TRUE; + result = params; + break; + } + } + if (!ifthen) { + result = ((len > 50) && params); + } + return result; +} + +static char * +fmt_complex(char *src, int level) +{ + bool percent = FALSE; + bool params = has_params(src); + + while (*src != '\0') { + switch (*src) { + case '\\': + percent = FALSE; + strncpy_DYN(&tmpbuf, src++, 1); + break; + case '%': + percent = TRUE; + break; + case '?': /* "if" */ + case 't': /* "then" */ + case 'e': /* "else" */ + if (percent) { + percent = FALSE; + tmpbuf.text[tmpbuf.used - 1] = '\n'; + /* treat a "%e" as else-if, on the same level */ + if (*src == 'e') { + indent_DYN(&tmpbuf, level); + strncpy_DYN(&tmpbuf, "%", 1); + strncpy_DYN(&tmpbuf, src, 1); + src++; + params = has_params(src); + if (!params && *src != '\0' && *src != '%') { + strncpy_DYN(&tmpbuf, "\n", 1); + indent_DYN(&tmpbuf, level + 1); + } + } else { + indent_DYN(&tmpbuf, level + 1); + strncpy_DYN(&tmpbuf, "%", 1); + strncpy_DYN(&tmpbuf, src, 1); + if (*src++ == '?') { + src = fmt_complex(src, level + 1); + if (*src != '\0' && *src != '%') { + strncpy_DYN(&tmpbuf, "\n", 1); + indent_DYN(&tmpbuf, level + 1); + } + } else if (level == 1) { + _nc_warning("%%%c without %%?", *src); + } + } + continue; + } + break; + case ';': /* "endif" */ + if (percent) { + percent = FALSE; + if (level > 1) { + tmpbuf.text[tmpbuf.used - 1] = '\n'; + indent_DYN(&tmpbuf, level); + strncpy_DYN(&tmpbuf, "%", 1); + strncpy_DYN(&tmpbuf, src++, 1); + return src; + } + _nc_warning("%%; without %%?"); + } + break; + case 'p': + if (percent && params) { + tmpbuf.text[tmpbuf.used - 1] = '\n'; + indent_DYN(&tmpbuf, level + 1); + strncpy_DYN(&tmpbuf, "%", 1); + } + params = FALSE; + percent = FALSE; + break; + case ' ': + strncpy_DYN(&tmpbuf, "\\s", 2); + ++src; + continue; + default: + percent = FALSE; + break; + } + strncpy_DYN(&tmpbuf, src++, 1); + } + return src; +} + +#define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap) +#define EXTRA_CAP 20 + +int +fmt_entry(TERMTYPE *tterm, + PredFunc pred, + bool content_only, + bool suppress_untranslatable, + bool infodump, + int numbers) +{ + PredIdx i, j; + char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP]; + char *capability; + NCURSES_CONST char *name; + int predval, len; + PredIdx num_bools = 0; + PredIdx num_values = 0; + PredIdx num_strings = 0; + bool outcount = 0; + +#define WRAP_CONCAT \ + wrap_concat(buffer); \ + outcount = TRUE + + len = 12; /* terminfo file-header */ + + if (pred == 0) { + cur_type = tterm; + pred = dump_predicate; + } + + strcpy_DYN(&outbuf, 0); + if (content_only) { + column = INDENT; /* FIXME: workaround to prevent empty lines */ + } else { + strcpy_DYN(&outbuf, tterm->term_names); + strcpy_DYN(&outbuf, separator); + column = (int) outbuf.used; + force_wrap(); + } + + for_each_boolean(j, tterm) { + i = BoolIndirect(j); + name = ExtBoolname(tterm, i, bool_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); + + if (!version_filter(BOOLEAN, i)) + continue; + else if (isObsolete(outform, name)) + continue; + + predval = pred(BOOLEAN, i); + if (predval != FAIL) { + (void) strcpy(buffer, name); + if (predval <= 0) + (void) strcat(buffer, "@"); + else if (i + 1 > num_bools) + num_bools = i + 1; + WRAP_CONCAT; + } + } + + if (column != INDENT) + force_wrap(); + + for_each_number(j, tterm) { + i = NumIndirect(j); + name = ExtNumname(tterm, i, num_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); + + if (!version_filter(NUMBER, i)) + continue; + else if (isObsolete(outform, name)) + continue; + + predval = pred(NUMBER, i); + if (predval != FAIL) { + if (tterm->Numbers[i] < 0) { + sprintf(buffer, "%s@", name); + } else { + sprintf(buffer, "%s#%d", name, tterm->Numbers[i]); + if (i + 1 > num_values) + num_values = i + 1; + } + WRAP_CONCAT; + } + } + + if (column != INDENT) + force_wrap(); + + len += (int) (num_bools + + num_values * 2 + + strlen(tterm->term_names) + 1); + if (len & 1) + len++; + +#undef CUR +#define CUR tterm-> + if (outform == F_TERMCAP) { + if (termcap_reset != ABSENT_STRING) { + if (init_3string != ABSENT_STRING + && !strcmp(init_3string, termcap_reset)) + DISCARD(init_3string); + + if (reset_2string != ABSENT_STRING + && !strcmp(reset_2string, termcap_reset)) + DISCARD(reset_2string); + } + } + + for_each_string(j, tterm) { + i = StrIndirect(j); + name = ExtStrname(tterm, i, str_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); + + capability = tterm->Strings[i]; + + if (!version_filter(STRING, i)) + continue; + else if (isObsolete(outform, name)) + continue; + +#if NCURSES_XNAMES + /* + * Extended names can be longer than 2 characters, but termcap programs + * cannot read those (filter them out). + */ + if (outform == F_TERMCAP && (strlen(name) > 2)) + continue; +#endif + + if (outform == F_TERMCAP) { + /* + * Some older versions of vi want rmir/smir to be defined + * for ich/ich1 to work. If they're not defined, force + * them to be output as defined and empty. + */ + if (PRESENT(insert_character) || PRESENT(parm_ich)) { + if (SAME_CAP(i, enter_insert_mode) + && enter_insert_mode == ABSENT_STRING) { + (void) strcpy(buffer, "im="); + WRAP_CONCAT; + continue; + } + + if (SAME_CAP(i, exit_insert_mode) + && exit_insert_mode == ABSENT_STRING) { + (void) strcpy(buffer, "ei="); + WRAP_CONCAT; + continue; + } + } + /* + * termcap applications such as screen will be confused if sgr0 + * is translated to a string containing rmacs. Filter that out. + */ + if (PRESENT(exit_attribute_mode)) { + if (SAME_CAP(i, exit_attribute_mode)) { + char *trimmed_sgr0; + char *my_sgr = set_attributes; + + set_attributes = save_sgr; + + trimmed_sgr0 = _nc_trim_sgr0(tterm); + if (strcmp(capability, trimmed_sgr0)) + capability = trimmed_sgr0; + + set_attributes = my_sgr; + } + } + } + + predval = pred(STRING, i); + buffer[0] = '\0'; + + if (predval != FAIL) { + if (capability != ABSENT_STRING + && i + 1 > num_strings) + num_strings = i + 1; + + if (!VALID_STRING(capability)) { + sprintf(buffer, "%s@", name); + WRAP_CONCAT; + } else if (outform == F_TERMCAP || outform == F_TCONVERR) { + int params = ((i < (int) SIZEOF(parametrized)) + ? parametrized[i] + : 0); + char *srccap = _nc_tic_expand(capability, TRUE, numbers); + char *cv = _nc_infotocap(name, srccap, params); + + if (cv == 0) { + if (outform == F_TCONVERR) { + sprintf(buffer, "%s=!!! %s WILL NOT CONVERT !!!", + name, srccap); + } else if (suppress_untranslatable) { + continue; + } else { + char *s = srccap, *d = buffer; + sprintf(d, "..%s=", name); + d += strlen(d); + while ((*d = *s++) != 0) { + if (*d == ':') { + *d++ = '\\'; + *d = ':'; + } else if (*d == '\\') { + *++d = *s++; + } + d++; + } + } + } else { + sprintf(buffer, "%s=%s", name, cv); + } + len += (int) strlen(capability) + 1; + WRAP_CONCAT; + } else { + char *src = _nc_tic_expand(capability, + outform == F_TERMINFO, numbers); + + strcpy_DYN(&tmpbuf, 0); + strcpy_DYN(&tmpbuf, name); + strcpy_DYN(&tmpbuf, "="); + if (pretty + && (outform == F_TERMINFO + || outform == F_VARIABLE)) { + fmt_complex(src, 1); + } else { + strcpy_DYN(&tmpbuf, src); + } + len += (int) strlen(capability) + 1; + wrap_concat(tmpbuf.text); + outcount = TRUE; + } + } + /* e.g., trimmed_sgr0 */ + if (capability != tterm->Strings[i]) + free(capability); + } + len += (int) (num_strings * 2); + + /* + * This piece of code should be an effective inverse of the functions + * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c. + * Much more work should be done on this to support dumping termcaps. + */ + if (tversion == V_HPUX) { + if (VALID_STRING(memory_lock)) { + (void) sprintf(buffer, "meml=%s", memory_lock); + WRAP_CONCAT; + } + if (VALID_STRING(memory_unlock)) { + (void) sprintf(buffer, "memu=%s", memory_unlock); + WRAP_CONCAT; + } + } else if (tversion == V_AIX) { + if (VALID_STRING(acs_chars)) { + bool box_ok = TRUE; + const char *acstrans = "lqkxjmwuvtn"; + const char *cp; + char *tp, *sp, boxchars[11]; + + tp = boxchars; + for (cp = acstrans; *cp; cp++) { + sp = strchr(acs_chars, *cp); + if (sp) + *tp++ = sp[1]; + else { + box_ok = FALSE; + break; + } + } + tp[0] = '\0'; + + if (box_ok) { + (void) strcpy(buffer, "box1="); + (void) strcat(buffer, _nc_tic_expand(boxchars, + outform == F_TERMINFO, numbers)); + WRAP_CONCAT; + } + } + } + + /* + * kludge: trim off trailer to avoid an extra blank line + * in infocmp -u output when there are no string differences + */ + if (outcount) { + bool trimmed = FALSE; + j = outbuf.used; + if (j >= 2 + && outbuf.text[j - 1] == '\t' + && outbuf.text[j - 2] == '\n') { + outbuf.used -= 2; + trimmed = TRUE; + } else if (j >= 4 + && outbuf.text[j - 1] == ':' + && outbuf.text[j - 2] == '\t' + && outbuf.text[j - 3] == '\n' + && outbuf.text[j - 4] == '\\') { + outbuf.used -= 4; + trimmed = TRUE; + } + if (trimmed) { + outbuf.text[outbuf.used] = '\0'; + column = oldcol; + strcpy_DYN(&outbuf, " "); + } + } +#if 0 + fprintf(stderr, "num_bools = %d\n", num_bools); + fprintf(stderr, "num_values = %d\n", num_values); + fprintf(stderr, "num_strings = %d\n", num_strings); + fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", + tterm->term_names, len, outbuf.used, outbuf.text); +#endif + /* + * Here's where we use infodump to trigger a more stringent length check + * for termcap-translation purposes. + * Return the length of the raw entry, without tc= expansions, + * It gives an idea of which entries are deadly to even *scan past*, + * as opposed to *use*. + */ + return (infodump ? len : (int) termcap_length(outbuf.text)); +} + +static bool +kill_string(TERMTYPE *tterm, char *cap) +{ + unsigned n; + for (n = 0; n < NUM_STRINGS(tterm); ++n) { + if (cap == tterm->Strings[n]) { + tterm->Strings[n] = ABSENT_STRING; + return TRUE; + } + } + return FALSE; +} + +static char * +find_string(TERMTYPE *tterm, char *name) +{ + PredIdx n; + for (n = 0; n < NUM_STRINGS(tterm); ++n) { + if (version_filter(STRING, n) + && !strcmp(name, strnames[n])) { + char *cap = tterm->Strings[n]; + if (VALID_STRING(cap)) { + return cap; + } + break; + } + } + return ABSENT_STRING; +} + +/* + * This is used to remove function-key labels from a termcap entry to + * make it smaller. + */ +static int +kill_labels(TERMTYPE *tterm, int target) +{ + int n; + int result = 0; + char *cap; + char name[10]; + + for (n = 0; n <= 10; ++n) { + sprintf(name, "lf%d", n); + if ((cap = find_string(tterm, name)) != ABSENT_STRING + && kill_string(tterm, cap)) { + target -= (int) (strlen(cap) + 5); + ++result; + if (target < 0) + break; + } + } + return result; +} + +/* + * This is used to remove function-key definitions from a termcap entry to + * make it smaller. + */ +static int +kill_fkeys(TERMTYPE *tterm, int target) +{ + int n; + int result = 0; + char *cap; + char name[10]; + + for (n = 60; n >= 0; --n) { + sprintf(name, "kf%d", n); + if ((cap = find_string(tterm, name)) != ABSENT_STRING + && kill_string(tterm, cap)) { + target -= (int) (strlen(cap) + 5); + ++result; + if (target < 0) + break; + } + } + return result; +} + +/* + * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100. + * Also, since this is for termcap, we only care about the line-drawing map. + */ +#define isLine(c) (strchr("lmkjtuvwqxn", c) != 0) + +static bool +one_one_mapping(const char *mapping) +{ + bool result = TRUE; + + if (mapping != ABSENT_STRING) { + int n = 0; + while (mapping[n] != '\0') { + if (isLine(mapping[n]) && + mapping[n] != mapping[n + 1]) { + result = FALSE; + break; + } + n += 2; + } + } + return result; +} + +#define FMT_ENTRY() \ + fmt_entry(tterm, pred, \ + 0, \ + suppress_untranslatable, \ + infodump, numbers) + +#define SHOW_WHY PRINTF + +static bool +purged_acs(TERMTYPE *tterm) +{ + bool result = FALSE; + + if (VALID_STRING(acs_chars)) { + if (!one_one_mapping(acs_chars)) { + enter_alt_charset_mode = ABSENT_STRING; + exit_alt_charset_mode = ABSENT_STRING; + SHOW_WHY("# (rmacs/smacs removed for consistency)\n"); + } + result = TRUE; + } + return result; +} + +/* + * Dump a single entry. + */ +void +dump_entry(TERMTYPE *tterm, + bool suppress_untranslatable, + bool limited, + int numbers, + PredFunc pred) +{ + TERMTYPE save_tterm; + int len, critlen; + const char *legend; + bool infodump; + + if (outform == F_TERMCAP || outform == F_TCONVERR) { + critlen = MAX_TERMCAP_LENGTH; + legend = "older termcap"; + infodump = FALSE; + set_obsolete_termcaps(tterm); + } else { + critlen = MAX_TERMINFO_LENGTH; + legend = "terminfo"; + infodump = TRUE; + } + + save_sgr = set_attributes; + + if ((FMT_ENTRY() > critlen) + && limited) { + + save_tterm = *tterm; + if (!suppress_untranslatable) { + SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n", + critlen); + suppress_untranslatable = TRUE; + } + if (FMT_ENTRY() > critlen) { + /* + * We pick on sgr because it's a nice long string capability that + * is really just an optimization hack. Another good candidate is + * acsc since it is both long and unused by BSD termcap. + */ + bool changed = FALSE; + +#if NCURSES_XNAMES + /* + * Extended names are most likely function-key definitions. Drop + * those first. + */ + unsigned n; + for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) { + const char *name = ExtStrname(tterm, n, strnames); + + if (VALID_STRING(tterm->Strings[n])) { + set_attributes = ABSENT_STRING; + /* we remove long names anyway - only report the short */ + if (strlen(name) <= 2) { + SHOW_WHY("# (%s removed to fit entry within %d bytes)\n", + name, + critlen); + } + changed = TRUE; + if (FMT_ENTRY() <= critlen) + break; + } + } +#endif + if (VALID_STRING(set_attributes)) { + set_attributes = ABSENT_STRING; + SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n", + critlen); + changed = TRUE; + } + if (!changed || (FMT_ENTRY() > critlen)) { + if (purged_acs(tterm)) { + acs_chars = ABSENT_STRING; + SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n", + critlen); + changed = TRUE; + } + } + if (!changed || (FMT_ENTRY() > critlen)) { + int oldversion = tversion; + + tversion = V_BSD; + SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", + critlen); + + len = FMT_ENTRY(); + if (len > critlen + && kill_labels(tterm, len - critlen)) { + SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n", + critlen); + len = FMT_ENTRY(); + } + if (len > critlen + && kill_fkeys(tterm, len - critlen)) { + SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n", + critlen); + len = FMT_ENTRY(); + } + if (len > critlen) { + (void) fprintf(stderr, + "warning: %s entry is %d bytes long\n", + _nc_first_name(tterm->term_names), + len); + SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", + len, legend); + } + tversion = oldversion; + } + set_attributes = save_sgr; + *tterm = save_tterm; + } + } else if (!version_filter(STRING, STR_IDX(acs_chars))) { + save_tterm = *tterm; + if (purged_acs(tterm)) { + (void) FMT_ENTRY(); + } + *tterm = save_tterm; + } +} + +void +dump_uses(const char *name, bool infodump) +/* dump "use=" clauses in the appropriate format */ +{ + char buffer[MAX_TERMINFO_LENGTH]; + + if (outform == F_TERMCAP || outform == F_TCONVERR) + trim_trailing(); + (void) sprintf(buffer, "%s%s", infodump ? "use=" : "tc=", name); + wrap_concat(buffer); +} + +int +show_entry(void) +{ + trim_trailing(); + (void) fputs(outbuf.text, stdout); + putchar('\n'); + return (int) outbuf.used; +} + +void +compare_entry(void (*hook) (PredType t, PredIdx i, const char *name), + TERMTYPE *tp GCC_UNUSED, + bool quiet) +/* compare two entries */ +{ + PredIdx i, j; + NCURSES_CONST char *name; + + if (!quiet) + fputs(" comparing booleans.\n", stdout); + for_each_boolean(j, tp) { + i = BoolIndirect(j); + name = ExtBoolname(tp, i, bool_names); + + if (isObsolete(outform, name)) + continue; + + (*hook) (CMP_BOOLEAN, i, name); + } + + if (!quiet) + fputs(" comparing numbers.\n", stdout); + for_each_number(j, tp) { + i = NumIndirect(j); + name = ExtNumname(tp, i, num_names); + + if (isObsolete(outform, name)) + continue; + + (*hook) (CMP_NUMBER, i, name); + } + + if (!quiet) + fputs(" comparing strings.\n", stdout); + for_each_string(j, tp) { + i = StrIndirect(j); + name = ExtStrname(tp, i, str_names); + + if (isObsolete(outform, name)) + continue; + + (*hook) (CMP_STRING, i, name); + } + + /* (void) fputs(" comparing use entries.\n", stdout); */ + (*hook) (CMP_USE, 0, "use"); + +} + +#define NOTSET(s) ((s) == 0) + +/* + * This bit of legerdemain turns all the terminfo variable names into + * references to locations in the arrays Booleans, Numbers, and Strings --- + * precisely what's needed. + */ +#undef CUR +#define CUR tp-> + +static void +set_obsolete_termcaps(TERMTYPE *tp) +{ +#include "capdefaults.c" +} + +/* + * Convert an alternate-character-set string to canonical form: sorted and + * unique. + */ +void +repair_acsc(TERMTYPE *tp) +{ + if (VALID_STRING(acs_chars)) { + size_t n, m; + char mapped[256]; + char extra = 0; + unsigned source; + unsigned target; + bool fix_needed = FALSE; + + for (n = 0, source = 0; acs_chars[n] != 0; n++) { + target = UChar(acs_chars[n]); + if (source >= target) { + fix_needed = TRUE; + break; + } + source = target; + if (acs_chars[n + 1]) + n++; + } + if (fix_needed) { + memset(mapped, 0, sizeof(mapped)); + for (n = 0; acs_chars[n] != 0; n++) { + source = UChar(acs_chars[n]); + if ((target = (unsigned char) acs_chars[n + 1]) != 0) { + mapped[source] = (char) target; + n++; + } else { + extra = (char) source; + } + } + for (n = m = 0; n < sizeof(mapped); n++) { + if (mapped[n]) { + acs_chars[m++] = (char) n; + acs_chars[m++] = mapped[n]; + } + } + if (extra) + acs_chars[m++] = extra; /* garbage in, garbage out */ + acs_chars[m] = 0; + } + } +} diff --git a/progs/dump_entry.h b/progs/dump_entry.h new file mode 100644 index 0000000..b99a37a --- /dev/null +++ b/progs/dump_entry.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996-on * + ****************************************************************************/ + + +/* + * $Id: dump_entry.h,v 1.30 2008/07/12 20:23:03 tom Exp $ + * + * Dump control definitions and variables + */ + +#ifndef DUMP_ENTRY_H +#define DUMP_ENTRY_H 1 + +/* capability output formats */ +#define F_TERMINFO 0 /* use terminfo names */ +#define F_VARIABLE 1 /* use C variable names */ +#define F_TERMCAP 2 /* termcap names with capability conversion */ +#define F_TCONVERR 3 /* as T_TERMCAP, no skip of untranslatables */ +#define F_LITERAL 4 /* like F_TERMINFO, but no smart defaults */ + +/* capability sort modes */ +#define S_DEFAULT 0 /* sort by terminfo name (implicit) */ +#define S_NOSORT 1 /* don't sort */ +#define S_TERMINFO 2 /* sort by terminfo names (explicit) */ +#define S_VARIABLE 3 /* sort by C variable names */ +#define S_TERMCAP 4 /* sort by termcap names */ + +/* capability types for the comparison hook */ +#define CMP_BOOLEAN 0 /* comparison on booleans */ +#define CMP_NUMBER 1 /* comparison on numerics */ +#define CMP_STRING 2 /* comparison on strings */ +#define CMP_USE 3 /* comparison on use capabilities */ + +typedef unsigned PredType; +typedef unsigned PredIdx; +typedef int (*PredFunc)(PredType, PredIdx); + +extern NCURSES_CONST char *nametrans(const char *); +extern int fmt_entry(TERMTYPE *, PredFunc, bool, bool, bool, int); +extern int show_entry(void); +extern void compare_entry(void (*)(PredType, PredIdx, const char *), TERMTYPE *, bool); +extern void dump_entry(TERMTYPE *, bool, bool, int, PredFunc); +extern void dump_init(const char *, int, int, int, int, bool); +extern void dump_uses(const char *, bool); +extern void repair_acsc(TERMTYPE * tp); + +#define FAIL -1 + +#endif /* DUMP_ENTRY_H */ diff --git a/progs/infocmp.c b/progs/infocmp.c new file mode 100644 index 0000000..2af9cb5 --- /dev/null +++ b/progs/infocmp.c @@ -0,0 +1,1657 @@ +/**************************************************************************** + * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996-on * + ****************************************************************************/ + +/* + * infocmp.c -- decompile an entry, or compare two entries + * written by Eric S. Raymond + * and Thomas E Dickey + */ + +#include <progs.priv.h> + +#include <dump_entry.h> + +MODULE_ID("$Id: infocmp.c,v 1.105 2010/05/01 22:04:08 tom Exp $") + +#define L_CURL "{" +#define R_CURL "}" + +#define MAX_STRING 1024 /* maximum formatted string */ + +const char *_nc_progname = "infocmp"; + +typedef char path[PATH_MAX]; + +/*************************************************************************** + * + * The following control variables, together with the contents of the + * terminfo entries, completely determine the actions of the program. + * + ***************************************************************************/ + +static ENTRY *entries; /* terminfo entries */ +static int termcount; /* count of terminal entries */ + +static bool limited = TRUE; /* "-r" option is not set */ +static bool quiet = FALSE; +static bool literal = FALSE; +static const char *bool_sep = ":"; +static const char *s_absent = "NULL"; +static const char *s_cancel = "NULL"; +static const char *tversion; /* terminfo version selected */ +static int itrace; /* trace flag for debugging */ +static int mwidth = 60; +static int numbers = 0; /* format "%'char'" to/from "%{number}" */ +static int outform = F_TERMINFO; /* output format */ +static int sortmode; /* sort_mode */ + +/* main comparison mode */ +static int compare; +#define C_DEFAULT 0 /* don't force comparison mode */ +#define C_DIFFERENCE 1 /* list differences between two terminals */ +#define C_COMMON 2 /* list common capabilities */ +#define C_NAND 3 /* list capabilities in neither terminal */ +#define C_USEALL 4 /* generate relative use-form entry */ +static bool ignorepads; /* ignore pad prefixes when diffing */ + +#if NO_LEAKS +#undef ExitProgram +static void ExitProgram(int code) GCC_NORETURN; +/* prototype is to get gcc to accept the noreturn attribute */ +static void +ExitProgram(int code) +{ + while (termcount-- > 0) + _nc_free_termtype(&entries[termcount].tterm); + _nc_leaks_dump_entry(); + free(entries); + _nc_free_tic(code); +} +#endif + +static char * +canonical_name(char *ptr, char *buf) +/* extract the terminal type's primary name */ +{ + char *bp; + + (void) strcpy(buf, ptr); + if ((bp = strchr(buf, '|')) != 0) + *bp = '\0'; + + return (buf); +} + +/*************************************************************************** + * + * Predicates for dump function + * + ***************************************************************************/ + +static int +capcmp(PredIdx idx, const char *s, const char *t) +/* capability comparison function */ +{ + if (!VALID_STRING(s) && !VALID_STRING(t)) + return (s != t); + else if (!VALID_STRING(s) || !VALID_STRING(t)) + return (1); + + if ((idx == acs_chars_index) || !ignorepads) + return (strcmp(s, t)); + else + return (_nc_capcmp(s, t)); +} + +static int +use_predicate(unsigned type, PredIdx idx) +/* predicate function to use for use decompilation */ +{ + ENTRY *ep; + + switch (type) { + case BOOLEAN: + { + int is_set = FALSE; + + /* + * This assumes that multiple use entries are supposed + * to contribute the logical or of their boolean capabilities. + * This is true if we take the semantics of multiple uses to + * be 'each capability gets the first non-default value found + * in the sequence of use entries'. + * + * Note that cancelled or absent booleans are stored as FALSE, + * unlike numbers and strings, whose cancelled/absent state is + * recorded in the terminfo database. + */ + for (ep = &entries[1]; ep < entries + termcount; ep++) + if (ep->tterm.Booleans[idx] == TRUE) { + is_set = entries[0].tterm.Booleans[idx]; + break; + } + if (is_set != entries[0].tterm.Booleans[idx]) + return (!is_set); + else + return (FAIL); + } + + case NUMBER: + { + int value = ABSENT_NUMERIC; + + /* + * We take the semantics of multiple uses to be 'each + * capability gets the first non-default value found + * in the sequence of use entries'. + */ + for (ep = &entries[1]; ep < entries + termcount; ep++) + if (VALID_NUMERIC(ep->tterm.Numbers[idx])) { + value = ep->tterm.Numbers[idx]; + break; + } + + if (value != entries[0].tterm.Numbers[idx]) + return (value != ABSENT_NUMERIC); + else + return (FAIL); + } + + case STRING: + { + char *termstr, *usestr = ABSENT_STRING; + + termstr = entries[0].tterm.Strings[idx]; + + /* + * We take the semantics of multiple uses to be 'each + * capability gets the first non-default value found + * in the sequence of use entries'. + */ + for (ep = &entries[1]; ep < entries + termcount; ep++) + if (ep->tterm.Strings[idx]) { + usestr = ep->tterm.Strings[idx]; + break; + } + + if (usestr == ABSENT_STRING && termstr == ABSENT_STRING) + return (FAIL); + else if (!usestr || !termstr || capcmp(idx, usestr, termstr)) + return (TRUE); + else + return (FAIL); + } + } + + return (FALSE); /* pacify compiler */ +} + +static bool +useeq(ENTRY * e1, ENTRY * e2) +/* are the use references in two entries equivalent? */ +{ + unsigned i, j; + + if (e1->nuses != e2->nuses) + return (FALSE); + + /* Ugh...this is quadratic again */ + for (i = 0; i < e1->nuses; i++) { + bool foundmatch = FALSE; + + /* search second entry for given use reference */ + for (j = 0; j < e2->nuses; j++) + if (!strcmp(e1->uses[i].name, e2->uses[j].name)) { + foundmatch = TRUE; + break; + } + + if (!foundmatch) + return (FALSE); + } + + return (TRUE); +} + +static bool +entryeq(TERMTYPE *t1, TERMTYPE *t2) +/* are two entries equivalent? */ +{ + unsigned i; + + for (i = 0; i < NUM_BOOLEANS(t1); i++) + if (t1->Booleans[i] != t2->Booleans[i]) + return (FALSE); + + for (i = 0; i < NUM_NUMBERS(t1); i++) + if (t1->Numbers[i] != t2->Numbers[i]) + return (FALSE); + + for (i = 0; i < NUM_STRINGS(t1); i++) + if (capcmp((PredIdx) i, t1->Strings[i], t2->Strings[i])) + return (FALSE); + + return (TRUE); +} + +#define TIC_EXPAND(result) _nc_tic_expand(result, outform==F_TERMINFO, numbers) + +static void +print_uses(ENTRY * ep, FILE *fp) +/* print an entry's use references */ +{ + unsigned i; + + if (!ep->nuses) + fputs("NULL", fp); + else + for (i = 0; i < ep->nuses; i++) { + fputs(ep->uses[i].name, fp); + if (i < ep->nuses - 1) + fputs(" ", fp); + } +} + +static const char * +dump_boolean(int val) +/* display the value of a boolean capability */ +{ + switch (val) { + case ABSENT_BOOLEAN: + return (s_absent); + case CANCELLED_BOOLEAN: + return (s_cancel); + case FALSE: + return ("F"); + case TRUE: + return ("T"); + default: + return ("?"); + } +} + +static void +dump_numeric(int val, char *buf) +/* display the value of a boolean capability */ +{ + switch (val) { + case ABSENT_NUMERIC: + strcpy(buf, s_absent); + break; + case CANCELLED_NUMERIC: + strcpy(buf, s_cancel); + break; + default: + sprintf(buf, "%d", val); + break; + } +} + +static void +dump_string(char *val, char *buf) +/* display the value of a string capability */ +{ + if (val == ABSENT_STRING) + strcpy(buf, s_absent); + else if (val == CANCELLED_STRING) + strcpy(buf, s_cancel); + else { + sprintf(buf, "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val)); + } +} + +static void +compare_predicate(PredType type, PredIdx idx, const char *name) +/* predicate function to use for entry difference reports */ +{ + register ENTRY *e1 = &entries[0]; + register ENTRY *e2 = &entries[1]; + char buf1[MAX_STRING], buf2[MAX_STRING]; + int b1, b2; + int n1, n2; + char *s1, *s2; + + switch (type) { + case CMP_BOOLEAN: + b1 = e1->tterm.Booleans[idx]; + b2 = e2->tterm.Booleans[idx]; + switch (compare) { + case C_DIFFERENCE: + if (!(b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN) && b1 != b2) + (void) printf("\t%s: %s%s%s.\n", + name, + dump_boolean(b1), + bool_sep, + dump_boolean(b2)); + break; + + case C_COMMON: + if (b1 == b2 && b1 != ABSENT_BOOLEAN) + (void) printf("\t%s= %s.\n", name, dump_boolean(b1)); + break; + + case C_NAND: + if (b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN) + (void) printf("\t!%s.\n", name); + break; + } + break; + + case CMP_NUMBER: + n1 = e1->tterm.Numbers[idx]; + n2 = e2->tterm.Numbers[idx]; + dump_numeric(n1, buf1); + dump_numeric(n2, buf2); + switch (compare) { + case C_DIFFERENCE: + if (!((n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)) && n1 != n2) + (void) printf("\t%s: %s, %s.\n", name, buf1, buf2); + break; + + case C_COMMON: + if (n1 != ABSENT_NUMERIC && n2 != ABSENT_NUMERIC && n1 == n2) + (void) printf("\t%s= %s.\n", name, buf1); + break; + + case C_NAND: + if (n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC) + (void) printf("\t!%s.\n", name); + break; + } + break; + + case CMP_STRING: + s1 = e1->tterm.Strings[idx]; + s2 = e2->tterm.Strings[idx]; + switch (compare) { + case C_DIFFERENCE: + if (capcmp(idx, s1, s2)) { + dump_string(s1, buf1); + dump_string(s2, buf2); + if (strcmp(buf1, buf2)) + (void) printf("\t%s: %s, %s.\n", name, buf1, buf2); + } + break; + + case C_COMMON: + if (s1 && s2 && !capcmp(idx, s1, s2)) + (void) printf("\t%s= '%s'.\n", name, TIC_EXPAND(s1)); + break; + + case C_NAND: + if (!s1 && !s2) + (void) printf("\t!%s.\n", name); + break; + } + break; + + case CMP_USE: + /* unlike the other modes, this compares *all* use entries */ + switch (compare) { + case C_DIFFERENCE: + if (!useeq(e1, e2)) { + (void) fputs("\tuse: ", stdout); + print_uses(e1, stdout); + fputs(", ", stdout); + print_uses(e2, stdout); + fputs(".\n", stdout); + } + break; + + case C_COMMON: + if (e1->nuses && e2->nuses && useeq(e1, e2)) { + (void) fputs("\tuse: ", stdout); + print_uses(e1, stdout); + fputs(".\n", stdout); + } + break; + + case C_NAND: + if (!e1->nuses && !e2->nuses) + (void) printf("\t!use.\n"); + break; + } + } +} + +/*************************************************************************** + * + * Init string analysis + * + ***************************************************************************/ + +typedef struct { + const char *from; + const char *to; +} assoc; + +static const assoc std_caps[] = +{ + /* these are specified by X.364 and iBCS2 */ + {"\033c", "RIS"}, /* full reset */ + {"\0337", "SC"}, /* save cursor */ + {"\0338", "RC"}, /* restore cursor */ + {"\033[r", "RSR"}, /* not an X.364 mnemonic */ + {"\033[m", "SGR0"}, /* not an X.364 mnemonic */ + {"\033[2J", "ED2"}, /* clear page */ + + /* this group is specified by ISO 2022 */ + {"\033(0", "ISO DEC G0"}, /* enable DEC graphics for G0 */ + {"\033(A", "ISO UK G0"}, /* enable UK chars for G0 */ + {"\033(B", "ISO US G0"}, /* enable US chars for G0 */ + {"\033)0", "ISO DEC G1"}, /* enable DEC graphics for G1 */ + {"\033)A", "ISO UK G1"}, /* enable UK chars for G1 */ + {"\033)B", "ISO US G1"}, /* enable US chars for G1 */ + + /* these are DEC private controls widely supported by emulators */ + {"\033=", "DECPAM"}, /* application keypad mode */ + {"\033>", "DECPNM"}, /* normal keypad mode */ + {"\033<", "DECANSI"}, /* enter ANSI mode */ + {"\033[!p", "DECSTR"}, /* soft reset */ + {"\033 F", "S7C1T"}, /* 7-bit controls */ + + {(char *) 0, (char *) 0} +}; + +static const assoc std_modes[] = +/* ECMA \E[ ... [hl] modes recognized by many emulators */ +{ + {"2", "AM"}, /* keyboard action mode */ + {"4", "IRM"}, /* insert/replace mode */ + {"12", "SRM"}, /* send/receive mode */ + {"20", "LNM"}, /* linefeed mode */ + {(char *) 0, (char *) 0} +}; + +static const assoc private_modes[] = +/* DEC \E[ ... [hl] modes recognized by many emulators */ +{ + {"1", "CKM"}, /* application cursor keys */ + {"2", "ANM"}, /* set VT52 mode */ + {"3", "COLM"}, /* 132-column mode */ + {"4", "SCLM"}, /* smooth scroll */ + {"5", "SCNM"}, /* reverse video mode */ + {"6", "OM"}, /* origin mode */ + {"7", "AWM"}, /* wraparound mode */ + {"8", "ARM"}, /* auto-repeat mode */ + {(char *) 0, (char *) 0} +}; + +static const assoc ecma_highlights[] = +/* recognize ECMA attribute sequences */ +{ + {"0", "NORMAL"}, /* normal */ + {"1", "+BOLD"}, /* bold on */ + {"2", "+DIM"}, /* dim on */ + {"3", "+ITALIC"}, /* italic on */ + {"4", "+UNDERLINE"}, /* underline on */ + {"5", "+BLINK"}, /* blink on */ + {"6", "+FASTBLINK"}, /* fastblink on */ + {"7", "+REVERSE"}, /* reverse on */ + {"8", "+INVISIBLE"}, /* invisible on */ + {"9", "+DELETED"}, /* deleted on */ + {"10", "MAIN-FONT"}, /* select primary font */ + {"11", "ALT-FONT-1"}, /* select alternate font 1 */ + {"12", "ALT-FONT-2"}, /* select alternate font 2 */ + {"13", "ALT-FONT-3"}, /* select alternate font 3 */ + {"14", "ALT-FONT-4"}, /* select alternate font 4 */ + {"15", "ALT-FONT-5"}, /* select alternate font 5 */ + {"16", "ALT-FONT-6"}, /* select alternate font 6 */ + {"17", "ALT-FONT-7"}, /* select alternate font 7 */ + {"18", "ALT-FONT-1"}, /* select alternate font 1 */ + {"19", "ALT-FONT-1"}, /* select alternate font 1 */ + {"20", "FRAKTUR"}, /* Fraktur font */ + {"21", "DOUBLEUNDER"}, /* double underline */ + {"22", "-DIM"}, /* dim off */ + {"23", "-ITALIC"}, /* italic off */ + {"24", "-UNDERLINE"}, /* underline off */ + {"25", "-BLINK"}, /* blink off */ + {"26", "-FASTBLINK"}, /* fastblink off */ + {"27", "-REVERSE"}, /* reverse off */ + {"28", "-INVISIBLE"}, /* invisible off */ + {"29", "-DELETED"}, /* deleted off */ + {(char *) 0, (char *) 0} +}; + +static int +skip_csi(const char *cap) +{ + int result = 0; + if (cap[0] == '\033' && cap[1] == '[') + result = 2; + else if (UChar(cap[0]) == 0233) + result = 1; + return result; +} + +static bool +same_param(const char *table, const char *param, unsigned length) +{ + bool result = FALSE; + if (strncmp(table, param, length) == 0) { + result = !isdigit(UChar(param[length])); + } + return result; +} + +static char * +lookup_params(const assoc * table, char *dst, char *src) +{ + char *result = 0; + const char *ep = strtok(src, ";"); + + if (ep != 0) { + const assoc *ap; + + do { + bool found = FALSE; + + for (ap = table; ap->from; ap++) { + size_t tlen = strlen(ap->from); + + if (same_param(ap->from, ep, tlen)) { + (void) strcat(dst, ap->to); + found = TRUE; + break; + } + } + + if (!found) + (void) strcat(dst, ep); + (void) strcat(dst, ";"); + } while + ((ep = strtok((char *) 0, ";"))); + + dst[strlen(dst) - 1] = '\0'; + + result = dst; + } + return result; +} + +static void +analyze_string(const char *name, const char *cap, TERMTYPE *tp) +{ + char buf2[MAX_TERMINFO_LENGTH]; + const char *sp; + const assoc *ap; + int tp_lines = tp->Numbers[2]; + + if (cap == ABSENT_STRING || cap == CANCELLED_STRING) + return; + (void) printf("%s: ", name); + + for (sp = cap; *sp; sp++) { + int i; + int csi; + size_t len = 0; + size_t next; + const char *expansion = 0; + char buf3[MAX_TERMINFO_LENGTH]; + + /* first, check other capabilities in this entry */ + for (i = 0; i < STRCOUNT; i++) { + char *cp = tp->Strings[i]; + + /* don't use soft-key capabilities */ + if (strnames[i][0] == 'k' && strnames[i][0] == 'f') + continue; + + if (cp != ABSENT_STRING && cp != CANCELLED_STRING && cp[0] && cp + != cap) { + len = strlen(cp); + (void) strncpy(buf2, sp, len); + buf2[len] = '\0'; + + if (_nc_capcmp(cp, buf2)) + continue; + +#define ISRS(s) (!strncmp((s), "is", 2) || !strncmp((s), "rs", 2)) + /* + * Theoretically we just passed the test for translation + * (equality once the padding is stripped). However, there + * are a few more hoops that need to be jumped so that + * identical pairs of initialization and reset strings + * don't just refer to each other. + */ + if (ISRS(name) || ISRS(strnames[i])) + if (cap < cp) + continue; +#undef ISRS + + expansion = strnames[i]; + break; + } + } + + /* now check the standard capabilities */ + if (!expansion) { + csi = skip_csi(sp); + for (ap = std_caps; ap->from; ap++) { + size_t adj = (size_t) (csi ? 2 : 0); + + len = strlen(ap->from); + if (csi && skip_csi(ap->from) != csi) + continue; + if (len > adj + && strncmp(ap->from + adj, sp + csi, len - adj) == 0) { + expansion = ap->to; + len -= adj; + len += (size_t) csi; + break; + } + } + } + + /* now check for standard-mode sequences */ + if (!expansion + && (csi = skip_csi(sp)) != 0 + && (len = strspn(sp + csi, "0123456789;")) + && (len < sizeof(buf3)) + && (next = (size_t) csi + len) + && ((sp[next] == 'h') || (sp[next] == 'l'))) { + + (void) strcpy(buf2, (sp[next] == 'h') ? "ECMA+" : "ECMA-"); + (void) strncpy(buf3, sp + csi, len); + buf3[len] = '\0'; + len += (size_t) csi + 1; + + expansion = lookup_params(std_modes, buf2, buf3); + } + + /* now check for private-mode sequences */ + if (!expansion + && (csi = skip_csi(sp)) != 0 + && sp[csi] == '?' + && (len = strspn(sp + csi + 1, "0123456789;")) + && (len < sizeof(buf3)) + && (next = (size_t) csi + 1 + len) + && ((sp[next] == 'h') || (sp[next] == 'l'))) { + + (void) strcpy(buf2, (sp[next] == 'h') ? "DEC+" : "DEC-"); + (void) strncpy(buf3, sp + csi + 1, len); + buf3[len] = '\0'; + len += (size_t) csi + 2; + + expansion = lookup_params(private_modes, buf2, buf3); + } + + /* now check for ECMA highlight sequences */ + if (!expansion + && (csi = skip_csi(sp)) != 0 + && (len = strspn(sp + csi, "0123456789;")) != 0 + && (len < sizeof(buf3)) + && (next = (size_t) csi + len) + && sp[next] == 'm') { + + (void) strcpy(buf2, "SGR:"); + (void) strncpy(buf3, sp + csi, len); + buf3[len] = '\0'; + len += (size_t) csi + 1; + + expansion = lookup_params(ecma_highlights, buf2, buf3); + } + + if (!expansion + && (csi = skip_csi(sp)) != 0 + && sp[csi] == 'm') { + len = (size_t) csi + 1; + (void) strcpy(buf2, "SGR:"); + strcat(buf2, ecma_highlights[0].to); + expansion = buf2; + } + + /* now check for scroll region reset */ + if (!expansion + && (csi = skip_csi(sp)) != 0) { + if (sp[csi] == 'r') { + expansion = "RSR"; + len = 1; + } else { + (void) sprintf(buf2, "1;%dr", tp_lines); + len = strlen(buf2); + if (strncmp(buf2, sp + csi, len) == 0) + expansion = "RSR"; + } + len += (size_t) csi; + } + + /* now check for home-down */ + if (!expansion + && (csi = skip_csi(sp)) != 0) { + (void) sprintf(buf2, "%d;1H", tp_lines); + len = strlen(buf2); + if (strncmp(buf2, sp + csi, len) == 0) { + expansion = "LL"; + } else { + (void) sprintf(buf2, "%dH", tp_lines); + len = strlen(buf2); + if (strncmp(buf2, sp + csi, len) == 0) { + expansion = "LL"; + } + } + len += (size_t) csi; + } + + /* now look at the expansion we got, if any */ + if (expansion) { + printf("{%s}", expansion); + sp += len - 1; + } else { + /* couldn't match anything */ + buf2[0] = *sp; + buf2[1] = '\0'; + fputs(TIC_EXPAND(buf2), stdout); + } + } + putchar('\n'); +} + +/*************************************************************************** + * + * File comparison + * + ***************************************************************************/ + +static void +file_comparison(int argc, char *argv[]) +{ +#define MAXCOMPARE 2 + /* someday we may allow comparisons on more files */ + int filecount = 0; + ENTRY *heads[MAXCOMPARE]; + ENTRY *qp, *rp; + int i, n; + + memset(heads, 0, sizeof(heads)); + dump_init((char *) 0, F_LITERAL, S_TERMINFO, 0, itrace, FALSE); + + for (n = 0; n < argc && n < MAXCOMPARE; n++) { + if (freopen(argv[n], "r", stdin) == 0) + _nc_err_abort("Can't open %s", argv[n]); + + _nc_head = _nc_tail = 0; + + /* parse entries out of the source file */ + _nc_set_source(argv[n]); + _nc_read_entry_source(stdin, NULL, TRUE, literal, NULLHOOK); + + if (itrace) + (void) fprintf(stderr, "Resolving file %d...\n", n - 0); + + /* maybe do use resolution */ + if (!_nc_resolve_uses2(!limited, literal)) { + (void) fprintf(stderr, + "There are unresolved use entries in %s:\n", + argv[n]); + for_entry_list(qp) { + if (qp->nuses) { + (void) fputs(qp->tterm.term_names, stderr); + (void) fputc('\n', stderr); + } + } + ExitProgram(EXIT_FAILURE); + } + + heads[filecount] = _nc_head; + filecount++; + } + + /* OK, all entries are in core. Ready to do the comparison */ + if (itrace) + (void) fprintf(stderr, "Entries are now in core...\n"); + + /* The entry-matching loop. Sigh, this is intrinsically quadratic. */ + for (qp = heads[0]; qp; qp = qp->next) { + for (rp = heads[1]; rp; rp = rp->next) + if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { + if (qp->ncrosslinks < MAX_CROSSLINKS) + qp->crosslinks[qp->ncrosslinks] = rp; + qp->ncrosslinks++; + + if (rp->ncrosslinks < MAX_CROSSLINKS) + rp->crosslinks[rp->ncrosslinks] = qp; + rp->ncrosslinks++; + } + } + + /* now we have two circular lists with crosslinks */ + if (itrace) + (void) fprintf(stderr, "Name matches are done...\n"); + + for (qp = heads[0]; qp; qp = qp->next) { + if (qp->ncrosslinks > 1) { + (void) fprintf(stderr, + "%s in file 1 (%s) has %d matches in file 2 (%s):\n", + _nc_first_name(qp->tterm.term_names), + argv[0], + qp->ncrosslinks, + argv[1]); + for (i = 0; i < qp->ncrosslinks; i++) + (void) fprintf(stderr, + "\t%s\n", + _nc_first_name((qp->crosslinks[i])->tterm.term_names)); + } + } + + for (rp = heads[1]; rp; rp = rp->next) { + if (rp->ncrosslinks > 1) { + (void) fprintf(stderr, + "%s in file 2 (%s) has %d matches in file 1 (%s):\n", + _nc_first_name(rp->tterm.term_names), + argv[1], + rp->ncrosslinks, + argv[0]); + for (i = 0; i < rp->ncrosslinks; i++) + (void) fprintf(stderr, + "\t%s\n", + _nc_first_name((rp->crosslinks[i])->tterm.term_names)); + } + } + + (void) printf("In file 1 (%s) only:\n", argv[0]); + for (qp = heads[0]; qp; qp = qp->next) + if (qp->ncrosslinks == 0) + (void) printf("\t%s\n", + _nc_first_name(qp->tterm.term_names)); + + (void) printf("In file 2 (%s) only:\n", argv[1]); + for (rp = heads[1]; rp; rp = rp->next) + if (rp->ncrosslinks == 0) + (void) printf("\t%s\n", + _nc_first_name(rp->tterm.term_names)); + + (void) printf("The following entries are equivalent:\n"); + for (qp = heads[0]; qp; qp = qp->next) { + if (qp->ncrosslinks == 1) { + rp = qp->crosslinks[0]; + + repair_acsc(&qp->tterm); + repair_acsc(&rp->tterm); +#if NCURSES_XNAMES + _nc_align_termtype(&qp->tterm, &rp->tterm); +#endif + if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) { + char name1[NAMESIZE], name2[NAMESIZE]; + + (void) canonical_name(qp->tterm.term_names, name1); + (void) canonical_name(rp->tterm.term_names, name2); + + (void) printf("%s = %s\n", name1, name2); + } + } + } + + (void) printf("Differing entries:\n"); + termcount = 2; + for (qp = heads[0]; qp; qp = qp->next) { + + if (qp->ncrosslinks == 1) { + rp = qp->crosslinks[0]; +#if NCURSES_XNAMES + /* sorry - we have to do this on each pass */ + _nc_align_termtype(&qp->tterm, &rp->tterm); +#endif + if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) { + char name1[NAMESIZE], name2[NAMESIZE]; + + entries[0] = *qp; + entries[1] = *rp; + + (void) canonical_name(qp->tterm.term_names, name1); + (void) canonical_name(rp->tterm.term_names, name2); + + switch (compare) { + case C_DIFFERENCE: + if (itrace) + (void) fprintf(stderr, + "%s: dumping differences\n", + _nc_progname); + (void) printf("comparing %s to %s.\n", name1, name2); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + case C_COMMON: + if (itrace) + (void) fprintf(stderr, + "%s: dumping common capabilities\n", + _nc_progname); + (void) printf("comparing %s to %s.\n", name1, name2); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + case C_NAND: + if (itrace) + (void) fprintf(stderr, + "%s: dumping differences\n", + _nc_progname); + (void) printf("comparing %s to %s.\n", name1, name2); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + } + } + } + } +} + +static void +usage(void) +{ + static const char *tbl[] = + { + "Usage: infocmp [options] [-A directory] [-B directory] [termname...]" + ,"" + ,"Options:" + ," -1 print single-column" + ," -C use termcap-names" + ," -F compare terminfo-files" + ," -I use terminfo-names" + ," -L use long names" + ," -R subset (see manpage)" + ," -T eliminate size limits (test)" + ," -U eliminate post-processing of entries" + ," -V print version" +#if NCURSES_XNAMES + ," -a with -F, list commented-out caps" +#endif + ," -c list common capabilities" + ," -d list different capabilities" + ," -e format output for C initializer" + ," -E format output as C tables" + ," -f with -1, format complex strings" + ," -G format %{number} to %'char'" + ," -g format %'char' to %{number}" + ," -i analyze initialization/reset" + ," -l output terminfo names" + ," -n list capabilities in neither" + ," -p ignore padding specifiers" + ," -q brief listing, removes headers" + ," -r with -C, output in termcap form" + ," -r with -F, resolve use-references" + ," -s [d|i|l|c] sort fields" +#if NCURSES_XNAMES + ," -t suppress commented-out capabilities" +#endif + ," -u produce source with 'use='" + ," -v number (verbose)" + ," -w number (width)" +#if NCURSES_XNAMES + ," -x treat unknown capabilities as user-defined" +#endif + }; + const size_t first = 3; + const size_t last = SIZEOF(tbl); + const size_t left = (last - first + 1) / 2 + first; + size_t n; + + for (n = 0; n < left; n++) { + size_t m = (n < first) ? last : n + left - first; + if (m < last) + fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]); + else + fprintf(stderr, "%s\n", tbl[n]); + } + ExitProgram(EXIT_FAILURE); +} + +static char * +any_initializer(const char *fmt, const char *type) +{ + static char *initializer; + char *s; + + if (initializer == 0) + initializer = (char *) malloc(strlen(entries->tterm.term_names) + + strlen(type) + strlen(fmt)); + + (void) strcpy(initializer, entries->tterm.term_names); + for (s = initializer; *s != 0 && *s != '|'; s++) { + if (!isalnum(UChar(*s))) + *s = '_'; + } + *s = 0; + (void) sprintf(s, fmt, type); + return initializer; +} + +static char * +name_initializer(const char *type) +{ + return any_initializer("_%s_data", type); +} + +static char * +string_variable(const char *type) +{ + return any_initializer("_s_%s", type); +} + +/* dump C initializers for the terminal type */ +static void +dump_initializers(TERMTYPE *term) +{ + unsigned n; + const char *str = 0; + + printf("\nstatic char %s[] = \"%s\";\n\n", + name_initializer("alias"), entries->tterm.term_names); + + for_each_string(n, term) { + char buf[MAX_STRING], *sp, *tp; + + if (VALID_STRING(term->Strings[n])) { + tp = buf; + *tp++ = '"'; + for (sp = term->Strings[n]; + *sp != 0 && (tp - buf) < MAX_STRING - 6; + sp++) { + if (isascii(UChar(*sp)) + && isprint(UChar(*sp)) + && *sp != '\\' + && *sp != '"') + *tp++ = *sp; + else { + (void) sprintf(tp, "\\%03o", UChar(*sp)); + tp += 4; + } + } + *tp++ = '"'; + *tp = '\0'; + (void) printf("static char %-20s[] = %s;\n", + string_variable(ExtStrname(term, n, strnames)), buf); + } + } + printf("\n"); + + (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL); + + for_each_boolean(n, term) { + switch ((int) (term->Booleans[n])) { + case TRUE: + str = "TRUE"; + break; + + case FALSE: + str = "FALSE"; + break; + + case ABSENT_BOOLEAN: + str = "ABSENT_BOOLEAN"; + break; + + case CANCELLED_BOOLEAN: + str = "CANCELLED_BOOLEAN"; + break; + } + (void) printf("\t/* %3u: %-8s */\t%s,\n", + n, ExtBoolname(term, n, boolnames), str); + } + (void) printf("%s;\n", R_CURL); + + (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL); + + for_each_number(n, term) { + char buf[BUFSIZ]; + switch (term->Numbers[n]) { + case ABSENT_NUMERIC: + str = "ABSENT_NUMERIC"; + break; + case CANCELLED_NUMERIC: + str = "CANCELLED_NUMERIC"; + break; + default: + sprintf(buf, "%d", term->Numbers[n]); + str = buf; + break; + } + (void) printf("\t/* %3u: %-8s */\t%s,\n", n, + ExtNumname(term, n, numnames), str); + } + (void) printf("%s;\n", R_CURL); + + (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL); + + for_each_string(n, term) { + + if (term->Strings[n] == ABSENT_STRING) + str = "ABSENT_STRING"; + else if (term->Strings[n] == CANCELLED_STRING) + str = "CANCELLED_STRING"; + else { + str = string_variable(ExtStrname(term, n, strnames)); + } + (void) printf("\t/* %3u: %-8s */\t%s,\n", n, + ExtStrname(term, n, strnames), str); + } + (void) printf("%s;\n", R_CURL); + +#if NCURSES_XNAMES + if ((NUM_BOOLEANS(term) != BOOLCOUNT) + || (NUM_NUMBERS(term) != NUMCOUNT) + || (NUM_STRINGS(term) != STRCOUNT)) { + (void) printf("static char * %s[] = %s\n", + name_initializer("string_ext"), L_CURL); + for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) { + (void) printf("\t/* %3u: bool */\t\"%s\",\n", + n, ExtBoolname(term, n, boolnames)); + } + for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) { + (void) printf("\t/* %3u: num */\t\"%s\",\n", + n, ExtNumname(term, n, numnames)); + } + for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) { + (void) printf("\t/* %3u: str */\t\"%s\",\n", + n, ExtStrname(term, n, strnames)); + } + (void) printf("%s;\n", R_CURL); + } +#endif +} + +/* dump C initializers for the terminal type */ +static void +dump_termtype(TERMTYPE *term) +{ + (void) printf("\t%s\n\t\t%s,\n", L_CURL, name_initializer("alias")); + (void) printf("\t\t(char *)0,\t/* pointer to string table */\n"); + + (void) printf("\t\t%s,\n", name_initializer("bool")); + (void) printf("\t\t%s,\n", name_initializer("number")); + + (void) printf("\t\t%s,\n", name_initializer("string")); + +#if NCURSES_XNAMES + (void) printf("#if NCURSES_XNAMES\n"); + (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n"); + (void) printf("\t\t%s,\t/* ...corresponding names */\n", + ((NUM_BOOLEANS(term) != BOOLCOUNT) + || (NUM_NUMBERS(term) != NUMCOUNT) + || (NUM_STRINGS(term) != STRCOUNT)) + ? name_initializer("string_ext") + : "(char **)0"); + + (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term)); + (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term)); + (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term)); + + (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n", + NUM_BOOLEANS(term) - BOOLCOUNT); + (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n", + NUM_NUMBERS(term) - NUMCOUNT); + (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n", + NUM_STRINGS(term) - STRCOUNT); + + (void) printf("#endif /* NCURSES_XNAMES */\n"); +#else + (void) term; +#endif /* NCURSES_XNAMES */ + (void) printf("\t%s\n", R_CURL); +} + +static int +optarg_to_number(void) +{ + char *temp = 0; + long value = strtol(optarg, &temp, 0); + + if (temp == 0 || temp == optarg || *temp != 0) { + fprintf(stderr, "Expected a number, not \"%s\"\n", optarg); + ExitProgram(EXIT_FAILURE); + } + return (int) value; +} + +static char * +terminal_env(void) +{ + char *terminal; + + if ((terminal = getenv("TERM")) == 0) { + (void) fprintf(stderr, + "%s: environment variable TERM not set\n", + _nc_progname); + exit(EXIT_FAILURE); + } + return terminal; +} + +/*************************************************************************** + * + * Main sequence + * + ***************************************************************************/ + +#if NO_LEAKS +#define MAIN_LEAKS() \ + free(myargv); \ + free(tfile); \ + free(tname) +#else +#define MAIN_LEAKS() /* nothing */ +#endif + +int +main(int argc, char *argv[]) +{ + /* Avoid "local data >32k" error with mwcc */ + /* Also avoid overflowing smaller stacks on systems like AmigaOS */ + path *tfile = 0; + char **tname = 0; + int maxterms; + + char **myargv; + + char *firstdir, *restdir; + int c, i, len; + bool formatted = FALSE; + bool filecompare = FALSE; + int initdump = 0; + bool init_analyze = FALSE; + bool suppress_untranslatable = FALSE; + + /* where is the terminfo database location going to default to? */ + restdir = firstdir = 0; + +#if NCURSES_XNAMES + use_extended_names(FALSE); +#endif + + _nc_progname = _nc_rootname(argv[0]); + + /* make sure we have enough space to add two terminal entries */ + myargv = typeCalloc(char *, (size_t) (argc + 3)); + memcpy(myargv, argv, (sizeof(char *) * (size_t) argc)); + argv = myargv; + + while ((c = getopt(argc, + argv, + "1A:aB:CcdEeFfGgIiLlnpqR:rs:TtUuVv:w:x")) != -1) { + switch (c) { + case '1': + mwidth = 0; + break; + + case 'A': + firstdir = optarg; + break; + +#if NCURSES_XNAMES + case 'a': + _nc_disable_period = TRUE; + use_extended_names(TRUE); + break; +#endif + case 'B': + restdir = optarg; + break; + + case 'C': + outform = F_TERMCAP; + tversion = "BSD"; + if (sortmode == S_DEFAULT) + sortmode = S_TERMCAP; + break; + + case 'c': + compare = C_COMMON; + break; + + case 'd': + compare = C_DIFFERENCE; + break; + + case 'E': + initdump |= 2; + break; + + case 'e': + initdump |= 1; + break; + + case 'F': + filecompare = TRUE; + break; + + case 'f': + formatted = TRUE; + break; + + case 'G': + numbers = 1; + break; + + case 'g': + numbers = -1; + break; + + case 'I': + outform = F_TERMINFO; + if (sortmode == S_DEFAULT) + sortmode = S_VARIABLE; + tversion = 0; + break; + + case 'i': + init_analyze = TRUE; + break; + + case 'L': + outform = F_VARIABLE; + if (sortmode == S_DEFAULT) + sortmode = S_VARIABLE; + break; + + case 'l': + outform = F_TERMINFO; + break; + + case 'n': + compare = C_NAND; + break; + + case 'p': + ignorepads = TRUE; + break; + + case 'q': + quiet = TRUE; + s_absent = "-"; + s_cancel = "@"; + bool_sep = ", "; + break; + + case 'R': + tversion = optarg; + break; + + case 'r': + tversion = 0; + break; + + case 's': + if (*optarg == 'd') + sortmode = S_NOSORT; + else if (*optarg == 'i') + sortmode = S_TERMINFO; + else if (*optarg == 'l') + sortmode = S_VARIABLE; + else if (*optarg == 'c') + sortmode = S_TERMCAP; + else { + (void) fprintf(stderr, + "%s: unknown sort mode\n", + _nc_progname); + ExitProgram(EXIT_FAILURE); + } + break; + + case 'T': + limited = FALSE; + break; + +#if NCURSES_XNAMES + case 't': + _nc_disable_period = FALSE; + suppress_untranslatable = TRUE; + break; +#endif + + case 'U': + literal = TRUE; + break; + + case 'u': + compare = C_USEALL; + break; + + case 'V': + puts(curses_version()); + ExitProgram(EXIT_SUCCESS); + + case 'v': + itrace = optarg_to_number(); + set_trace_level(itrace); + break; + + case 'w': + mwidth = optarg_to_number(); + break; + +#if NCURSES_XNAMES + case 'x': + use_extended_names(TRUE); + break; +#endif + + default: + usage(); + } + } + + maxterms = (argc + 2 - optind); + tfile = typeMalloc(path, maxterms); + tname = typeCalloc(char *, maxterms); + entries = typeCalloc(ENTRY, maxterms); + + if (tfile == 0 + || tname == 0 + || entries == 0) { + fprintf(stderr, "%s: not enough memory\n", _nc_progname); + ExitProgram(EXIT_FAILURE); + } + + /* by default, sort by terminfo name */ + if (sortmode == S_DEFAULT) + sortmode = S_TERMINFO; + + /* set up for display */ + dump_init(tversion, outform, sortmode, mwidth, itrace, formatted); + + /* make sure we have at least one terminal name to work with */ + if (optind >= argc) + argv[argc++] = terminal_env(); + + /* if user is after a comparison, make sure we have two entries */ + if (compare != C_DEFAULT && optind >= argc - 1) + argv[argc++] = terminal_env(); + + /* exactly two terminal names with no options means do -d */ + if (argc - optind == 2 && compare == C_DEFAULT) + compare = C_DIFFERENCE; + + if (!filecompare) { + /* grab the entries */ + termcount = 0; + for (; optind < argc; optind++) { + const char *directory = termcount ? restdir : firstdir; + int status; + + tname[termcount] = argv[optind]; + + if (directory) { +#if USE_DATABASE +#if MIXEDCASE_FILENAMES +#define LEAF_FMT "%c" +#else +#define LEAF_FMT "%02x" +#endif + (void) sprintf(tfile[termcount], "%s/" LEAF_FMT "/%s", + directory, + UChar(*argv[optind]), argv[optind]); + if (itrace) + (void) fprintf(stderr, + "%s: reading entry %s from file %s\n", + _nc_progname, + argv[optind], tfile[termcount]); + + status = _nc_read_file_entry(tfile[termcount], + &entries[termcount].tterm); +#else + (void) fprintf(stderr, "%s: terminfo files not supported\n", + _nc_progname); + MAIN_LEAKS(); + ExitProgram(EXIT_FAILURE); +#endif + } else { + if (itrace) + (void) fprintf(stderr, + "%s: reading entry %s from database\n", + _nc_progname, + tname[termcount]); + + status = _nc_read_entry(tname[termcount], + tfile[termcount], + &entries[termcount].tterm); + } + + if (status <= 0) { + (void) fprintf(stderr, + "%s: couldn't open terminfo file %s.\n", + _nc_progname, + tfile[termcount]); + MAIN_LEAKS(); + ExitProgram(EXIT_FAILURE); + } + repair_acsc(&entries[termcount].tterm); + termcount++; + } + +#if NCURSES_XNAMES + if (termcount > 1) + _nc_align_termtype(&entries[0].tterm, &entries[1].tterm); +#endif + + /* dump as C initializer for the terminal type */ + if (initdump) { + if (initdump & 1) + dump_termtype(&entries[0].tterm); + if (initdump & 2) + dump_initializers(&entries[0].tterm); + } + + /* analyze the init strings */ + else if (init_analyze) { +#undef CUR +#define CUR entries[0].tterm. + analyze_string("is1", init_1string, &entries[0].tterm); + analyze_string("is2", init_2string, &entries[0].tterm); + analyze_string("is3", init_3string, &entries[0].tterm); + analyze_string("rs1", reset_1string, &entries[0].tterm); + analyze_string("rs2", reset_2string, &entries[0].tterm); + analyze_string("rs3", reset_3string, &entries[0].tterm); + analyze_string("smcup", enter_ca_mode, &entries[0].tterm); + analyze_string("rmcup", exit_ca_mode, &entries[0].tterm); +#undef CUR + } else { + + /* + * Here's where the real work gets done + */ + switch (compare) { + case C_DEFAULT: + if (itrace) + (void) fprintf(stderr, + "%s: about to dump %s\n", + _nc_progname, + tname[0]); + (void) printf("#\tReconstructed via infocmp from file: %s\n", + tfile[0]); + dump_entry(&entries[0].tterm, + suppress_untranslatable, + limited, + numbers, + NULL); + len = show_entry(); + if (itrace) + (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len); + break; + + case C_DIFFERENCE: + if (itrace) + (void) fprintf(stderr, "%s: dumping differences\n", _nc_progname); + (void) printf("comparing %s to %s.\n", tname[0], tname[1]); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + case C_COMMON: + if (itrace) + (void) fprintf(stderr, + "%s: dumping common capabilities\n", + _nc_progname); + (void) printf("comparing %s to %s.\n", tname[0], tname[1]); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + case C_NAND: + if (itrace) + (void) fprintf(stderr, + "%s: dumping differences\n", + _nc_progname); + (void) printf("comparing %s to %s.\n", tname[0], tname[1]); + compare_entry(compare_predicate, &entries->tterm, quiet); + break; + + case C_USEALL: + if (itrace) + (void) fprintf(stderr, "%s: dumping use entry\n", _nc_progname); + dump_entry(&entries[0].tterm, + suppress_untranslatable, + limited, + numbers, + use_predicate); + for (i = 1; i < termcount; i++) + dump_uses(tname[i], !(outform == F_TERMCAP + || outform == F_TCONVERR)); + len = show_entry(); + if (itrace) + (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len); + break; + } + } + } else if (compare == C_USEALL) + (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n"); + else if (compare == C_DEFAULT) + (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n"); + else if (argc - optind != 2) + (void) fprintf(stderr, + "File comparison needs exactly two file arguments.\n"); + else + file_comparison(argc - optind, argv + optind); + + MAIN_LEAKS(); + ExitProgram(EXIT_SUCCESS); +} + +/* infocmp.c ends here */ diff --git a/progs/modules b/progs/modules new file mode 100644 index 0000000..55d7a9f --- /dev/null +++ b/progs/modules @@ -0,0 +1,45 @@ +# $Id: modules,v 1.17 2010/01/23 17:47:23 tom Exp $ +# Program modules (some are in ncurses lib!) +############################################################################## +# Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, distribute # +# with modifications, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the # +# following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # +# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +# Except as contained in this notice, the name(s) of the above copyright # +# holders shall not be used in advertising or otherwise to promote the sale, # +# use or other dealings in this Software without prior written # +# authorization. # +############################################################################## +# +# Author: Thomas E. Dickey 1995-on +# + +@ base +clear progs $(srcdir) $(HEADER_DEPS) +tic progs $(srcdir) $(HEADER_DEPS) transform.h $(srcdir)/dump_entry.h +toe progs $(srcdir) $(HEADER_DEPS) $(INCDIR)/hashed_db.h +dump_entry progs $(srcdir) $(HEADER_DEPS) $(srcdir)/dump_entry.h ../include/parametrized.h $(INCDIR)/capdefaults.c termsort.c +infocmp progs $(srcdir) $(HEADER_DEPS) $(srcdir)/dump_entry.h +tabs progs $(srcdir) $(HEADER_DEPS) +tput progs $(srcdir) $(HEADER_DEPS) transform.h $(srcdir)/dump_entry.h termsort.c +tset progs $(srcdir) $(HEADER_DEPS) transform.h $(srcdir)/dump_entry.h ../include/termcap.h +transform progs $(srcdir) $(HEADER_DEPS) transform.h + +# vile:makemode diff --git a/progs/progs.priv.h b/progs/progs.priv.h new file mode 100644 index 0000000..f0ea460 --- /dev/null +++ b/progs/progs.priv.h @@ -0,0 +1,192 @@ +/**************************************************************************** + * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Thomas E. Dickey 1997-on * + ****************************************************************************/ +/* + * $Id: progs.priv.h,v 1.34 2008/08/03 17:43:05 tom Exp $ + * + * progs.priv.h + * + * Header file for curses utility programs + */ + +#include <ncurses_cfg.h> + +#if USE_RCS_IDS +#define MODULE_ID(id) static const char Ident[] = id; +#else +#define MODULE_ID(id) /*nothing*/ +#endif + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <sys/types.h> + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if HAVE_SYS_BSDTYPES_H +#include <sys/bsdtypes.h> /* needed for ISC */ +#endif + +#if HAVE_LIMITS_H +# include <limits.h> +#elif HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +# if defined(_FILE_OFFSET_BITS) && defined(HAVE_STRUCT_DIRENT64) +# if !defined(_LP64) && (_FILE_OFFSET_BITS == 64) +# define DIRENT struct dirent64 +# else +# define DIRENT struct dirent +# endif +# else +# define DIRENT struct dirent +# endif +#else +# define DIRENT struct direct +# define NAMLEN(dirent) (dirent)->d_namlen +# 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 + +#include <assert.h> +#include <errno.h> + +#if DECL_ERRNO +extern int errno; +#endif + +#if HAVE_GETOPT_H +#include <getopt.h> +#else +/* 'getopt()' may be prototyped in <stdlib.h>, but declaring its + * variables doesn't hurt. + */ +extern char *optarg; +extern int optind; +#endif /* HAVE_GETOPT_H */ + +#include <curses.h> +#include <term_entry.h> +#include <tic.h> +#include <nc_tparm.h> + +#include <nc_alloc.h> +#if HAVE_NC_FREEALL +#undef ExitProgram +#ifdef USE_LIBTINFO +#define ExitProgram(code) _nc_free_tinfo(code) +#else +#define ExitProgram(code) _nc_free_tic(code) +#endif +#endif + +/* usually in <unistd.h> */ +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#endif + +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif + +#ifndef R_OK +#define R_OK 4 /* Test for readable. */ +#endif + +#ifndef W_OK +#define W_OK 2 /* Test for writable. */ +#endif + +#ifndef X_OK +#define X_OK 1 /* Test for executable. */ +#endif + +#ifndef F_OK +#define F_OK 0 /* Test for existence. */ +#endif + +/* usually in <unistd.h> */ +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +/* may be in limits.h, included from various places */ +#ifndef PATH_MAX +# if defined(_POSIX_PATH_MAX) +# define PATH_MAX _POSIX_PATH_MAX +# elif defined(MAXPATHLEN) +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 255 /* the Posix minimum pathsize */ +# endif +#endif + +/* We use isascii only to guard against use of 7-bit ctype tables in the + * isprint test in infocmp. + */ +#if !HAVE_ISASCII +# undef isascii +# if ('z'-'a' == 25) && ('z' < 127) && ('Z'-'A' == 25) && ('Z' < 127) && ('9' < 127) +# define isascii(c) (UChar(c) <= 127) +# else +# define isascii(c) 1 /* not really ascii anyway */ +# endif +#endif + +#define UChar(c) ((unsigned char)(c)) + +#define SIZEOF(v) (sizeof(v)/sizeof(v[0])) diff --git a/progs/tabs.c b/progs/tabs.c new file mode 100644 index 0000000..b59c908 --- /dev/null +++ b/progs/tabs.c @@ -0,0 +1,510 @@ +/**************************************************************************** + * Copyright (c) 2008-2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Thomas E. Dickey 2008 * + ****************************************************************************/ + +/* + * tabs.c -- set terminal hard-tabstops + */ + +#define USE_LIBTINFO +#include <progs.priv.h> + +MODULE_ID("$Id: tabs.c,v 1.19 2010/10/23 22:26:01 tom Exp $") + +static void usage(void) GCC_NORETURN; + +static int max_cols; + +static int +putch(int c) +{ + return putchar(c); +} + +static void +do_tabs(int *tab_list) +{ + int last = 1; + int stop; + + putchar('\r'); + while ((stop = *tab_list++) > 0) { + if (last < stop) { + while (last++ < stop) { + if (last > max_cols) + break; + putchar(' '); + } + } + if (stop <= max_cols) { + tputs(tparm(set_tab, stop), 1, putch); + last = stop; + } else { + break; + } + } + putchar('\n'); +} + +static int * +decode_tabs(const char *tab_list) +{ + int *result = typeCalloc(int, strlen(tab_list) + (unsigned) max_cols); + int n = 0; + int value = 0; + int prior = 0; + int ch; + + if (result != 0) { + while ((ch = *tab_list++) != '\0') { + if (isdigit(UChar(ch))) { + value *= 10; + value += (ch - '0'); + } else if (ch == ',') { + result[n] = value + prior; + if (n > 0 && result[n] <= result[n - 1]) { + fprintf(stderr, + "tab-stops are not in increasing order: %d %d\n", + value, result[n - 1]); + free(result); + result = 0; + break; + } + ++n; + value = 0; + prior = 0; + } else if (ch == '+') { + if (n) + prior = result[n - 1]; + } + } + } + + if (result != 0) { + /* + * If there is only one value, then it is an option such as "-8". + */ + if ((n == 0) && (value > 0)) { + int step = value; + while (n < max_cols - 1) { + result[n++] = value; + value += step; + } + } + + /* + * Add the last value, if any. + */ + result[n++] = value + prior; + result[n] = 0; + } + return result; +} + +static void +print_ruler(int *tab_list) +{ + int last = 0; + int stop; + int n; + + /* first print a readable ruler */ + for (n = 0; n < max_cols; n += 10) { + int ch = 1 + (n / 10); + char buffer[20]; + sprintf(buffer, "----+----%c", + ((ch < 10) + ? (ch + '0') + : (ch + 'A' - 10))); + printf("%.*s", ((max_cols - n) > 10) ? 10 : (max_cols - n), buffer); + } + putchar('\n'); + + /* now, print '*' for each stop */ + for (n = 0, last = 0; (tab_list[n] > 0) && (last < max_cols); ++n) { + stop = tab_list[n]; + while (++last < stop) { + if (last <= max_cols) { + putchar('-'); + } else { + break; + } + } + if (last <= max_cols) { + putchar('*'); + last = stop; + } else { + break; + } + } + while (++last <= max_cols) + putchar('-'); + putchar('\n'); +} + +/* + * Write an '*' on each tabstop, to demonstrate whether it lines up with the + * ruler. + */ +static void +write_tabs(int *tab_list) +{ + int stop; + + while ((stop = *tab_list++) > 0 && stop <= max_cols) { + fputs((stop == 1) ? "*" : "\t*", stdout); + }; + /* also show a tab _past_ the stops */ + if (stop < max_cols) + fputs("\t+", stdout); + putchar('\n'); +} + +/* + * Trim leading/trailing blanks, as well as blanks after a comma. + * Convert embedded blanks to commas. + */ +static char * +trimmed_tab_list(const char *source) +{ + char *result = strdup(source); + int ch, j, k, last; + + if (result != 0) { + for (j = k = last = 0; result[j] != 0; ++j) { + ch = UChar(result[j]); + if (isspace(ch)) { + if (last == '\0') { + continue; + } else if (isdigit(last) || last == ',') { + ch = ','; + } + } else if (ch == ',') { + ; + } else { + if (last == ',') + result[k++] = (char) last; + result[k++] = (char) ch; + } + last = ch; + } + result[k] = '\0'; + } + return result; +} + +static bool +comma_is_needed(const char *source) +{ + bool result = FALSE; + + if (source != 0) { + unsigned len = strlen(source); + if (len != 0) + result = (source[len - 1] != ','); + } else { + result = FALSE; + } + return result; +} + +/* + * Add a command-line parameter to the tab-list. It can be blank- or comma- + * separated (or a mixture). For simplicity, empty tabs are ignored, e.g., + * tabs 1,,6,11 + * tabs 1,6,11 + * are treated the same. + */ +static const char * +add_to_tab_list(char **append, const char *value) +{ + char *result = *append; + char *copied = trimmed_tab_list(value); + + if (copied != 0 && *copied != '\0') { + const char *comma = ","; + unsigned need = 1 + strlen(copied); + + if (*copied == ',') + comma = ""; + else if (!comma_is_needed(*append)) + comma = ""; + + need += strlen(comma); + if (*append != 0) + need += strlen(*append); + + result = malloc(need); + if (result != 0) { + *result = '\0'; + if (*append != 0) { + strcpy(result, *append); + free(*append); + } + strcat(result, comma); + strcat(result, copied); + } + + *append = result; + } + return result; +} + +/* + * Check for illegal characters in the tab-list. + */ +static bool +legal_tab_list(const char *program, const char *tab_list) +{ + bool result = TRUE; + + if (tab_list != 0 && *tab_list != '\0') { + if (comma_is_needed(tab_list)) { + int n, ch; + for (n = 0; tab_list[n] != '\0'; ++n) { + ch = UChar(tab_list[n]); + if (!(isdigit(ch) || ch == ',' || ch == '+')) { + fprintf(stderr, + "%s: unexpected character found '%c'\n", + program, ch); + result = FALSE; + break; + } + } + } else { + fprintf(stderr, "%s: trailing comma found '%s'\n", program, tab_list); + result = FALSE; + } + } else { + fprintf(stderr, "%s: no tab-list given\n", program); + result = FALSE; + } + return result; +} + +static void +usage(void) +{ + static const char *msg[] = + { + "Usage: tabs [options] [tabstop-list]" + ,"" + ,"Options:" + ," -0 reset tabs" + ," -8 set tabs to standard interval" + ," -a Assembler, IBM S/370, first format" + ," -a2 Assembler, IBM S/370, second format" + ," -c COBOL, normal format" + ," -c2 COBOL compact format" + ," -c3 COBOL compact format extended" + ," -d debug (show ruler with expected/actual tab positions)" + ," -f FORTRAN" + ," -n no-op (do not modify terminal settings)" + ," -p PL/I" + ," -s SNOBOL" + ," -u UNIVAC 1100 Assembler" + ," -T name use terminal type 'name'" + ,"" + ,"A tabstop-list is an ordered list of column numbers, e.g., 1,11,21" + ,"or 1,+10,+10 which is the same." + }; + unsigned n; + + fflush(stdout); + for (n = 0; n < SIZEOF(msg); ++n) { + fprintf(stderr, "%s\n", msg[n]); + } + ExitProgram(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + int rc = EXIT_FAILURE; + bool debug = FALSE; + bool no_op = FALSE; + int n, ch; + NCURSES_CONST char *term_name = 0; + const char *mar_list = 0; /* ignored */ + char *append = 0; + const char *tab_list = 0; + + if ((term_name = getenv("TERM")) == 0) + term_name = "ansi+tabs"; + + /* cannot use getopt, since some options are two-character */ + for (n = 1; n < argc; ++n) { + char *option = argv[n]; + switch (option[0]) { + case '-': + while ((ch = *++option) != '\0') { + switch (ch) { + case 'a': + switch (*option) { + case '\0': + tab_list = "1,10,16,36,72"; + /* Assembler, IBM S/370, first format */ + break; + case '2': + tab_list = "1,10,16,40,72"; + /* Assembler, IBM S/370, second format */ + break; + default: + usage(); + } + break; + case 'c': + switch (*option) { + case '\0': + tab_list = "1,8,12,16,20,55"; + /* COBOL, normal format */ + break; + case '2': + tab_list = "1,6,10,14,49"; + /* COBOL compact format */ + break; + case '3': + tab_list = "1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67"; + /* COBOL compact format extended */ + break; + default: + usage(); + } + break; + case 'd': /* ncurses extension */ + debug = TRUE; + break; + case 'f': + tab_list = "1,7,11,15,19,23"; + /* FORTRAN */ + break; + case 'n': /* ncurses extension */ + no_op = TRUE; + break; + case 'p': + tab_list = "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61"; + /* PL/I */ + break; + case 's': + tab_list = "1,10,55"; + /* SNOBOL */ + break; + case 'u': + tab_list = "1,12,20,44"; + /* UNIVAC 1100 Assembler */ + break; + case 'T': + ++n; + if (*++option != '\0') { + term_name = option; + } else { + term_name = argv[n++]; + } + option += ((int) strlen(option)) - 1; + continue; + default: + if (isdigit(UChar(*option))) { + tab_list = option; + ++n; + } else { + usage(); + } + option += ((int) strlen(option)) - 1; + break; + } + } + break; + case '+': + while ((ch = *++option) != '\0') { + switch (ch) { + case 'm': + mar_list = option; + break; + default: + /* special case of relative stops separated by spaces? */ + if (option == argv[n] + 1) { + tab_list = add_to_tab_list(&append, argv[n]); + } + break; + } + } + break; + default: + if (append != 0) { + if (tab_list != (const char *) append) { + /* one of the predefined options was used */ + free(append); + append = 0; + } + } + tab_list = add_to_tab_list(&append, option); + break; + } + } + + setupterm(term_name, STDOUT_FILENO, (int *) 0); + + max_cols = (columns > 0) ? columns : 80; + + if (!VALID_STRING(clear_all_tabs)) { + fprintf(stderr, + "%s: terminal type '%s' cannot reset tabs\n", + argv[0], term_name); + } else if (!VALID_STRING(set_tab)) { + fprintf(stderr, + "%s: terminal type '%s' cannot set tabs\n", + argv[0], term_name); + } else if (legal_tab_list(argv[0], tab_list)) { + int *list = decode_tabs(tab_list); + + if (!no_op) + tputs(clear_all_tabs, 1, putch); + + if (list != 0) { + if (!no_op) + do_tabs(list); + if (debug) { + fflush(stderr); + printf("tabs %s\n", tab_list); + print_ruler(list); + write_tabs(list); + } + free(list); + } else if (debug) { + fflush(stderr); + printf("tabs %s\n", tab_list); + } + rc = EXIT_SUCCESS; + } + if (append != 0) + free(append); + ExitProgram(rc); +} diff --git a/progs/tic.c b/progs/tic.c new file mode 100644 index 0000000..8e89095 --- /dev/null +++ b/progs/tic.c @@ -0,0 +1,1714 @@ +/**************************************************************************** + * Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996 on * + ****************************************************************************/ + +/* + * tic.c --- Main program for terminfo compiler + * by Eric S. Raymond + * + */ + +#include <progs.priv.h> +#include <sys/stat.h> + +#include <dump_entry.h> +#include <transform.h> + +MODULE_ID("$Id: tic.c,v 1.147 2011/02/12 18:39:08 tom Exp $") + +const char *_nc_progname = "tic"; + +static FILE *log_fp; +static FILE *tmp_fp; +static bool capdump = FALSE; /* running as infotocap? */ +static bool infodump = FALSE; /* running as captoinfo? */ +static bool showsummary = FALSE; +static const char *to_remove; + +static void (*save_check_termtype) (TERMTYPE *, bool); +static void check_termtype(TERMTYPE *tt, bool); + +static const char usage_string[] = "\ +[-e names] \ +[-o dir] \ +[-R name] \ +[-v[n]] \ +[-V] \ +[-w[n]] \ +[-\ +1\ +a\ +C\ +c\ +f\ +G\ +g\ +I\ +L\ +N\ +r\ +s\ +T\ +t\ +U\ +x\ +] \ +source-file\n"; + +#if NO_LEAKS +static void +free_namelist(char **src) +{ + if (src != 0) { + int n; + for (n = 0; src[n] != 0; ++n) + free(src[n]); + free(src); + } +} +#endif + +static void +cleanup(char **namelst GCC_UNUSED) +{ +#if NO_LEAKS + free_namelist(namelst); +#endif + if (tmp_fp != 0) + fclose(tmp_fp); + if (to_remove != 0) { +#if HAVE_REMOVE + remove(to_remove); +#else + unlink(to_remove); +#endif + } +} + +static void +failed(const char *msg) +{ + perror(msg); + cleanup((char **) 0); + ExitProgram(EXIT_FAILURE); +} + +static void +usage(void) +{ + static const char *const tbl[] = + { + "Options:", + " -1 format translation output one capability per line", +#if NCURSES_XNAMES + " -a retain commented-out capabilities (sets -x also)", +#endif + " -C translate entries to termcap source form", + " -c check only, validate input without compiling or translating", + " -e<names> translate/compile only entries named by comma-separated list", + " -f format complex strings for readability", + " -G format %{number} to %'char'", + " -g format %'char' to %{number}", + " -I translate entries to terminfo source form", + " -L translate entries to full terminfo source form", + " -N disable smart defaults for source translation", + " -o<dir> set output directory for compiled entry writes", + " -R<name> restrict translation to given terminfo/termcap version", + " -r force resolution of all use entries in source translation", + " -s print summary statistics", + " -T remove size-restrictions on compiled description", +#if NCURSES_XNAMES + " -t suppress commented-out capabilities", +#endif + " -U suppress post-processing of entries", + " -V print version", + " -v[n] set verbosity level", + " -w[n] set format width for translation output", +#if NCURSES_XNAMES + " -x treat unknown capabilities as user-defined", +#endif + "", + "Parameters:", + " <file> file to translate or compile" + }; + size_t j; + + fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string); + for (j = 0; j < SIZEOF(tbl); j++) { + fputs(tbl[j], stderr); + putc('\n', stderr); + } + ExitProgram(EXIT_FAILURE); +} + +#define L_BRACE '{' +#define R_BRACE '}' +#define S_QUOTE '\''; + +static void +write_it(ENTRY * ep) +{ + unsigned n; + int ch; + char *s, *d, *t; + char result[MAX_ENTRY_SIZE]; + + /* + * Look for strings that contain %{number}, convert them to %'char', + * which is shorter and runs a little faster. + */ + for (n = 0; n < STRCOUNT; n++) { + s = ep->tterm.Strings[n]; + if (VALID_STRING(s) + && strchr(s, L_BRACE) != 0) { + d = result; + t = s; + while ((ch = *t++) != 0) { + *d++ = (char) ch; + if (ch == '\\') { + *d++ = *t++; + } else if ((ch == '%') + && (*t == L_BRACE)) { + char *v = 0; + long value = strtol(t + 1, &v, 0); + if (v != 0 + && *v == R_BRACE + && value > 0 + && value != '\\' /* FIXME */ + && value < 127 + && isprint((int) value)) { + *d++ = S_QUOTE; + *d++ = (char) value; + *d++ = S_QUOTE; + t = (v + 1); + } + } + } + *d = 0; + if (strlen(result) < strlen(s)) + strcpy(s, result); + } + } + + _nc_set_type(_nc_first_name(ep->tterm.term_names)); + _nc_curr_line = ep->startline; + _nc_write_entry(&ep->tterm); +} + +static bool +immedhook(ENTRY * ep GCC_UNUSED) +/* write out entries with no use capabilities immediately to save storage */ +{ +#if !HAVE_BIG_CORE + /* + * This is strictly a core-economy kluge. The really clean way to handle + * compilation is to slurp the whole file into core and then do all the + * name-collision checks and entry writes in one swell foop. But the + * terminfo master file is large enough that some core-poor systems swap + * like crazy when you compile it this way...there have been reports of + * this process taking *three hours*, rather than the twenty seconds or + * less typical on my development box. + * + * So. This hook *immediately* writes out the referenced entry if it + * has no use capabilities. The compiler main loop refrains from + * adding the entry to the in-core list when this hook fires. If some + * other entry later needs to reference an entry that got written + * immediately, that's OK; the resolution code will fetch it off disk + * when it can't find it in core. + * + * Name collisions will still be detected, just not as cleanly. The + * write_entry() code complains before overwriting an entry that + * postdates the time of tic's first call to write_entry(). Thus + * it will complain about overwriting entries newly made during the + * tic run, but not about overwriting ones that predate it. + * + * The reason this is a hook, and not in line with the rest of the + * compiler code, is that the support for termcap fallback cannot assume + * it has anywhere to spool out these entries! + * + * The _nc_set_type() call here requires a compensating one in + * _nc_parse_entry(). + * + * If you define HAVE_BIG_CORE, you'll disable this kluge. This will + * make tic a bit faster (because the resolution code won't have to do + * disk I/O nearly as often). + */ + if (ep->nuses == 0) { + int oldline = _nc_curr_line; + + write_it(ep); + _nc_curr_line = oldline; + free(ep->tterm.str_table); + return (TRUE); + } +#endif /* HAVE_BIG_CORE */ + return (FALSE); +} + +static void +put_translate(int c) +/* emit a comment char, translating terminfo names to termcap names */ +{ + static bool in_name = FALSE; + static size_t have, used; + static char *namebuf, *suffix; + + if (in_name) { + if (used + 1 >= have) { + have += 132; + namebuf = typeRealloc(char, have, namebuf); + suffix = typeRealloc(char, have, suffix); + } + if (c == '\n' || c == '@') { + namebuf[used++] = '\0'; + (void) putchar('<'); + (void) fputs(namebuf, stdout); + putchar(c); + in_name = FALSE; + } else if (c != '>') { + namebuf[used++] = (char) c; + } else { /* ah! candidate name! */ + char *up; + NCURSES_CONST char *tp; + + namebuf[used++] = '\0'; + in_name = FALSE; + + suffix[0] = '\0'; + if ((up = strchr(namebuf, '#')) != 0 + || (up = strchr(namebuf, '=')) != 0 + || ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) { + (void) strcpy(suffix, up); + *up = '\0'; + } + + if ((tp = nametrans(namebuf)) != 0) { + (void) putchar(':'); + (void) fputs(tp, stdout); + (void) fputs(suffix, stdout); + (void) putchar(':'); + } else { + /* couldn't find a translation, just dump the name */ + (void) putchar('<'); + (void) fputs(namebuf, stdout); + (void) fputs(suffix, stdout); + (void) putchar('>'); + } + } + } else { + used = 0; + if (c == '<') { + in_name = TRUE; + } else { + putchar(c); + } + } +} + +/* Returns a string, stripped of leading/trailing whitespace */ +static char * +stripped(char *src) +{ + while (isspace(UChar(*src))) + src++; + if (*src != '\0') { + char *dst; + size_t len; + + if ((dst = strdup(src)) == NULL) + failed("strdup"); + + assert(dst != 0); + + len = strlen(dst); + while (--len != 0 && isspace(UChar(dst[len]))) + dst[len] = '\0'; + return dst; + } + return 0; +} + +static FILE * +open_input(const char *filename) +{ + FILE *fp = fopen(filename, "r"); + struct stat sb; + + if (fp == 0) { + fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename); + ExitProgram(EXIT_FAILURE); + } + if (fstat(fileno(fp), &sb) < 0 + || (sb.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename); + ExitProgram(EXIT_FAILURE); + } + return fp; +} + +/* Parse the "-e" option-value into a list of names */ +static char ** +make_namelist(char *src) +{ + char **dst = 0; + + char *s, *base; + unsigned pass, n, nn; + char buffer[BUFSIZ]; + + if (src == 0) { + /* EMPTY */ ; + } else if (strchr(src, '/') != 0) { /* a filename */ + FILE *fp = open_input(src); + + for (pass = 1; pass <= 2; pass++) { + nn = 0; + while (fgets(buffer, sizeof(buffer), fp) != 0) { + if ((s = stripped(buffer)) != 0) { + if (dst != 0) + dst[nn] = s; + else + free(s); + nn++; + } + } + if (pass == 1) { + dst = typeCalloc(char *, nn + 1); + rewind(fp); + } + } + fclose(fp); + } else { /* literal list of names */ + for (pass = 1; pass <= 2; pass++) { + for (n = nn = 0, base = src;; n++) { + int mark = src[n]; + if (mark == ',' || mark == '\0') { + if (pass == 1) { + nn++; + } else { + src[n] = '\0'; + if ((s = stripped(base)) != 0) + dst[nn++] = s; + base = &src[n + 1]; + } + } + if (mark == '\0') + break; + } + if (pass == 1) + dst = typeCalloc(char *, nn + 1); + } + } + if (showsummary && (dst != 0)) { + fprintf(log_fp, "Entries that will be compiled:\n"); + for (n = 0; dst[n] != 0; n++) + fprintf(log_fp, "%u:%s\n", n + 1, dst[n]); + } + return dst; +} + +static bool +matches(char **needle, const char *haystack) +/* does entry in needle list match |-separated field in haystack? */ +{ + bool code = FALSE; + size_t n; + + if (needle != 0) { + for (n = 0; needle[n] != 0; n++) { + if (_nc_name_match(haystack, needle[n], "|")) { + code = TRUE; + break; + } + } + } else + code = TRUE; + return (code); +} + +static FILE * +open_tempfile(char *name) +{ + FILE *result = 0; +#if HAVE_MKSTEMP + int fd = mkstemp(name); + if (fd >= 0) + result = fdopen(fd, "w"); +#else + if (tmpnam(name) != 0) + result = fopen(name, "w"); +#endif + return result; +} + +int +main(int argc, char *argv[]) +{ + char my_tmpname[PATH_MAX]; + int v_opt = -1, debug_level; + int smart_defaults = TRUE; + char *termcap; + ENTRY *qp; + + int this_opt, last_opt = '?'; + + int outform = F_TERMINFO; /* output format */ + int sortmode = S_TERMINFO; /* sort_mode */ + + int width = 60; + bool formatted = FALSE; /* reformat complex strings? */ + bool literal = FALSE; /* suppress post-processing? */ + int numbers = 0; /* format "%'char'" to/from "%{number}" */ + bool forceresolve = FALSE; /* force resolution */ + bool limited = TRUE; + char *tversion = (char *) NULL; + const char *source_file = "terminfo"; + char **namelst = 0; + char *outdir = (char *) NULL; + bool check_only = FALSE; + bool suppress_untranslatable = FALSE; + + log_fp = stderr; + + _nc_progname = _nc_rootname(argv[0]); + + if ((infodump = same_program(_nc_progname, PROG_CAPTOINFO)) != FALSE) { + outform = F_TERMINFO; + sortmode = S_TERMINFO; + } + if ((capdump = same_program(_nc_progname, PROG_INFOTOCAP)) != FALSE) { + outform = F_TERMCAP; + sortmode = S_TERMCAP; + } +#if NCURSES_XNAMES + use_extended_names(FALSE); +#endif + + /* + * Processing arguments is a little complicated, since someone made a + * design decision to allow the numeric values for -w, -v options to + * be optional. + */ + while ((this_opt = getopt(argc, argv, + "0123456789CILNR:TUVace:fGgo:rstvwx")) != -1) { + if (isdigit(this_opt)) { + switch (last_opt) { + case 'v': + v_opt = (v_opt * 10) + (this_opt - '0'); + break; + case 'w': + width = (width * 10) + (this_opt - '0'); + break; + default: + if (this_opt != '1') + usage(); + last_opt = this_opt; + width = 0; + } + continue; + } + switch (this_opt) { + case 'C': + capdump = TRUE; + outform = F_TERMCAP; + sortmode = S_TERMCAP; + break; + case 'I': + infodump = TRUE; + outform = F_TERMINFO; + sortmode = S_TERMINFO; + break; + case 'L': + infodump = TRUE; + outform = F_VARIABLE; + sortmode = S_VARIABLE; + break; + case 'N': + smart_defaults = FALSE; + literal = TRUE; + break; + case 'R': + tversion = optarg; + break; + case 'T': + limited = FALSE; + break; + case 'U': + literal = TRUE; + break; + case 'V': + puts(curses_version()); + cleanup(namelst); + ExitProgram(EXIT_SUCCESS); + case 'c': + check_only = TRUE; + break; + case 'e': + namelst = make_namelist(optarg); + break; + case 'f': + formatted = TRUE; + break; + case 'G': + numbers = 1; + break; + case 'g': + numbers = -1; + break; + case 'o': + outdir = optarg; + break; + case 'r': + forceresolve = TRUE; + break; + case 's': + showsummary = TRUE; + break; + case 'v': + v_opt = 0; + break; + case 'w': + width = 0; + break; +#if NCURSES_XNAMES + case 't': + _nc_disable_period = FALSE; + suppress_untranslatable = TRUE; + break; + case 'a': + _nc_disable_period = TRUE; + /* FALLTHRU */ + case 'x': + use_extended_names(TRUE); + break; +#endif + default: + usage(); + } + last_opt = this_opt; + } + + debug_level = (v_opt > 0) ? v_opt : (v_opt == 0); + set_trace_level(debug_level); + + if (_nc_tracing) { + save_check_termtype = _nc_check_termtype2; + _nc_check_termtype2 = check_termtype; + } +#if !HAVE_BIG_CORE + /* + * Aaargh! immedhook seriously hoses us! + * + * One problem with immedhook is it means we can't do -e. Problem + * is that we can't guarantee that for each terminal listed, all the + * terminals it depends on will have been kept in core for reference + * resolution -- in fact it's certain the primitive types at the end + * of reference chains *won't* be in core unless they were explicitly + * in the select list themselves. + */ + if (namelst && (!infodump && !capdump)) { + (void) fprintf(stderr, + "Sorry, -e can't be used without -I or -C\n"); + cleanup(namelst); + ExitProgram(EXIT_FAILURE); + } +#endif /* HAVE_BIG_CORE */ + + if (optind < argc) { + source_file = argv[optind++]; + if (optind < argc) { + fprintf(stderr, + "%s: Too many file names. Usage:\n\t%s %s", + _nc_progname, + _nc_progname, + usage_string); + ExitProgram(EXIT_FAILURE); + } + } else { + if (infodump == TRUE) { + /* captoinfo's no-argument case */ + source_file = "/etc/termcap"; + if ((termcap = getenv("TERMCAP")) != 0 + && (namelst = make_namelist(getenv("TERM"))) != 0) { + if (access(termcap, F_OK) == 0) { + /* file exists */ + source_file = termcap; + } else if ((tmp_fp = open_tempfile(strcpy(my_tmpname, + "/tmp/XXXXXX"))) + != 0) { + source_file = my_tmpname; + fprintf(tmp_fp, "%s\n", termcap); + fclose(tmp_fp); + tmp_fp = open_input(source_file); + to_remove = source_file; + } else { + failed("tmpnam"); + } + } + } else { + /* tic */ + fprintf(stderr, + "%s: File name needed. Usage:\n\t%s %s", + _nc_progname, + _nc_progname, + usage_string); + cleanup(namelst); + ExitProgram(EXIT_FAILURE); + } + } + + if (tmp_fp == 0) + tmp_fp = open_input(source_file); + + if (infodump) + dump_init(tversion, + smart_defaults + ? outform + : F_LITERAL, + sortmode, width, debug_level, formatted); + else if (capdump) + dump_init(tversion, + outform, + sortmode, width, debug_level, FALSE); + + /* parse entries out of the source file */ + _nc_set_source(source_file); +#if !HAVE_BIG_CORE + if (!(check_only || infodump || capdump)) + _nc_set_writedir(outdir); +#endif /* HAVE_BIG_CORE */ + _nc_read_entry_source(tmp_fp, (char *) NULL, + !smart_defaults || literal, FALSE, + ((check_only || infodump || capdump) + ? NULLHOOK + : immedhook)); + + /* do use resolution */ + if (check_only || (!infodump && !capdump) || forceresolve) { + if (!_nc_resolve_uses2(TRUE, literal) && !check_only) { + cleanup(namelst); + ExitProgram(EXIT_FAILURE); + } + } + + /* length check */ + if (check_only && (capdump || infodump)) { + for_entry_list(qp) { + if (matches(namelst, qp->tterm.term_names)) { + int len = fmt_entry(&qp->tterm, NULL, FALSE, TRUE, infodump, numbers); + + if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH)) + (void) fprintf(stderr, + "warning: resolved %s entry is %d bytes long\n", + _nc_first_name(qp->tterm.term_names), + len); + } + } + } + + /* write or dump all entries */ + if (!check_only) { + if (!infodump && !capdump) { + _nc_set_writedir(outdir); + for_entry_list(qp) { + if (matches(namelst, qp->tterm.term_names)) + write_it(qp); + } + } else { + /* this is in case infotocap() generates warnings */ + _nc_curr_col = _nc_curr_line = -1; + + for_entry_list(qp) { + if (matches(namelst, qp->tterm.term_names)) { + int j = qp->cend - qp->cstart; + int len = 0; + + /* this is in case infotocap() generates warnings */ + _nc_set_type(_nc_first_name(qp->tterm.term_names)); + + (void) fseek(tmp_fp, qp->cstart, SEEK_SET); + while (j-- > 0) { + if (infodump) + (void) putchar(fgetc(tmp_fp)); + else + put_translate(fgetc(tmp_fp)); + } + + repair_acsc(&qp->tterm); + dump_entry(&qp->tterm, suppress_untranslatable, + limited, numbers, NULL); + for (j = 0; j < (int) qp->nuses; j++) + dump_uses(qp->uses[j].name, !capdump); + len = show_entry(); + if (debug_level != 0 && !limited) + printf("# length=%d\n", len); + } + } + if (!namelst && _nc_tail) { + int c, oldc = '\0'; + bool in_comment = FALSE; + bool trailing_comment = FALSE; + + (void) fseek(tmp_fp, _nc_tail->cend, SEEK_SET); + while ((c = fgetc(tmp_fp)) != EOF) { + if (oldc == '\n') { + if (c == '#') { + trailing_comment = TRUE; + in_comment = TRUE; + } else { + in_comment = FALSE; + } + } + if (trailing_comment + && (in_comment || (oldc == '\n' && c == '\n'))) + putchar(c); + oldc = c; + } + } + } + } + + /* Show the directory into which entries were written, and the total + * number of entries + */ + if (showsummary + && (!(check_only || infodump || capdump))) { + int total = _nc_tic_written(); + if (total != 0) + fprintf(log_fp, "%d entries written to %s\n", + total, + _nc_tic_dir((char *) 0)); + else + fprintf(log_fp, "No entries written\n"); + } + cleanup(namelst); + ExitProgram(EXIT_SUCCESS); +} + +/* + * This bit of legerdemain turns all the terminfo variable names into + * references to locations in the arrays Booleans, Numbers, and Strings --- + * precisely what's needed (see comp_parse.c). + */ +#undef CUR +#define CUR tp-> + +/* + * Check if the alternate character-set capabilities are consistent. + */ +static void +check_acs(TERMTYPE *tp) +{ + if (VALID_STRING(acs_chars)) { + const char *boxes = "lmkjtuvwqxn"; + char mapped[256]; + char missing[256]; + const char *p; + char *q; + + memset(mapped, 0, sizeof(mapped)); + for (p = acs_chars; *p != '\0'; p += 2) { + if (p[1] == '\0') { + _nc_warning("acsc has odd number of characters"); + break; + } + mapped[UChar(p[0])] = p[1]; + } + + if (mapped[UChar('I')] && !mapped[UChar('i')]) { + _nc_warning("acsc refers to 'I', which is probably an error"); + } + + for (p = boxes, q = missing; *p != '\0'; ++p) { + if (!mapped[UChar(p[0])]) { + *q++ = p[0]; + } + } + *q = '\0'; + + assert(strlen(missing) <= strlen(boxes)); + if (*missing != '\0' && strcmp(missing, boxes)) { + _nc_warning("acsc is missing some line-drawing mapping: %s", missing); + } + } +} + +/* + * Check if the color capabilities are consistent + */ +static void +check_colors(TERMTYPE *tp) +{ + if ((max_colors > 0) != (max_pairs > 0) + || ((max_colors > max_pairs) && (initialize_pair == 0))) + _nc_warning("inconsistent values for max_colors (%d) and max_pairs (%d)", + max_colors, max_pairs); + + PAIRED(set_foreground, set_background); + PAIRED(set_a_foreground, set_a_background); + PAIRED(set_color_pair, initialize_pair); + + if (VALID_STRING(set_foreground) + && VALID_STRING(set_a_foreground) + && !_nc_capcmp(set_foreground, set_a_foreground)) + _nc_warning("expected setf/setaf to be different"); + + if (VALID_STRING(set_background) + && VALID_STRING(set_a_background) + && !_nc_capcmp(set_background, set_a_background)) + _nc_warning("expected setb/setab to be different"); + + /* see: has_colors() */ + if (VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs) + && (((set_foreground != NULL) + && (set_background != NULL)) + || ((set_a_foreground != NULL) + && (set_a_background != NULL)) + || set_color_pair)) { + if (!VALID_STRING(orig_pair) && !VALID_STRING(orig_colors)) + _nc_warning("expected either op/oc string for resetting colors"); + } +} + +static char +keypad_final(const char *string) +{ + char result = '\0'; + + if (VALID_STRING(string) + && *string++ == '\033' + && *string++ == 'O' + && strlen(string) == 1) { + result = *string; + } + + return result; +} + +static int +keypad_index(const char *string) +{ + char *test; + const char *list = "PQRSwxymtuvlqrsPpn"; /* app-keypad except "Enter" */ + int ch; + int result = -1; + + if ((ch = keypad_final(string)) != '\0') { + test = strchr(list, ch); + if (test != 0) + result = (test - list); + } + return result; +} + +/* + * list[] is down, up, left, right + * "left" may be ^H rather than \E[D + * "down" may be ^J rather than \E[B + * But up/right are generally consistently escape sequences for ANSI terminals. + */ +static void +check_ansi_cursor(char *list[4]) +{ + int j, k; + int want; + size_t prefix = 0; + size_t suffix; + bool skip[4]; + bool repeated = FALSE; + + for (j = 0; j < 4; ++j) { + skip[j] = FALSE; + for (k = 0; k < j; ++k) { + if (j != k + && !strcmp(list[j], list[k])) { + char *value = _nc_tic_expand(list[k], TRUE, 0); + _nc_warning("repeated cursor control %s\n", value); + repeated = TRUE; + } + } + } + if (!repeated) { + char *up = list[1]; + + if (UChar(up[0]) == '\033') { + if (up[1] == '[') { + prefix = 2; + } else { + prefix = 1; + } + } else if (UChar(up[0]) == UChar('\233')) { + prefix = 1; + } + if (prefix) { + suffix = prefix; + while (up[suffix] && isdigit(UChar(up[suffix]))) + ++suffix; + } + if (prefix && up[suffix] == 'A') { + skip[1] = TRUE; + if (!strcmp(list[0], "\n")) + skip[0] = TRUE; + if (!strcmp(list[2], "\b")) + skip[2] = TRUE; + + for (j = 0; j < 4; ++j) { + if (skip[j] || strlen(list[j]) == 1) + continue; + if (memcmp(list[j], up, prefix)) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent prefix for %s\n", value); + continue; + } + if (strlen(list[j]) < suffix) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent length for %s, expected %d\n", + value, (int) suffix + 1); + continue; + } + want = "BADC"[j]; + if (list[j][suffix] != want) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent suffix for %s, expected %c, have %c\n", + value, want, list[j][suffix]); + } + } + } + } +} + +#define EXPECTED(name) if (!PRESENT(name)) _nc_warning("expected " #name) + +static void +check_cursor(TERMTYPE *tp) +{ + int count; + char *list[4]; + + /* if we have a parameterized form, then the non-parameterized is easy */ + ANDMISSING(parm_down_cursor, cursor_down); + ANDMISSING(parm_up_cursor, cursor_up); + ANDMISSING(parm_left_cursor, cursor_left); + ANDMISSING(parm_right_cursor, cursor_right); + + /* Given any of a set of cursor movement, the whole set should be present. + * Technically this is not true (we could use cursor_address to fill in + * unsupported controls), but it is likely. + */ + count = 0; + if (PRESENT(parm_down_cursor)) { + list[count++] = parm_down_cursor; + } + if (PRESENT(parm_up_cursor)) { + list[count++] = parm_up_cursor; + } + if (PRESENT(parm_left_cursor)) { + list[count++] = parm_left_cursor; + } + if (PRESENT(parm_right_cursor)) { + list[count++] = parm_right_cursor; + } + if (count == 4) { + check_ansi_cursor(list); + } else if (count != 0) { + EXPECTED(parm_down_cursor); + EXPECTED(parm_up_cursor); + EXPECTED(parm_left_cursor); + EXPECTED(parm_right_cursor); + } + + count = 0; + if (PRESENT(cursor_down)) { + list[count++] = cursor_down; + } + if (PRESENT(cursor_up)) { + list[count++] = cursor_up; + } + if (PRESENT(cursor_left)) { + list[count++] = cursor_left; + } + if (PRESENT(cursor_right)) { + list[count++] = cursor_right; + } + if (count == 4) { + check_ansi_cursor(list); + } else if (count != 0) { + count = 0; + if (PRESENT(cursor_down) && strcmp(cursor_down, "\n")) + ++count; + if (PRESENT(cursor_left) && strcmp(cursor_left, "\b")) + ++count; + if (PRESENT(cursor_up) && strlen(cursor_up) > 1) + ++count; + if (PRESENT(cursor_right) && strlen(cursor_right) > 1) + ++count; + if (count) { + EXPECTED(cursor_down); + EXPECTED(cursor_up); + EXPECTED(cursor_left); + EXPECTED(cursor_right); + } + } +} + +#define MAX_KP 5 +/* + * Do a quick sanity-check for vt100-style keypads to see if the 5-key keypad + * is mapped inconsistently. + */ +static void +check_keypad(TERMTYPE *tp) +{ + char show[80]; + + if (VALID_STRING(key_a1) && + VALID_STRING(key_a3) && + VALID_STRING(key_b2) && + VALID_STRING(key_c1) && + VALID_STRING(key_c3)) { + char final[MAX_KP + 1]; + int list[MAX_KP]; + int increase = 0; + int j, k, kk; + int last; + int test; + + final[0] = keypad_final(key_a1); + final[1] = keypad_final(key_a3); + final[2] = keypad_final(key_b2); + final[3] = keypad_final(key_c1); + final[4] = keypad_final(key_c3); + final[5] = '\0'; + + /* special case: legacy coding using 1,2,3,0,. on the bottom */ + assert(strlen(final) <= MAX_KP); + if (!strcmp(final, "qsrpn")) + return; + + list[0] = keypad_index(key_a1); + list[1] = keypad_index(key_a3); + list[2] = keypad_index(key_b2); + list[3] = keypad_index(key_c1); + list[4] = keypad_index(key_c3); + + /* check that they're all vt100 keys */ + for (j = 0; j < MAX_KP; ++j) { + if (list[j] < 0) { + return; + } + } + + /* check if they're all in increasing order */ + for (j = 1; j < MAX_KP; ++j) { + if (list[j] > list[j - 1]) { + ++increase; + } + } + if (increase != (MAX_KP - 1)) { + show[0] = '\0'; + + for (j = 0, last = -1; j < MAX_KP; ++j) { + for (k = 0, kk = -1, test = 100; k < 5; ++k) { + if (list[k] > last && + list[k] < test) { + test = list[k]; + kk = k; + } + } + last = test; + assert(strlen(show) < (MAX_KP * 4)); + switch (kk) { + case 0: + strcat(show, " ka1"); + break; + case 1: + strcat(show, " ka3"); + break; + case 2: + strcat(show, " kb2"); + break; + case 3: + strcat(show, " kc1"); + break; + case 4: + strcat(show, " kc3"); + break; + } + } + + _nc_warning("vt100 keypad order inconsistent: %s", show); + } + + } else if (VALID_STRING(key_a1) || + VALID_STRING(key_a3) || + VALID_STRING(key_b2) || + VALID_STRING(key_c1) || + VALID_STRING(key_c3)) { + show[0] = '\0'; + if (keypad_index(key_a1) >= 0) + strcat(show, " ka1"); + if (keypad_index(key_a3) >= 0) + strcat(show, " ka3"); + if (keypad_index(key_b2) >= 0) + strcat(show, " kb2"); + if (keypad_index(key_c1) >= 0) + strcat(show, " kc1"); + if (keypad_index(key_c3) >= 0) + strcat(show, " kc3"); + if (*show != '\0') + _nc_warning("vt100 keypad map incomplete:%s", show); + } +} + +static void +check_printer(TERMTYPE *tp) +{ + PAIRED(enter_doublewide_mode, exit_doublewide_mode); + PAIRED(enter_italics_mode, exit_italics_mode); + PAIRED(enter_leftward_mode, exit_leftward_mode); + PAIRED(enter_micro_mode, exit_micro_mode); + PAIRED(enter_shadow_mode, exit_shadow_mode); + PAIRED(enter_subscript_mode, exit_subscript_mode); + PAIRED(enter_superscript_mode, exit_superscript_mode); + PAIRED(enter_upward_mode, exit_upward_mode); + + ANDMISSING(start_char_set_def, stop_char_set_def); + + /* if we have a parameterized form, then the non-parameterized is easy */ + ANDMISSING(set_bottom_margin_parm, set_bottom_margin); + ANDMISSING(set_left_margin_parm, set_left_margin); + ANDMISSING(set_right_margin_parm, set_right_margin); + ANDMISSING(set_top_margin_parm, set_top_margin); + + ANDMISSING(parm_down_micro, micro_down); + ANDMISSING(parm_left_micro, micro_left); + ANDMISSING(parm_right_micro, micro_right); + ANDMISSING(parm_up_micro, micro_up); +} + +/* + * Returns the expected number of parameters for the given capability. + */ +static int +expected_params(const char *name) +{ + /* *INDENT-OFF* */ + static const struct { + const char *name; + int count; + } table[] = { + { "S0", 1 }, /* 'screen' extension */ + { "birep", 2 }, + { "chr", 1 }, + { "colornm", 1 }, + { "cpi", 1 }, + { "csnm", 1 }, + { "csr", 2 }, + { "cub", 1 }, + { "cud", 1 }, + { "cuf", 1 }, + { "cup", 2 }, + { "cuu", 1 }, + { "cvr", 1 }, + { "cwin", 5 }, + { "dch", 1 }, + { "defc", 3 }, + { "dial", 1 }, + { "dispc", 1 }, + { "dl", 1 }, + { "ech", 1 }, + { "getm", 1 }, + { "hpa", 1 }, + { "ich", 1 }, + { "il", 1 }, + { "indn", 1 }, + { "initc", 4 }, + { "initp", 7 }, + { "lpi", 1 }, + { "mc5p", 1 }, + { "mrcup", 2 }, + { "mvpa", 1 }, + { "pfkey", 2 }, + { "pfloc", 2 }, + { "pfx", 2 }, + { "pfxl", 3 }, + { "pln", 2 }, + { "qdial", 1 }, + { "rcsd", 1 }, + { "rep", 2 }, + { "rin", 1 }, + { "sclk", 3 }, + { "scp", 1 }, + { "scs", 1 }, + { "scsd", 2 }, + { "setab", 1 }, + { "setaf", 1 }, + { "setb", 1 }, + { "setcolor", 1 }, + { "setf", 1 }, + { "sgr", 9 }, + { "sgr1", 6 }, + { "slength", 1 }, + { "slines", 1 }, + { "smgbp", 1 }, /* 2 if smgtp is not given */ + { "smglp", 1 }, + { "smglr", 2 }, + { "smgrp", 1 }, + { "smgtb", 2 }, + { "smgtp", 1 }, + { "tsl", 1 }, + { "u6", -1 }, + { "vpa", 1 }, + { "wind", 4 }, + { "wingo", 1 }, + }; + /* *INDENT-ON* */ + + unsigned n; + int result = 0; /* function-keys, etc., use none */ + + for (n = 0; n < SIZEOF(table); n++) { + if (!strcmp(name, table[n].name)) { + result = table[n].count; + break; + } + } + + return result; +} + +/* + * Make a quick sanity check for the parameters which are used in the given + * strings. If there are no "%p" tokens, then there should be no other "%" + * markers. + */ +static void +check_params(TERMTYPE *tp, const char *name, char *value) +{ + int expected = expected_params(name); + int actual = 0; + int n; + bool params[10]; + char *s = value; + +#ifdef set_top_margin_parm + if (!strcmp(name, "smgbp") + && set_top_margin_parm == 0) + expected = 2; +#endif + + for (n = 0; n < 10; n++) + params[n] = FALSE; + + while (*s != 0) { + if (*s == '%') { + if (*++s == '\0') { + _nc_warning("expected character after %% in %s", name); + break; + } else if (*s == 'p') { + if (*++s == '\0' || !isdigit((int) *s)) { + _nc_warning("expected digit after %%p in %s", name); + return; + } else { + n = (*s - '0'); + if (n > actual) + actual = n; + params[n] = TRUE; + } + } + } + s++; + } + + if (params[0]) { + _nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name); + } + if (value == set_attributes || expected < 0) { + ; + } else if (expected != actual) { + _nc_warning("%s uses %d parameters, expected %d", name, + actual, expected); + for (n = 1; n < actual; n++) { + if (!params[n]) + _nc_warning("%s omits parameter %d", name, n); + } + } +} + +static char * +skip_delay(char *s) +{ + while (*s == '/' || isdigit(UChar(*s))) + ++s; + return s; +} + +/* + * Skip a delay altogether, e.g., when comparing a simple string to sgr, + * the latter may have a worst-case delay on the end. + */ +static char * +ignore_delays(char *s) +{ + int delaying = 0; + + do { + switch (*s) { + case '$': + if (delaying == 0) + delaying = 1; + break; + case '<': + if (delaying == 1) + delaying = 2; + break; + case '\0': + delaying = 0; + break; + default: + if (delaying) { + s = skip_delay(s); + if (*s == '>') + ++s; + delaying = 0; + } + break; + } + if (delaying) + ++s; + } while (delaying); + return s; +} + +/* + * An sgr string may contain several settings other than the one we're + * interested in, essentially sgr0 + rmacs + whatever. As long as the + * "whatever" is contained in the sgr string, that is close enough for our + * sanity check. + */ +static bool +similar_sgr(int num, char *a, char *b) +{ + static const char *names[] = + { + "none" + ,"standout" + ,"underline" + ,"reverse" + ,"blink" + ,"dim" + ,"bold" + ,"invis" + ,"protect" + ,"altcharset" + }; + char *base_a = a; + char *base_b = b; + int delaying = 0; + + while (*b != 0) { + while (*a != *b) { + if (*a == 0) { + if (b[0] == '$' + && b[1] == '<') { + _nc_warning("Did not find delay %s", _nc_visbuf(b)); + } else { + _nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s", + names[num], _nc_visbuf2(1, base_a), + _nc_visbuf2(2, base_b), + _nc_visbuf2(3, b)); + } + return FALSE; + } else if (delaying) { + a = skip_delay(a); + b = skip_delay(b); + } else if ((*b == '0' || (*b == ';')) && *a == 'm') { + b++; + } else { + a++; + } + } + switch (*a) { + case '$': + if (delaying == 0) + delaying = 1; + break; + case '<': + if (delaying == 1) + delaying = 2; + break; + default: + delaying = 0; + break; + } + a++; + b++; + } + /* ignore delays on the end of the string */ + a = ignore_delays(a); + return ((num != 0) || (*a == 0)); +} + +static char * +check_sgr(TERMTYPE *tp, char *zero, int num, char *cap, const char *name) +{ + char *test; + + _nc_tparm_err = 0; + test = TPARM_9(set_attributes, + num == 1, + num == 2, + num == 3, + num == 4, + num == 5, + num == 6, + num == 7, + num == 8, + num == 9); + if (test != 0) { + if (PRESENT(cap)) { + if (!similar_sgr(num, test, cap)) { + _nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s", + name, num, + name, _nc_visbuf2(1, cap), + num, _nc_visbuf2(2, test)); + } + } else if (_nc_capcmp(test, zero)) { + _nc_warning("sgr(%d) present, but not %s", num, name); + } + } else if (PRESENT(cap)) { + _nc_warning("sgr(%d) missing, but %s present", num, name); + } + if (_nc_tparm_err) + _nc_warning("stack error in sgr(%d) string", num); + return test; +} + +#define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name) + +#ifdef TRACE +/* + * If tic is compiled with TRACE, we'll be able to see the output from the + * DEBUG() macro. But since it doesn't use traceon(), it always goes to + * the standard error. Use this function to make it simpler to follow the + * resulting debug traces. + */ +static void +show_where(unsigned level) +{ + if (_nc_tracing >= DEBUG_LEVEL(level)) { + char my_name[256]; + _nc_get_type(my_name); + _tracef("\"%s\", line %d, '%s'", + _nc_get_source(), + _nc_curr_line, my_name); + } +} + +#else +#define show_where(level) /* nothing */ +#endif + +/* other sanity-checks (things that we don't want in the normal + * logic that reads a terminfo entry) + */ +static void +check_termtype(TERMTYPE *tp, bool literal) +{ + bool conflict = FALSE; + unsigned j, k; + char fkeys[STRCOUNT]; + + /* + * A terminal entry may contain more than one keycode assigned to + * a given string (e.g., KEY_END and KEY_LL). But curses will only + * return one (the last one assigned). + */ + if (!(_nc_syntax == SYN_TERMCAP && capdump)) { + memset(fkeys, 0, sizeof(fkeys)); + for (j = 0; _nc_tinfo_fkeys[j].code; j++) { + char *a = tp->Strings[_nc_tinfo_fkeys[j].offset]; + bool first = TRUE; + if (!VALID_STRING(a)) + continue; + for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) { + char *b = tp->Strings[_nc_tinfo_fkeys[k].offset]; + if (!VALID_STRING(b) + || fkeys[k]) + continue; + if (!_nc_capcmp(a, b)) { + fkeys[j] = 1; + fkeys[k] = 1; + if (first) { + if (!conflict) { + _nc_warning("Conflicting key definitions (using the last)"); + conflict = TRUE; + } + fprintf(stderr, "... %s is the same as %s", + keyname((int) _nc_tinfo_fkeys[j].code), + keyname((int) _nc_tinfo_fkeys[k].code)); + first = FALSE; + } else { + fprintf(stderr, ", %s", + keyname((int) _nc_tinfo_fkeys[k].code)); + } + } + } + if (!first) + fprintf(stderr, "\n"); + } + } + + for (j = 0; j < NUM_STRINGS(tp); j++) { + char *a = tp->Strings[j]; + if (VALID_STRING(a)) + check_params(tp, ExtStrname(tp, j, strnames), a); + } + + check_acs(tp); + check_colors(tp); + check_cursor(tp); + check_keypad(tp); + check_printer(tp); + + /* + * These may be mismatched because the terminal description relies on + * restoring the cursor visibility by resetting it. + */ + ANDMISSING(cursor_invisible, cursor_normal); + ANDMISSING(cursor_visible, cursor_normal); + + if (PRESENT(cursor_visible) && PRESENT(cursor_normal) + && !_nc_capcmp(cursor_visible, cursor_normal)) + _nc_warning("cursor_visible is same as cursor_normal"); + + /* + * From XSI & O'Reilly, we gather that sc/rc are required if csr is + * given, because the cursor position after the scrolling operation is + * performed is undefined. + */ + ANDMISSING(change_scroll_region, save_cursor); + ANDMISSING(change_scroll_region, restore_cursor); + + /* + * If we can clear tabs, we should be able to initialize them. + */ + ANDMISSING(clear_all_tabs, set_tab); + + if (PRESENT(set_attributes)) { + char *zero = 0; + + _nc_tparm_err = 0; + if (PRESENT(exit_attribute_mode)) { + zero = strdup(CHECK_SGR(0, exit_attribute_mode)); + } else { + zero = strdup(TPARM_9(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + } + if (_nc_tparm_err) + _nc_warning("stack error in sgr(0) string"); + + if (zero != 0) { + CHECK_SGR(1, enter_standout_mode); + CHECK_SGR(2, enter_underline_mode); + CHECK_SGR(3, enter_reverse_mode); + CHECK_SGR(4, enter_blink_mode); + CHECK_SGR(5, enter_dim_mode); + CHECK_SGR(6, enter_bold_mode); + CHECK_SGR(7, enter_secure_mode); + CHECK_SGR(8, enter_protected_mode); + CHECK_SGR(9, enter_alt_charset_mode); + free(zero); + } else { + _nc_warning("sgr(0) did not return a value"); + } + } else if (PRESENT(exit_attribute_mode) && + set_attributes != CANCELLED_STRING) { + if (_nc_syntax == SYN_TERMINFO) + _nc_warning("missing sgr string"); + } + + if (PRESENT(exit_attribute_mode)) { + char *check_sgr0 = _nc_trim_sgr0(tp); + + if (check_sgr0 == 0 || *check_sgr0 == '\0') { + _nc_warning("trimmed sgr0 is empty"); + } else { + show_where(2); + if (check_sgr0 != exit_attribute_mode) { + DEBUG(2, + ("will trim sgr0\n\toriginal sgr0=%s\n\ttrimmed sgr0=%s", + _nc_visbuf2(1, exit_attribute_mode), + _nc_visbuf2(2, check_sgr0))); + free(check_sgr0); + } else { + DEBUG(2, + ("will not trim sgr0\n\toriginal sgr0=%s", + _nc_visbuf(exit_attribute_mode))); + } + } + } +#ifdef TRACE + show_where(2); + if (!auto_right_margin) { + DEBUG(2, + ("can write to lower-right directly")); + } else if (PRESENT(enter_am_mode) && PRESENT(exit_am_mode)) { + DEBUG(2, + ("can write to lower-right by suppressing automargin")); + } else if ((PRESENT(enter_insert_mode) && PRESENT(exit_insert_mode)) + || PRESENT(insert_character) || PRESENT(parm_ich)) { + DEBUG(2, + ("can write to lower-right by using inserts")); + } else { + DEBUG(2, + ("cannot write to lower-right")); + } +#endif + + /* + * Some standard applications (e.g., vi) and some non-curses + * applications (e.g., jove) get confused if we have both ich1 and + * smir/rmir. Let's be nice and warn about that, too, even though + * ncurses handles it. + */ + if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode)) + && PRESENT(parm_ich)) { + _nc_warning("non-curses applications may be confused by ich1 with smir/rmir"); + } + + /* + * Finally, do the non-verbose checks + */ + if (save_check_termtype != 0) + save_check_termtype(tp, literal); +} diff --git a/progs/toe.c b/progs/toe.c new file mode 100644 index 0000000..6f45992 --- /dev/null +++ b/progs/toe.c @@ -0,0 +1,525 @@ +/**************************************************************************** + * Copyright (c) 1998-2008,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996-on * + ****************************************************************************/ + +/* + * toe.c --- table of entries report generator + */ + +#include <progs.priv.h> + +#include <sys/stat.h> + +#if USE_HASHED_DB +#include <hashed_db.h> +#endif + +MODULE_ID("$Id: toe.c,v 1.52 2010/05/01 22:04:08 tom Exp $") + +#define isDotname(name) (!strcmp(name, ".") || !strcmp(name, "..")) + +const char *_nc_progname; + +#if NO_LEAKS +#undef ExitProgram +static void ExitProgram(int code) GCC_NORETURN; +static void +ExitProgram(int code) +{ + _nc_free_entries(_nc_head); + _nc_free_tic(code); +} +#endif + +static void +failed(const char *msg) +{ + perror(msg); + ExitProgram(EXIT_FAILURE); +} + +#if USE_HASHED_DB +static bool +make_db_name(char *dst, const char *src, unsigned limit) +{ + static const char suffix[] = DBM_SUFFIX; + + bool result = FALSE; + unsigned lens = sizeof(suffix) - 1; + unsigned size = strlen(src); + unsigned need = lens + size; + + if (need <= limit) { + if (size >= lens + && !strcmp(src + size - lens, suffix)) + (void) strcpy(dst, src); + else + (void) sprintf(dst, "%s%s", src, suffix); + result = TRUE; + } + return result; +} +#endif + +static bool +is_database(const char *path) +{ + bool result = FALSE; +#if USE_DATABASE + if (_nc_is_dir_path(path) && access(path, R_OK | X_OK) == 0) { + result = TRUE; + } +#endif +#if USE_TERMCAP + if (_nc_is_file_path(path) && access(path, R_OK) == 0) { + result = TRUE; + } +#endif +#if USE_HASHED_DB + if (!result) { + char filename[PATH_MAX]; + if (_nc_is_file_path(path) && access(path, R_OK) == 0) { + result = TRUE; + } else if (make_db_name(filename, path, sizeof(filename))) { + if (_nc_is_file_path(filename) && access(filename, R_OK) == 0) { + result = TRUE; + } + } + } +#endif + return result; +} + +static void +deschook(const char *cn, TERMTYPE *tp) +/* display a description for the type */ +{ + const char *desc; + + if ((desc = strrchr(tp->term_names, '|')) == 0 || *++desc == '\0') + desc = "(No description)"; + + (void) printf("%-10s\t%s\n", cn, desc); +} + +#if USE_TERMCAP +static void +show_termcap(char *buffer, + void (*hook) (const char *, TERMTYPE *tp)) +{ + TERMTYPE data; + char *next = strchr(buffer, ':'); + char *last; + char *list = buffer; + + if (next) + *next = '\0'; + + last = strrchr(buffer, '|'); + if (last) + ++last; + + data.term_names = strdup(buffer); + while ((next = strtok(list, "|")) != 0) { + if (next != last) + hook(next, &data); + list = 0; + } + free(data.term_names); +} +#endif + +static int +typelist(int eargc, char *eargv[], + bool verbosity, + void (*hook) (const char *, TERMTYPE *tp)) +/* apply a function to each entry in given terminfo directories */ +{ + int i; + + for (i = 0; i < eargc; i++) { +#if USE_DATABASE + if (_nc_is_dir_path(eargv[i])) { + char *cwd_buf = 0; + DIR *termdir; + DIRENT *subdir; + + if ((termdir = opendir(eargv[i])) == 0) { + (void) fflush(stdout); + (void) fprintf(stderr, + "%s: can't open terminfo directory %s\n", + _nc_progname, eargv[i]); + return (EXIT_FAILURE); + } else if (verbosity) + (void) printf("#\n#%s:\n#\n", eargv[i]); + + while ((subdir = readdir(termdir)) != 0) { + size_t len = NAMLEN(subdir); + size_t cwd_len = len + strlen(eargv[i]) + 3; + char name_1[PATH_MAX]; + DIR *entrydir; + DIRENT *entry; + + cwd_buf = typeRealloc(char, cwd_len, cwd_buf); + if (cwd_buf == 0) + failed("realloc cwd_buf"); + + assert(cwd_buf != 0); + + strncpy(name_1, subdir->d_name, len)[len] = '\0'; + if (isDotname(name_1)) + continue; + + (void) sprintf(cwd_buf, "%s/%.*s/", eargv[i], (int) len, name_1); + if (chdir(cwd_buf) != 0) + continue; + + entrydir = opendir("."); + if (entrydir == 0) { + perror(cwd_buf); + continue; + } + while ((entry = readdir(entrydir)) != 0) { + char name_2[PATH_MAX]; + TERMTYPE lterm; + char *cn; + int status; + + len = NAMLEN(entry); + strncpy(name_2, entry->d_name, len)[len] = '\0'; + if (isDotname(name_2) || !_nc_is_file_path(name_2)) + continue; + + status = _nc_read_file_entry(name_2, <erm); + if (status <= 0) { + (void) fflush(stdout); + (void) fprintf(stderr, + "%s: couldn't open terminfo file %s.\n", + _nc_progname, name_2); + return (EXIT_FAILURE); + } + + /* only visit things once, by primary name */ + cn = _nc_first_name(lterm.term_names); + if (!strcmp(cn, name_2)) { + /* apply the selected hook function */ + (*hook) (cn, <erm); + } + _nc_free_termtype(<erm); + } + closedir(entrydir); + } + closedir(termdir); + if (cwd_buf != 0) + free(cwd_buf); + } +#if USE_HASHED_DB + else { + DB *capdbp; + char filename[PATH_MAX]; + + if (make_db_name(filename, eargv[i], sizeof(filename))) { + if ((capdbp = _nc_db_open(filename, FALSE)) != 0) { + DBT key, data; + int code; + + code = _nc_db_first(capdbp, &key, &data); + while (code == 0) { + TERMTYPE lterm; + int used; + char *have; + char *cn; + + if (_nc_db_have_data(&key, &data, &have, &used)) { + if (_nc_read_termtype(<erm, have, used) > 0) { + /* only visit things once, by primary name */ + cn = _nc_first_name(lterm.term_names); + /* apply the selected hook function */ + (*hook) (cn, <erm); + _nc_free_termtype(<erm); + } + } + code = _nc_db_next(capdbp, &key, &data); + } + + _nc_db_close(capdbp); + } + } + } +#endif +#endif +#if USE_TERMCAP +#if HAVE_BSD_CGETENT + char *db_array[2]; + char *buffer = 0; + + if (verbosity) + (void) printf("#\n#%s:\n#\n", eargv[i]); + + db_array[0] = eargv[i]; + db_array[1] = 0; + + if (cgetfirst(&buffer, db_array)) { + show_termcap(buffer, hook); + free(buffer); + while (cgetnext(&buffer, db_array)) { + show_termcap(buffer, hook); + free(buffer); + } + } + cgetclose(); +#else + /* scan termcap text-file only */ + if (_nc_is_file_path(eargv[i])) { + char buffer[2048]; + FILE *fp; + + if ((fp = fopen(eargv[i], "r")) != 0) { + while (fgets(buffer, sizeof(buffer), fp) != 0) { + if (*buffer == '#') + continue; + if (isspace(*buffer)) + continue; + show_termcap(buffer, hook); + } + fclose(fp); + } + } +#endif +#endif + } + + return (EXIT_SUCCESS); +} + +static void +usage(void) +{ + (void) fprintf(stderr, "usage: %s [-ahuUV] [-v n] [file...]\n", _nc_progname); + ExitProgram(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + bool all_dirs = FALSE; + bool direct_dependencies = FALSE; + bool invert_dependencies = FALSE; + bool header = FALSE; + char *report_file = 0; + unsigned i; + int code; + int this_opt, last_opt = '?'; + int v_opt = 0; + + _nc_progname = _nc_rootname(argv[0]); + + while ((this_opt = getopt(argc, argv, "0123456789ahu:vU:V")) != -1) { + /* handle optional parameter */ + if (isdigit(this_opt)) { + switch (last_opt) { + case 'v': + v_opt = (this_opt - '0'); + break; + default: + if (isdigit(last_opt)) + v_opt *= 10; + else + v_opt = 0; + v_opt += (this_opt - '0'); + last_opt = this_opt; + } + continue; + } + switch (this_opt) { + case 'a': + all_dirs = TRUE; + break; + case 'h': + header = TRUE; + break; + case 'u': + direct_dependencies = TRUE; + report_file = optarg; + break; + case 'v': + v_opt = 1; + break; + case 'U': + invert_dependencies = TRUE; + report_file = optarg; + break; + case 'V': + puts(curses_version()); + ExitProgram(EXIT_SUCCESS); + default: + usage(); + } + } + set_trace_level(v_opt); + + if (report_file != 0) { + if (freopen(report_file, "r", stdin) == 0) { + (void) fflush(stdout); + fprintf(stderr, "%s: can't open %s\n", _nc_progname, report_file); + ExitProgram(EXIT_FAILURE); + } + + /* parse entries out of the source file */ + _nc_set_source(report_file); + _nc_read_entry_source(stdin, 0, FALSE, FALSE, NULLHOOK); + } + + /* maybe we want a direct-dependency listing? */ + if (direct_dependencies) { + ENTRY *qp; + + for_entry_list(qp) { + if (qp->nuses) { + unsigned j; + + (void) printf("%s:", _nc_first_name(qp->tterm.term_names)); + for (j = 0; j < qp->nuses; j++) + (void) printf(" %s", qp->uses[j].name); + putchar('\n'); + } + } + + ExitProgram(EXIT_SUCCESS); + } + + /* maybe we want a reverse-dependency listing? */ + if (invert_dependencies) { + ENTRY *qp, *rp; + int matchcount; + + for_entry_list(qp) { + matchcount = 0; + for_entry_list(rp) { + if (rp->nuses == 0) + continue; + + for (i = 0; i < rp->nuses; i++) + if (_nc_name_match(qp->tterm.term_names, + rp->uses[i].name, "|")) { + if (matchcount++ == 0) + (void) printf("%s:", + _nc_first_name(qp->tterm.term_names)); + (void) printf(" %s", + _nc_first_name(rp->tterm.term_names)); + } + } + if (matchcount) + putchar('\n'); + } + + ExitProgram(EXIT_SUCCESS); + } + + /* + * If we get this far, user wants a simple terminal type listing. + */ + if (optind < argc) { + code = typelist(argc - optind, argv + optind, header, deschook); + } else if (all_dirs) { + DBDIRS state; + int offset; + int pass; + const char *path; + char **eargv = 0; + + code = EXIT_FAILURE; + for (pass = 0; pass < 2; ++pass) { + unsigned count = 0; + + _nc_first_db(&state, &offset); + while ((path = _nc_next_db(&state, &offset)) != 0) { + if (!is_database(path)) { + ; + } else if (eargv != 0) { + unsigned n; + int found = FALSE; + + /* eliminate duplicates */ + for (n = 0; n < count; ++n) { + if (!strcmp(path, eargv[n])) { + found = TRUE; + break; + } + } + if (!found) { + eargv[count] = strdup(path); + ++count; + } + } else { + ++count; + } + } + if (!pass) { + eargv = typeCalloc(char *, count + 1); + if (eargv == 0) + failed("realloc eargv"); + + assert(eargv != 0); + } else { + code = typelist((int) count, eargv, header, deschook); + while (count-- > 0) + free(eargv[count]); + free(eargv); + } + } + } else { + DBDIRS state; + int offset; + const char *path; + char *eargv[3]; + int count = 0; + + _nc_first_db(&state, &offset); + while ((path = _nc_next_db(&state, &offset)) != 0) { + if (is_database(path)) { + eargv[count++] = strdup(path); + break; + } + } + eargv[count] = 0; + + code = typelist(count, eargv, header, deschook); + + while (count-- > 0) + free(eargv[count]); + } + _nc_last_db(); + + ExitProgram(code); +} diff --git a/progs/tput.c b/progs/tput.c new file mode 100644 index 0000000..2e67cfe --- /dev/null +++ b/progs/tput.c @@ -0,0 +1,447 @@ +/**************************************************************************** + * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + ****************************************************************************/ + +/* + * tput.c -- shellscript access to terminal capabilities + * + * by Eric S. Raymond <esr@snark.thyrsus.com>, portions based on code from + * Ross Ridge's mytinfo package. + */ + +#define USE_LIBTINFO +#include <progs.priv.h> + +#if !PURE_TERMINFO +#include <dump_entry.h> +#include <termsort.c> +#endif +#include <transform.h> + +MODULE_ID("$Id: tput.c,v 1.46 2010/01/09 16:53:24 tom Exp $") + +#define PUTS(s) fputs(s, stdout) +#define PUTCHAR(c) putchar(c) +#define FLUSH fflush(stdout) + +typedef enum { + Numbers = 0 + ,Num_Str + ,Num_Str_Str +} TParams; + +static char *prg_name; +static bool is_init = FALSE; +static bool is_reset = FALSE; + +static void +quit(int status, const char *fmt,...) +{ + va_list argp; + + va_start(argp, fmt); + fprintf(stderr, "%s: ", prg_name); + vfprintf(stderr, fmt, argp); + fprintf(stderr, "\n"); + va_end(argp); + ExitProgram(status); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-V] [-S] [-T term] capname\n", prg_name); + ExitProgram(EXIT_FAILURE); +} + +static void +check_aliases(const char *name) +{ + is_init = same_program(name, PROG_INIT); + is_reset = same_program(name, PROG_RESET); +} + +/* + * Lookup the type of call we should make to tparm(). This ignores the actual + * terminfo capability (bad, because it is not extensible), but makes this + * code portable to platforms where sizeof(int) != sizeof(char *). + * + * FIXME: If we want extensibility, analyze the capability string as we do + * in tparm() to decide how to parse the varargs list. + */ +static TParams +tparm_type(const char *name) +{ +#define TD(code, longname, ti, tc) {code,longname},{code,ti},{code,tc} + TParams result = Numbers; + /* *INDENT-OFF* */ + static const struct { + TParams code; + const char *name; + } table[] = { + TD(Num_Str, "pkey_key", "pfkey", "pk"), + TD(Num_Str, "pkey_local", "pfloc", "pl"), + TD(Num_Str, "pkey_xmit", "pfx", "px"), + TD(Num_Str, "plab_norm", "pln", "pn"), + TD(Num_Str_Str, "pkey_plab", "pfxl", "xl"), + }; + /* *INDENT-ON* */ + + unsigned n; + for (n = 0; n < SIZEOF(table); n++) { + if (!strcmp(name, table[n].name)) { + result = table[n].code; + break; + } + } + return result; +} + +static int +exit_code(int token, int value) +{ + int result = 99; + + switch (token) { + case BOOLEAN: + result = !value; /* TRUE=0, FALSE=1 */ + break; + case NUMBER: + result = 0; /* always zero */ + break; + case STRING: + result = value; /* 0=normal, 1=missing */ + break; + } + return result; +} + +static int +tput(int argc, char *argv[]) +{ + NCURSES_CONST char *name; + char *s; + int i, j, c; + int status; + FILE *f; +#if !PURE_TERMINFO + bool termcap = FALSE; +#endif + + if ((name = argv[0]) == 0) + name = ""; + check_aliases(name); + if (is_reset || is_init) { + if (init_prog != 0) { + system(init_prog); + } + FLUSH; + + if (is_reset && reset_1string != 0) { + PUTS(reset_1string); + } else if (init_1string != 0) { + PUTS(init_1string); + } + FLUSH; + + if (is_reset && reset_2string != 0) { + PUTS(reset_2string); + } else if (init_2string != 0) { + PUTS(init_2string); + } + FLUSH; + +#ifdef set_lr_margin + if (set_lr_margin != 0) { + PUTS(TPARM_2(set_lr_margin, 0, columns - 1)); + } else +#endif +#ifdef set_left_margin_parm + if (set_left_margin_parm != 0 + && set_right_margin_parm != 0) { + PUTS(TPARM_1(set_left_margin_parm, 0)); + PUTS(TPARM_1(set_right_margin_parm, columns - 1)); + } else +#endif + if (clear_margins != 0 + && set_left_margin != 0 + && set_right_margin != 0) { + PUTS(clear_margins); + if (carriage_return != 0) { + PUTS(carriage_return); + } else { + PUTCHAR('\r'); + } + PUTS(set_left_margin); + if (parm_right_cursor) { + PUTS(TPARM_1(parm_right_cursor, columns - 1)); + } else { + for (i = 0; i < columns - 1; i++) { + PUTCHAR(' '); + } + } + PUTS(set_right_margin); + if (carriage_return != 0) { + PUTS(carriage_return); + } else { + PUTCHAR('\r'); + } + } + FLUSH; + + if (init_tabs != 8) { + if (clear_all_tabs != 0 && set_tab != 0) { + for (i = 0; i < columns - 1; i += 8) { + if (parm_right_cursor) { + PUTS(TPARM_1(parm_right_cursor, 8)); + } else { + for (j = 0; j < 8; j++) + PUTCHAR(' '); + } + PUTS(set_tab); + } + FLUSH; + } + } + + if (is_reset && reset_file != 0) { + f = fopen(reset_file, "r"); + if (f == 0) { + quit(4 + errno, "Can't open reset_file: '%s'", reset_file); + } + while ((c = fgetc(f)) != EOF) { + PUTCHAR(c); + } + fclose(f); + } else if (init_file != 0) { + f = fopen(init_file, "r"); + if (f == 0) { + quit(4 + errno, "Can't open init_file: '%s'", init_file); + } + while ((c = fgetc(f)) != EOF) { + PUTCHAR(c); + } + fclose(f); + } + FLUSH; + + if (is_reset && reset_3string != 0) { + PUTS(reset_3string); + } else if (init_3string != 0) { + PUTS(init_3string); + } + FLUSH; + return 0; + } + + if (strcmp(name, "longname") == 0) { + PUTS(longname()); + return 0; + } +#if !PURE_TERMINFO + retry: +#endif + if ((status = tigetflag(name)) != -1) { + return exit_code(BOOLEAN, status); + } else if ((status = tigetnum(name)) != CANCELLED_NUMERIC) { + (void) printf("%d\n", status); + return exit_code(NUMBER, 0); + } else if ((s = tigetstr(name)) == CANCELLED_STRING) { +#if !PURE_TERMINFO + if (!termcap) { + const struct name_table_entry *np; + + termcap = TRUE; + if ((np = _nc_find_entry(name, _nc_get_hash_table(termcap))) != 0) { + switch (np->nte_type) { + case BOOLEAN: + if (bool_from_termcap[np->nte_index]) + name = boolnames[np->nte_index]; + break; + + case NUMBER: + if (num_from_termcap[np->nte_index]) + name = numnames[np->nte_index]; + break; + + case STRING: + if (str_from_termcap[np->nte_index]) + name = strnames[np->nte_index]; + break; + } + goto retry; + } + } +#endif + quit(4, "unknown terminfo capability '%s'", name); + } else if (s != ABSENT_STRING) { + if (argc > 1) { + int k; + int popcount; + long numbers[1 + NUM_PARM]; + char *strings[1 + NUM_PARM]; + char *p_is_s[NUM_PARM]; + + /* Nasty hack time. The tparm function needs to see numeric + * parameters as numbers, not as pointers to their string + * representations + */ + + for (k = 1; k < argc; k++) { + char *tmp = 0; + strings[k] = argv[k]; + numbers[k] = strtol(argv[k], &tmp, 0); + if (tmp == 0 || *tmp != 0) + numbers[k] = 0; + } + for (k = argc; k <= NUM_PARM; k++) { + numbers[k] = 0; + strings[k] = 0; + } + + switch (tparm_type(name)) { + case Num_Str: + s = TPARM_2(s, numbers[1], strings[2]); + break; + case Num_Str_Str: + s = TPARM_3(s, numbers[1], strings[2], strings[3]); + break; + case Numbers: + default: + (void) _nc_tparm_analyze(s, p_is_s, &popcount); +#define myParam(n) (p_is_s[n - 1] != 0 ? ((long) strings[n]) : numbers[n]) + s = TPARM_9(s, + myParam(1), + myParam(2), + myParam(3), + myParam(4), + myParam(5), + myParam(6), + myParam(7), + myParam(8), + myParam(9)); + break; + } + } + + /* use putp() in order to perform padding */ + putp(s); + return exit_code(STRING, 0); + } + return exit_code(STRING, 1); +} + +int +main(int argc, char **argv) +{ + char *term; + int errret; + bool cmdline = TRUE; + int c; + char buf[BUFSIZ]; + int result = 0; + + check_aliases(prg_name = _nc_rootname(argv[0])); + + term = getenv("TERM"); + + while ((c = getopt(argc, argv, "ST:V")) != -1) { + switch (c) { + case 'S': + cmdline = FALSE; + break; + case 'T': + use_env(FALSE); + term = optarg; + break; + case 'V': + puts(curses_version()); + ExitProgram(EXIT_SUCCESS); + default: + usage(); + /* NOTREACHED */ + } + } + + /* + * Modify the argument list to omit the options we processed. + */ + if (is_reset || is_init) { + if (optind-- < argc) { + argc -= optind; + argv += optind; + } + argv[0] = prg_name; + } else { + argc -= optind; + argv += optind; + } + + if (term == 0 || *term == '\0') + quit(2, "No value for $TERM and no -T specified"); + + if (setupterm(term, STDOUT_FILENO, &errret) != OK && errret <= 0) + quit(3, "unknown terminal \"%s\"", term); + + if (cmdline) { + if ((argc <= 0) && !is_reset && !is_init) + usage(); + ExitProgram(tput(argc, argv)); + } + + while (fgets(buf, sizeof(buf), stdin) != 0) { + char *argvec[16]; /* command, 9 parms, null, & slop */ + int argnum = 0; + char *cp; + + /* crack the argument list into a dope vector */ + for (cp = buf; *cp; cp++) { + if (isspace(UChar(*cp))) { + *cp = '\0'; + } else if (cp == buf || cp[-1] == 0) { + argvec[argnum++] = cp; + if (argnum >= (int) SIZEOF(argvec) - 1) + break; + } + } + argvec[argnum] = 0; + + if (argnum != 0 + && tput(argnum, argvec) != 0) { + if (result == 0) + result = 4; /* will return value >4 */ + ++result; + } + } + + ExitProgram(result); +} diff --git a/progs/transform.c b/progs/transform.c new file mode 100644 index 0000000..75f4573 --- /dev/null +++ b/progs/transform.c @@ -0,0 +1,79 @@ +/**************************************************************************** + * Copyright (c) 2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Thomas E. Dickey * + ****************************************************************************/ +#include <progs.priv.h> +#include <string.h> + +#include <transform.h> + +MODULE_ID("$Id: transform.c,v 1.2 2010/09/04 21:16:17 tom Exp $") + +#ifdef SUFFIX_IGNORED +static void +trim_suffix(const char *a, unsigned *len) +{ + const char ignore[] = SUFFIX_IGNORED; + + if (sizeof(ignore) != 0) { + bool trim = FALSE; + unsigned need = (sizeof(ignore) - 1); + + if (*len > need) { + unsigned first = *len - need; + unsigned n; + trim = TRUE; + for (n = first; n < *len; ++n) { + if (tolower(UChar(a[n])) != tolower(UChar(ignore[n - first]))) { + trim = FALSE; + break; + } + } + if (trim) { + *len -= need; + } + } + } +} +#else +#define trim_suffix(a, len) /* nothing */ +#endif + +bool +same_program(const char *a, const char *b) +{ + unsigned len_a = strlen(a); + unsigned len_b = strlen(b); + + trim_suffix(a, &len_a); + trim_suffix(b, &len_b); + + return (len_a == len_b) && (strncmp(a, b, len_a) == 0); +} diff --git a/progs/tset.c b/progs/tset.c new file mode 100644 index 0000000..084e41d --- /dev/null +++ b/progs/tset.c @@ -0,0 +1,1349 @@ +/**************************************************************************** + * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * + * and: Eric S. Raymond <esr@snark.thyrsus.com> * + * and: Thomas E. Dickey 1996-on * + ****************************************************************************/ + +/* + * Notes: + * The initial adaptation from 4.4BSD Lite sources in September 1995 used 686 + * lines from that version, and made changes/additions for 150 lines. There + * was no reformatting, so with/without ignoring whitespace, the amount of + * change is the same. + * + * Comparing with current (2009) source, excluding this comment: + * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines + * changed/added. + * a) Ignoring whitespace, the current version still uses 516 lines from the + * 4.4BSD Lite sources, with 402 lines changed/added. + * + * Raymond's original comment on this follows... + */ + +/* + * tset.c - terminal initialization utility + * + * This code was mostly swiped from 4.4BSD tset, with some obsolescent + * cruft removed and substantial portions rewritten. A Regents of the + * University of California copyright applies to some portions of the + * code, and is reproduced below: + */ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define USE_LIBTINFO +#define __INTERNAL_CAPS_VISIBLE /* we need to see has_hardware_tabs */ +#include <progs.priv.h> + +#include <errno.h> +#include <stdio.h> +#include <termcap.h> +#include <fcntl.h> + +#if HAVE_GETTTYNAM && HAVE_TTYENT_H +#include <ttyent.h> +#endif +#ifdef NeXT +char *ttyname(int fd); +#endif + +#if HAVE_SIZECHANGE +# if !defined(sun) || !TERMIOS +# if HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif +# endif +#endif + +#if NEED_PTEM_H +/* they neglected to define struct winsize in termios.h -- it's only + in termio.h */ +#include <sys/stream.h> +#include <sys/ptem.h> +#endif + +#include <dump_entry.h> +#include <transform.h> + +MODULE_ID("$Id: tset.c,v 1.82 2010/05/01 21:42:46 tom Exp $") + +/* + * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS, + * Solaris, IRIX) define TIOCGWINSZ and struct winsize. + */ +#ifdef TIOCGSIZE +# define IOCTL_GET_WINSIZE TIOCGSIZE +# define IOCTL_SET_WINSIZE TIOCSSIZE +# define STRUCT_WINSIZE struct ttysize +# define WINSIZE_ROWS(n) n.ts_lines +# define WINSIZE_COLS(n) n.ts_cols +#else +# ifdef TIOCGWINSZ +# define IOCTL_GET_WINSIZE TIOCGWINSZ +# define IOCTL_SET_WINSIZE TIOCSWINSZ +# define STRUCT_WINSIZE struct winsize +# define WINSIZE_ROWS(n) n.ws_row +# define WINSIZE_COLS(n) n.ws_col +# endif +#endif + +#ifndef environ +extern char **environ; +#endif + +#undef CTRL +#define CTRL(x) ((x) & 0x1f) + +const char *_nc_progname = "tset"; + +static TTY mode, oldmode, original; + +static bool opt_c; /* set control-chars */ +static bool opt_w; /* set window-size */ + +static bool can_restore = FALSE; +static bool isreset = FALSE; /* invoked as reset */ +static int terasechar = -1; /* new erase character */ +static int intrchar = -1; /* new interrupt character */ +static int tkillchar = -1; /* new kill character */ +static int tlines, tcolumns; /* window size */ + +#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c)) + +static int +CaselessCmp(const char *a, const char *b) +{ /* strcasecmp isn't portable */ + while (*a && *b) { + int cmp = LOWERCASE(*a) - LOWERCASE(*b); + if (cmp != 0) + break; + a++, b++; + } + return LOWERCASE(*a) - LOWERCASE(*b); +} + +static void +exit_error(void) +{ + if (can_restore) + SET_TTY(STDERR_FILENO, &original); + (void) fprintf(stderr, "\n"); + fflush(stderr); + ExitProgram(EXIT_FAILURE); + /* NOTREACHED */ +} + +static void +err(const char *fmt,...) +{ + va_list ap; + va_start(ap, fmt); + (void) fprintf(stderr, "%s: ", _nc_progname); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + exit_error(); + /* NOTREACHED */ +} + +static void +failed(const char *msg) +{ + char temp[BUFSIZ]; + unsigned len = strlen(_nc_progname) + 2; + + if ((int) len < (int) sizeof(temp) - 12) { + strcpy(temp, _nc_progname); + strcat(temp, ": "); + } else { + strcpy(temp, "tset: "); + } + perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2)); + exit_error(); + /* NOTREACHED */ +} + +static void +cat(char *file) +{ + FILE *fp; + size_t nr; + char buf[BUFSIZ]; + + if ((fp = fopen(file, "r")) == 0) + failed(file); + + while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) + if (fwrite(buf, sizeof(char), nr, stderr) != nr) + failed("write to stderr"); + fclose(fp); +} + +static int +outc(int c) +{ + return putc(c, stderr); +} + +/* Prompt the user for a terminal type. */ +static const char * +askuser(const char *dflt) +{ + static char answer[256]; + char *p; + + /* We can get recalled; if so, don't continue uselessly. */ + clearerr(stdin); + if (feof(stdin) || ferror(stdin)) { + (void) fprintf(stderr, "\n"); + exit_error(); + /* NOTREACHED */ + } + for (;;) { + if (dflt) + (void) fprintf(stderr, "Terminal type? [%s] ", dflt); + else + (void) fprintf(stderr, "Terminal type? "); + (void) fflush(stderr); + + if (fgets(answer, sizeof(answer), stdin) == 0) { + if (dflt == 0) { + exit_error(); + /* NOTREACHED */ + } + return (dflt); + } + + if ((p = strchr(answer, '\n')) != 0) + *p = '\0'; + if (answer[0]) + return (answer); + if (dflt != 0) + return (dflt); + } +} + +/************************************************************************** + * + * Mapping logic begins here + * + **************************************************************************/ + +/* Baud rate conditionals for mapping. */ +#define GT 0x01 +#define EQ 0x02 +#define LT 0x04 +#define NOT 0x08 +#define GE (GT | EQ) +#define LE (LT | EQ) + +typedef struct map { + struct map *next; /* Linked list of maps. */ + const char *porttype; /* Port type, or "" for any. */ + const char *type; /* Terminal type to select. */ + int conditional; /* Baud rate conditionals bitmask. */ + int speed; /* Baud rate to compare against. */ +} MAP; + +static MAP *cur, *maplist; + +typedef struct speeds { + const char *string; + int speed; +} SPEEDS; + +static const SPEEDS speeds[] = +{ + {"0", B0}, + {"50", B50}, + {"75", B75}, + {"110", B110}, + {"134", B134}, + {"134.5", B134}, + {"150", B150}, + {"200", B200}, + {"300", B300}, + {"600", B600}, + {"1200", B1200}, + {"1800", B1800}, + {"2400", B2400}, + {"4800", B4800}, + {"9600", B9600}, + /* sgttyb may define up to this point */ +#ifdef B19200 + {"19200", B19200}, +#endif +#ifdef B38400 + {"38400", B38400}, +#endif +#ifdef B19200 + {"19200", B19200}, +#endif +#ifdef B38400 + {"38400", B38400}, +#endif +#ifdef B19200 + {"19200", B19200}, +#else +#ifdef EXTA + {"19200", EXTA}, +#endif +#endif +#ifdef B38400 + {"38400", B38400}, +#else +#ifdef EXTB + {"38400", EXTB}, +#endif +#endif +#ifdef B57600 + {"57600", B57600}, +#endif +#ifdef B115200 + {"115200", B115200}, +#endif +#ifdef B230400 + {"230400", B230400}, +#endif +#ifdef B460800 + {"460800", B460800}, +#endif + {(char *) 0, 0} +}; + +static int +tbaudrate(char *rate) +{ + const SPEEDS *sp; + int found = FALSE; + + /* The baudrate number can be preceded by a 'B', which is ignored. */ + if (*rate == 'B') + ++rate; + + for (sp = speeds; sp->string; ++sp) { + if (!CaselessCmp(rate, sp->string)) { + found = TRUE; + break; + } + } + if (!found) + err("unknown baud rate %s", rate); + return (sp->speed); +} + +/* + * Syntax for -m: + * [port-type][test baudrate]:terminal-type + * The baud rate tests are: >, <, @, =, ! + */ +static void +add_mapping(const char *port, char *arg) +{ + MAP *mapp; + char *copy, *p; + const char *termp; + char *base = 0; + + copy = strdup(arg); + mapp = typeMalloc(MAP, 1); + if (copy == 0 || mapp == 0) + failed("malloc"); + + assert(copy != 0); + assert(mapp != 0); + + mapp->next = 0; + if (maplist == 0) + cur = maplist = mapp; + else { + cur->next = mapp; + cur = mapp; + } + + mapp->porttype = arg; + mapp->conditional = 0; + + arg = strpbrk(arg, "><@=!:"); + + if (arg == 0) { /* [?]term */ + mapp->type = mapp->porttype; + mapp->porttype = 0; + goto done; + } + + if (arg == mapp->porttype) /* [><@=! baud]:term */ + termp = mapp->porttype = 0; + else + termp = base = arg; + + for (;; ++arg) { /* Optional conditionals. */ + switch (*arg) { + case '<': + if (mapp->conditional & GT) + goto badmopt; + mapp->conditional |= LT; + break; + case '>': + if (mapp->conditional & LT) + goto badmopt; + mapp->conditional |= GT; + break; + case '@': + case '=': /* Not documented. */ + mapp->conditional |= EQ; + break; + case '!': + mapp->conditional |= NOT; + break; + default: + goto next; + } + } + + next: + if (*arg == ':') { + if (mapp->conditional) + goto badmopt; + ++arg; + } else { /* Optional baudrate. */ + arg = strchr(p = arg, ':'); + if (arg == 0) + goto badmopt; + *arg++ = '\0'; + mapp->speed = tbaudrate(p); + } + + if (arg == (char *) 0) /* Non-optional type. */ + goto badmopt; + + mapp->type = arg; + + /* Terminate porttype, if specified. */ + if (termp != 0) + *base = '\0'; + + /* If a NOT conditional, reverse the test. */ + if (mapp->conditional & NOT) + mapp->conditional = ~mapp->conditional & (EQ | GT | LT); + + /* If user specified a port with an option flag, set it. */ + done: + if (port) { + if (mapp->porttype) { + badmopt: + err("illegal -m option format: %s", copy); + } + mapp->porttype = port; + } + free(copy); +#ifdef MAPDEBUG + (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY"); + (void) printf("type: %s\n", mapp->type); + (void) printf("conditional: "); + p = ""; + if (mapp->conditional & GT) { + (void) printf("GT"); + p = "/"; + } + if (mapp->conditional & EQ) { + (void) printf("%sEQ", p); + p = "/"; + } + if (mapp->conditional & LT) + (void) printf("%sLT", p); + (void) printf("\nspeed: %d\n", mapp->speed); +#endif +} + +/* + * Return the type of terminal to use for a port of type 'type', as specified + * by the first applicable mapping in 'map'. If no mappings apply, return + * 'type'. + */ +static const char * +mapped(const char *type) +{ + MAP *mapp; + int match; + + for (mapp = maplist; mapp; mapp = mapp->next) + if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) { + switch (mapp->conditional) { + case 0: /* No test specified. */ + match = TRUE; + break; + case EQ: + match = (ospeed == mapp->speed); + break; + case GE: + match = (ospeed >= mapp->speed); + break; + case GT: + match = (ospeed > mapp->speed); + break; + case LE: + match = (ospeed <= mapp->speed); + break; + case LT: + match = (ospeed < mapp->speed); + break; + default: + match = FALSE; + } + if (match) + return (mapp->type); + } + /* No match found; return given type. */ + return (type); +} + +/************************************************************************** + * + * Entry fetching + * + **************************************************************************/ + +/* + * Figure out what kind of terminal we're dealing with, and then read in + * its termcap entry. + */ +static const char * +get_termcap_entry(char *userarg) +{ + int errret; + char *p; + const char *ttype; +#if HAVE_GETTTYNAM + struct ttyent *t; +#else + FILE *fp; +#endif + char *ttypath; + + if (userarg) { + ttype = userarg; + goto found; + } + + /* Try the environment. */ + if ((ttype = getenv("TERM")) != 0) + goto map; + + if ((ttypath = ttyname(STDERR_FILENO)) != 0) { + p = _nc_basename(ttypath); +#if HAVE_GETTTYNAM + /* + * We have the 4.3BSD library call getttynam(3); that means + * there's an /etc/ttys to look up device-to-type mappings in. + * Try ttyname(3); check for dialup or other mapping. + */ + if ((t = getttynam(p))) { + ttype = t->ty_type; + goto map; + } +#else + if ((fp = fopen("/etc/ttytype", "r")) != 0 + || (fp = fopen("/etc/ttys", "r")) != 0) { + char buffer[BUFSIZ]; + char *s, *t, *d; + + while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) { + for (s = buffer, t = d = 0; *s; s++) { + if (isspace(UChar(*s))) + *s = '\0'; + else if (t == 0) + t = s; + else if (d == 0 && s != buffer && s[-1] == '\0') + d = s; + } + if (t != 0 && d != 0 && !strcmp(d, p)) { + ttype = strdup(t); + fclose(fp); + goto map; + } + } + fclose(fp); + } +#endif /* HAVE_GETTTYNAM */ + } + + /* If still undefined, use "unknown". */ + ttype = "unknown"; + + map:ttype = mapped(ttype); + + /* + * If not a path, remove TERMCAP from the environment so we get a + * real entry from /etc/termcap. This prevents us from being fooled + * by out of date stuff in the environment. + */ + found:if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) { + /* 'unsetenv("TERMCAP")' is not portable. + * The 'environ' array is better. + */ + int n; + for (n = 0; environ[n] != 0; n++) { + if (!strncmp("TERMCAP=", environ[n], 8)) { + while ((environ[n] = environ[n + 1]) != 0) { + n++; + } + break; + } + } + } + + /* + * ttype now contains a pointer to the type of the terminal. + * If the first character is '?', ask the user. + */ + if (ttype[0] == '?') { + if (ttype[1] != '\0') + ttype = askuser(ttype + 1); + else + ttype = askuser(0); + } + /* Find the terminfo entry. If it doesn't exist, ask the user. */ + while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret) + != OK) { + if (errret == 0) { + (void) fprintf(stderr, "%s: unknown terminal type %s\n", + _nc_progname, ttype); + ttype = 0; + } else { + (void) fprintf(stderr, + "%s: can't initialize terminal type %s (error %d)\n", + _nc_progname, ttype, errret); + ttype = 0; + } + ttype = askuser(ttype); + } +#if BROKEN_LINKER + tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */ +#endif + return (ttype); +} + +/************************************************************************** + * + * Mode-setting logic + * + **************************************************************************/ + +/* some BSD systems have these built in, some systems are missing + * one or more definitions. The safest solution is to override unless the + * commonly-altered ones are defined. + */ +#if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT)) +#undef CEOF +#undef CERASE +#undef CINTR +#undef CKILL +#undef CLNEXT +#undef CRPRNT +#undef CQUIT +#undef CSTART +#undef CSTOP +#undef CSUSP +#endif + +/* control-character defaults */ +#ifndef CEOF +#define CEOF CTRL('D') +#endif +#ifndef CERASE +#define CERASE CTRL('H') +#endif +#ifndef CINTR +#define CINTR 127 /* ^? */ +#endif +#ifndef CKILL +#define CKILL CTRL('U') +#endif +#ifndef CLNEXT +#define CLNEXT CTRL('v') +#endif +#ifndef CRPRNT +#define CRPRNT CTRL('r') +#endif +#ifndef CQUIT +#define CQUIT CTRL('\\') +#endif +#ifndef CSTART +#define CSTART CTRL('Q') +#endif +#ifndef CSTOP +#define CSTOP CTRL('S') +#endif +#ifndef CSUSP +#define CSUSP CTRL('Z') +#endif + +#if defined(_POSIX_VDISABLE) +#define DISABLED(val) (((_POSIX_VDISABLE != -1) \ + && ((val) == _POSIX_VDISABLE)) \ + || ((val) <= 0)) +#else +#define DISABLED(val) ((int)(val) <= 0) +#endif + +#define CHK(val, dft) (DISABLED(val) ? dft : val) + +static bool set_tabs(void); + +/* + * Reset the terminal mode bits to a sensible state. Very useful after + * a child program dies in raw mode. + */ +static void +reset_mode(void) +{ +#ifdef TERMIOS + tcgetattr(STDERR_FILENO, &mode); +#else + stty(STDERR_FILENO, &mode); +#endif + +#ifdef TERMIOS +#if defined(VDISCARD) && defined(CDISCARD) + mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD); +#endif + mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF); + mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE); +#if defined(VFLUSH) && defined(CFLUSH) + mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH); +#endif + mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR); + mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL); +#if defined(VLNEXT) && defined(CLNEXT) + mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT); +#endif + mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT); +#if defined(VREPRINT) && defined(CRPRNT) + mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT); +#endif +#if defined(VSTART) && defined(CSTART) + mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART); +#endif +#if defined(VSTOP) && defined(CSTOP) + mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP); +#endif +#if defined(VSUSP) && defined(CSUSP) + mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP); +#endif +#if defined(VWERASE) && defined(CWERASE) + mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE); +#endif + + mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR +#ifdef IUCLC + | IUCLC +#endif +#ifdef IXANY + | IXANY +#endif + | IXOFF); + + mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON +#ifdef IMAXBEL + | IMAXBEL +#endif + ); + + mode.c_oflag &= ~(0 +#ifdef OLCUC + | OLCUC +#endif +#ifdef OCRNL + | OCRNL +#endif +#ifdef ONOCR + | ONOCR +#endif +#ifdef ONLRET + | ONLRET +#endif +#ifdef OFILL + | OFILL +#endif +#ifdef OFDEL + | OFDEL +#endif +#ifdef NLDLY + | NLDLY +#endif +#ifdef CRDLY + | CRDLY +#endif +#ifdef TABDLY + | TABDLY +#endif +#ifdef BSDLY + | BSDLY +#endif +#ifdef VTDLY + | VTDLY +#endif +#ifdef FFDLY + | FFDLY +#endif + ); + + mode.c_oflag |= (OPOST +#ifdef ONLCR + | ONLCR +#endif + ); + + mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL); + mode.c_cflag |= (CS8 | CREAD); + mode.c_lflag &= ~(ECHONL | NOFLSH +#ifdef TOSTOP + | TOSTOP +#endif +#ifdef ECHOPTR + | ECHOPRT +#endif +#ifdef XCASE + | XCASE +#endif + ); + + mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK +#ifdef ECHOCTL + | ECHOCTL +#endif +#ifdef ECHOKE + | ECHOKE +#endif + ); +#endif + + SET_TTY(STDERR_FILENO, &mode); +} + +/* + * Returns a "good" value for the erase character. This is loosely based on + * the BSD4.4 logic. + */ +#ifdef TERMIOS +static int +default_erase(void) +{ + int result; + + if (over_strike + && key_backspace != 0 + && strlen(key_backspace) == 1) + result = key_backspace[0]; + else + result = CERASE; + + return result; +} +#endif + +/* + * Update the values of the erase, interrupt, and kill characters in 'mode'. + * + * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase + * characters if they're unset, or if we specify them as options. This differs + * from BSD 4.4 tset, which always sets erase. + */ +static void +set_control_chars(void) +{ +#ifdef TERMIOS + if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0) + mode.c_cc[VERASE] = (terasechar >= 0) ? terasechar : default_erase(); + + if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0) + mode.c_cc[VINTR] = (intrchar >= 0) ? intrchar : CINTR; + + if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0) + mode.c_cc[VKILL] = (tkillchar >= 0) ? tkillchar : CKILL; +#endif +} + +/* + * Set up various conversions in 'mode', including parity, tabs, returns, + * echo, and case, according to the termcap entry. If the program we're + * running was named with a leading upper-case character, map external + * uppercase to internal lowercase. + */ +static void +set_conversions(void) +{ +#ifdef __OBSOLETE__ + /* + * Conversion logic for some *really* ancient terminal glitches, + * not supported in terminfo. Left here for succeeding generations + * to marvel at. + */ + if (tgetflag("UC")) { +#ifdef IUCLC + mode.c_iflag |= IUCLC; + mode.c_oflag |= OLCUC; +#endif + } else if (tgetflag("LC")) { +#ifdef IUCLC + mode.c_iflag &= ~IUCLC; + mode.c_oflag &= ~OLCUC; +#endif + } + mode.c_iflag &= ~(PARMRK | INPCK); + mode.c_lflag |= ICANON; + if (tgetflag("EP")) { + mode.c_cflag |= PARENB; + mode.c_cflag &= ~PARODD; + } + if (tgetflag("OP")) { + mode.c_cflag |= PARENB; + mode.c_cflag |= PARODD; + } +#endif /* __OBSOLETE__ */ + +#ifdef TERMIOS +#ifdef ONLCR + mode.c_oflag |= ONLCR; +#endif + mode.c_iflag |= ICRNL; + mode.c_lflag |= ECHO; +#ifdef OXTABS + mode.c_oflag |= OXTABS; +#endif /* OXTABS */ + + /* test used to be tgetflag("NL") */ + if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) { + /* Newline, not linefeed. */ +#ifdef ONLCR + mode.c_oflag &= ~ONLCR; +#endif + mode.c_iflag &= ~ICRNL; + } +#ifdef __OBSOLETE__ + if (tgetflag("HD")) /* Half duplex. */ + mode.c_lflag &= ~ECHO; +#endif /* __OBSOLETE__ */ +#ifdef OXTABS + /* test used to be tgetflag("pt") */ + if (has_hardware_tabs) /* Print tabs. */ + mode.c_oflag &= ~OXTABS; +#endif /* OXTABS */ + mode.c_lflag |= (ECHOE | ECHOK); +#endif +} + +/* Output startup string. */ +static void +set_init(void) +{ + char *p; + bool settle; + +#ifdef __OBSOLETE__ + if (pad_char != (char *) 0) /* Get/set pad character. */ + PC = pad_char[0]; +#endif /* OBSOLETE */ + +#ifdef TAB3 + if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { + oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); + SET_TTY(STDERR_FILENO, &oldmode); + } +#endif + settle = set_tabs(); + + if (isreset) { + if ((p = reset_1string) != 0) { + tputs(p, 0, outc); + settle = TRUE; + } + if ((p = reset_2string) != 0) { + tputs(p, 0, outc); + settle = TRUE; + } + /* What about rf, rs3, as per terminfo man page? */ + /* also might be nice to send rmacs, rmul, rmm */ + if ((p = reset_file) != 0 + || (p = init_file) != 0) { + cat(p); + settle = TRUE; + } + } + + if (settle) { + (void) putc('\r', stderr); + (void) fflush(stderr); + (void) napms(1000); /* Settle the terminal. */ + } +} + +/* + * Set the hardware tabs on the terminal, using the ct (clear all tabs), + * st (set one tab) and ch (horizontal cursor addressing) capabilities. + * This is done before if and is, so they can patch in case we blow this. + * Return TRUE if we set any tab stops, FALSE if not. + */ +static bool +set_tabs(void) +{ + if (set_tab && clear_all_tabs) { + int c; + + (void) putc('\r', stderr); /* Force to left margin. */ + tputs(clear_all_tabs, 0, outc); + + for (c = 8; c < tcolumns; c += 8) { + /* Get to the right column. In BSD tset, this + * used to try a bunch of half-clever things + * with cup and hpa, for an average saving of + * somewhat less than two character times per + * tab stop, less than .01 sec at 2400cps. We + * lost all this cruft because it seemed to be + * introducing some odd bugs. + * -----------12345678----------- */ + (void) fputs(" ", stderr); + tputs(set_tab, 0, outc); + } + putc('\r', stderr); + return (TRUE); + } + return (FALSE); +} + +/************************************************************************** + * + * Main sequence + * + **************************************************************************/ + +/* + * Tell the user if a control key has been changed from the default value. + */ +#ifdef TERMIOS +static void +report(const char *name, int which, unsigned def) +{ + unsigned older, newer; + char *p; + + newer = mode.c_cc[which]; + older = oldmode.c_cc[which]; + + if (older == newer && older == def) + return; + + (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); + + if (DISABLED(newer)) + (void) fprintf(stderr, "undef.\n"); + /* + * Check 'delete' before 'backspace', since the key_backspace value + * is ambiguous. + */ + else if (newer == 0177) + (void) fprintf(stderr, "delete.\n"); + else if ((p = key_backspace) != 0 + && newer == (unsigned char) p[0] + && p[1] == '\0') + (void) fprintf(stderr, "backspace.\n"); + else if (newer < 040) { + newer ^= 0100; + (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer)); + } else + (void) fprintf(stderr, "%c.\n", UChar(newer)); +} +#endif + +/* + * Convert the obsolete argument forms into something that getopt can handle. + * This means that -e, -i and -k get default arguments supplied for them. + */ +static void +obsolete(char **argv) +{ + for (; *argv; ++argv) { + char *parm = argv[0]; + + if (parm[0] == '-' && parm[1] == '\0') { + argv[0] = strdup("-q"); + continue; + } + + if ((parm[0] != '-') + || (argv[1] && argv[1][0] != '-') + || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k') + || (parm[2] != '\0')) + continue; + switch (argv[0][1]) { + case 'e': + argv[0] = strdup("-e^H"); + break; + case 'i': + argv[0] = strdup("-i^C"); + break; + case 'k': + argv[0] = strdup("-k^U"); + break; + } + } +} + +static void +usage(void) +{ + static const char *tbl[] = + { + "" + ,"Options:" + ," -c set control characters" + ," -e ch erase character" + ," -I no initialization strings" + ," -i ch interrupt character" + ," -k ch kill character" + ," -m mapping map identifier to type" + ," -Q do not output control key settings" + ," -r display term on stderr" + ," -s output TERM set command" + ," -V print curses-version" + ," -w set window-size" + }; + unsigned n; + (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname); + for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n) + fprintf(stderr, "%s\n", tbl[n]); + exit_error(); + /* NOTREACHED */ +} + +static char +arg_to_char(void) +{ + return (char) ((optarg[0] == '^' && optarg[1] != '\0') + ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1])) + : optarg[0]); +} + +int +main(int argc, char **argv) +{ + int ch, noinit, noset, quiet, Sflag, sflag, showterm; + const char *p; + const char *ttype; + + obsolete(argv); + noinit = noset = quiet = Sflag = sflag = showterm = 0; + while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -1) { + switch (ch) { + case 'c': /* set control-chars */ + opt_c = TRUE; + break; + case 'a': /* OBSOLETE: map identifier to type */ + add_mapping("arpanet", optarg); + break; + case 'd': /* OBSOLETE: map identifier to type */ + add_mapping("dialup", optarg); + break; + case 'e': /* erase character */ + terasechar = arg_to_char(); + break; + case 'I': /* no initialization strings */ + noinit = 1; + break; + case 'i': /* interrupt character */ + intrchar = arg_to_char(); + break; + case 'k': /* kill character */ + tkillchar = arg_to_char(); + break; + case 'm': /* map identifier to type */ + add_mapping(0, optarg); + break; + case 'n': /* OBSOLETE: set new tty driver */ + break; + case 'p': /* OBSOLETE: map identifier to type */ + add_mapping("plugboard", optarg); + break; + case 'Q': /* don't output control key settings */ + quiet = 1; + break; + case 'q': /* display term only */ + noset = 1; + break; + case 'r': /* display term on stderr */ + showterm = 1; + break; + case 'S': /* OBSOLETE: output TERM & TERMCAP */ + Sflag = 1; + break; + case 's': /* output TERM set command */ + sflag = 1; + break; + case 'V': /* print curses-version */ + puts(curses_version()); + ExitProgram(EXIT_SUCCESS); + case 'w': /* set window-size */ + opt_w = TRUE; + break; + case '?': + default: + usage(); + } + } + + _nc_progname = _nc_rootname(*argv); + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + if (!opt_c && !opt_w) + opt_c = opt_w = TRUE; + + if (GET_TTY(STDERR_FILENO, &mode) < 0) + failed("standard error"); + can_restore = TRUE; + original = oldmode = mode; +#ifdef TERMIOS + ospeed = (NCURSES_OSPEED) cfgetospeed(&mode); +#else + ospeed = (NCURSES_OSPEED) mode.sg_ospeed; +#endif + + if (same_program(_nc_progname, PROG_RESET)) { + isreset = TRUE; + reset_mode(); + } + + (void) get_termcap_entry(*argv); + + if (!noset) { + tcolumns = columns; + tlines = lines; + +#if HAVE_SIZECHANGE + if (opt_w) { + STRUCT_WINSIZE win; + /* Set window size if not set already */ + (void) ioctl(STDERR_FILENO, IOCTL_GET_WINSIZE, &win); + if (WINSIZE_ROWS(win) == 0 && + WINSIZE_COLS(win) == 0 && + tlines > 0 && tcolumns > 0) { + WINSIZE_ROWS(win) = tlines; + WINSIZE_COLS(win) = tcolumns; + (void) ioctl(STDERR_FILENO, IOCTL_SET_WINSIZE, &win); + } + } +#endif + if (opt_c) { + set_control_chars(); + set_conversions(); + + if (!noinit) + set_init(); + + /* Set the modes if they've changed. */ + if (memcmp(&mode, &oldmode, sizeof(mode))) { + SET_TTY(STDERR_FILENO, &mode); + } + } + } + + /* Get the terminal name from the entry. */ + ttype = _nc_first_name(cur_term->type.term_names); + + if (noset) + (void) printf("%s\n", ttype); + else { + if (showterm) + (void) fprintf(stderr, "Terminal type is %s.\n", ttype); + /* + * If erase, kill and interrupt characters could have been + * modified and not -Q, display the changes. + */ +#ifdef TERMIOS + if (!quiet) { + report("Erase", VERASE, CERASE); + report("Kill", VKILL, CKILL); + report("Interrupt", VINTR, CINTR); + } +#endif + } + + if (Sflag) + err("The -S option is not supported under terminfo."); + + if (sflag) { + int len; + char *var; + char *leaf; + /* + * Figure out what shell we're using. A hack, we look for an + * environmental variable SHELL ending in "csh". + */ + if ((var = getenv("SHELL")) != 0 + && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3) + && !strcmp(leaf + len - 3, "csh")) + p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n"; + else + p = "TERM=%s;\n"; + (void) printf(p, ttype); + } + + ExitProgram(EXIT_SUCCESS); +} |