commit 5eb9195c4dcb8c80be65ec008831f43d41e2e391
Author: conrad <conrad@9c49b5d1-7df3-0310-bce2-b7278e68f44c>
Date: Wed, 19 Sep 2007 00:24:07 +0000
import of 0.9.5 sources
git-svn-id: http://svn.kfish.org/xsel/trunk@188 9c49b5d1-7df3-0310-bce2-b7278e68f44c
Diffstat:
A | Imakefile | | | 10 | ++++++++++ |
A | Makefile | | | 757 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | xsel.1x | | | 134 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | xsel.c | | | 1954 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | xsel.h | | | 99 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 2954 insertions(+), 0 deletions(-)
diff --git a/Imakefile b/Imakefile
@@ -0,0 +1,10 @@
+ INCLUDES = -I$(TOP) -I$(TOP)/X11
+LOCAL_LIBRARIES = $(XLIB)
+ SRCS = xsel.c
+ OBJS = xsel.o
+ CFLAGS += -Wall
+
+ComplexProgramTarget(xsel)
+
+shar:
+ shar Imakefile xsel.c xsel.h xsel.1x >xsel.shar
diff --git a/Makefile b/Makefile
@@ -0,0 +1,757 @@
+# Makefile generated by imake - do not edit!
+# $TOG: imake.c /main/97 1997/06/20 20:23:51 kaleb $
+
+# ----------------------------------------------------------------------
+# Makefile generated from "Imake.tmpl" and <Imakefile>
+# $TOG: Imake.tmpl /main/245 1997/05/20 10:05:47 kaleb $
+#
+#
+#
+#
+# $XFree86: xc/config/cf/Imake.tmpl,v 3.32.2.12 1999/01/09 14:18:57 dawes Exp $
+# ----------------------------------------------------------------------
+
+all::
+
+.SUFFIXES: .i
+
+# $TOG: Imake.cf /main/28 1997/06/25 08:31:36 barstow $
+# $XFree86: xc/config/cf/Imake.cf,v 3.34.2.14 1999/07/29 09:22:23 hohndel Exp $
+
+# -----------------------------------------------------------------------
+# site-specific configuration parameters that need to come before
+# the platform-specific parameters - edit site.def to change
+
+# site: $XConsortium: site.def /main/revisionist/4 1996/12/31 08:02:07 kaleb $
+# site: $XFree86: xc/config/cf/site.def,v 3.17.2.1 1997/06/22 10:32:21 dawes Exp $
+
+# $XFree86: xc/config/cf/xf86site.def,v 3.101.2.31 1999/08/02 08:37:48 hohndel Exp $
+
+# ----------------------------------------------------------------------
+# platform-specific configuration parameters - edit linux.cf to change
+
+# platform: $TOG: linux.cf /main/36 1997/06/16 22:21:03 kaleb $
+# platform: $XFree86: xc/config/cf/linux.cf,v 3.57.2.18 1999/07/22 08:13:51 hohndel Exp $
+
+# operating system: Linux 2.2.16eepro-smp i686 [ELF] (2.2.16)
+# libc: (6.1.2)
+# binutils: (29)
+
+# $XConsortium: lnxLib.rules /main/13 1996/09/28 16:11:01 rws $
+# $XFree86: xc/config/cf/lnxLib.rules,v 3.28.2.4 1998/12/18 11:56:08 dawes Exp $
+
+# $XFree86: xc/config/cf/xfree86.cf,v 3.129.2.47 1999/08/03 09:41:30 hohndel Exp $
+
+# $XConsortium: xfree86.cf /main/34 1996/12/06 11:45:18 rws $
+
+LINKKITDIR = $(USRLIBDIR)/Server
+XF98LINKKITDIR = $(USRLIBDIR)/Server
+
+ XF86SRC = $(SERVERSRC)/hw/xfree86
+ XF86ACCELSRC = $(XF86SRC)/accel
+ XF86COMSRC = $(XF86SRC)/common
+ XF86CONFIGSRC = $(XF86COMSRC)
+ XF86HWSRC = $(XF86SRC)/common_hw
+ XF86OSSRC = $(XF86SRC)/os-support
+ VGADRIVERSRC = $(XF86SRC)/vga256/drivers
+VGA16DRIVERSRC = $(XF86SRC)/vga16/drivers
+ VGA2DRIVERSRC = $(XF86SRC)/vga2/drivers
+ MONODRIVERSRC = $(XF86SRC)/mono/drivers
+ S3DRIVERSRC = $(XF86SRC)/accel/s3/drivers
+ S3VDRIVERSRC = $(XF86SRC)/accel/s3_virge/drivers
+
+ XF68SRC = $(SERVERSRC)/hw/xfree68
+ XF68COMSRC = $(XF68SRC)/common
+ XF68CONFIGSRC = $(XF68COMSRC)
+ XF68OSSRC = $(XF68SRC)/os-support
+
+ XF98SRC = $(SERVERSRC)/hw/xfree98
+ XF98ACCELSRC = $(XF98SRC)/accel
+ XF98COMSRC = $(XF98SRC)/common
+ XF98CONFIGSRC = $(XF98COMSRC)
+ XF98HWSRC = $(XF98SRC)/common_hw/generic
+ XF98HWNECSRC = $(XF98SRC)/common_hw/nec
+ XF98HWPWSKBSRC = $(XF98SRC)/common_hw/pwskb
+ XF98HWPWLBSRC = $(XF98SRC)/common_hw/pwlb
+ XF98HWGA968SRC = $(XF98SRC)/common_hw/ga968
+ XF98OSSRC = $(XF98SRC)/os-support
+ XF98VGADRIVERSRC = $(XF98SRC)/vga256/drivers
+XF98VGA16DRIVERSRC = $(XF98SRC)/vga16/drivers
+ XF98VGA2DRIVERSRC = $(XF98SRC)/vga2/drivers
+ XF98MONODRIVERSRC = $(XF98SRC)/mono/drivers
+XF98NECS3DRIVERSRC = $(XF98SRC)/accel/s3nec/drivers
+XF98PWSKBDRIVERSRC = $(XF98SRC)/accel/s3pwskb/drivers
+ XF98PWLBDRIVERSRC = $(XF98SRC)/accel/s3pwlb/drivers
+XF98GA968DRIVERSRC = $(XF98SRC)/accel/s3ga968/drivers
+
+ XFREE86DOCDIR = $(DOCDIR)
+ XFREE86PSDOCDIR = $(DOCPSDIR)
+ XFREE86HTMLDOCDIR = $(DOCHTMLDIR)
+XFREE86JAPANESEDOCDIR = $(DOCDIR)/Japanese
+
+# $XConsortium: xf86.rules /main/9 1996/10/31 14:54:26 kaleb $
+# $XFree86: xc/config/cf/xf86.rules,v 3.16.2.2 1999/07/23 09:00:21 hohndel Exp $
+
+# ----------------------------------------------------------------------
+# site-specific configuration parameters that go after
+# the platform-specific parameters - edit site.def to change
+
+# site: $XConsortium: site.def /main/revisionist/4 1996/12/31 08:02:07 kaleb $
+# site: $XFree86: xc/config/cf/site.def,v 3.17.2.1 1997/06/22 10:32:21 dawes Exp $
+
+# ---------------------------------------------------------------------
+# Imake rules for building libraries, programs, scripts, and data files
+# rules: $TOG: Imake.rules /main/222 1997/07/17 20:04:40 kaleb $
+# rules: $XFree86: xc/config/cf/Imake.rules,v 3.33.2.10 1998/11/04 10:53:01 dawes Exp $
+
+ _NULLCMD_ = @ echo -n
+
+TKLIBNAME = tk
+
+TKLIBDIR = /usr/lib
+
+TCLLIBNAME = tcl
+
+TCLIBDIR = /usr/lib
+
+JPTKLIBNAME =
+
+JPTKLIBDIR =
+
+JPTCLLIBNAME =
+
+JPTCLIBDIR =
+
+ PATHSEP = /
+ SHELL = /bin/sh
+
+ TOP = .
+ CURRENT_DIR = .
+
+ IMAKE = imake
+ DEPEND = gccmakedep
+ MKDIRHIER = mkdir -p
+ EXPORTLISTGEN =
+ CONFIGSRC = $(TOP)/config
+ IMAKESRC = $(CONFIGSRC)/imake
+ DEPENDSRC = $(CONFIGSRC)/util
+
+ INCROOT = /usr/X11R6/include
+ USRLIBDIR = /usr/X11R6/lib
+ VARLIBDIR = /var/lib
+ SHLIBDIR = /usr/X11R6/lib
+ LINTLIBDIR = $(USRLIBDIR)/lint
+ MANPATH = /usr/X11R6/man
+ MANSOURCEPATH = $(MANPATH)/man
+ MANDIR = $(MANSOURCEPATH)1
+ LIBMANDIR = $(MANSOURCEPATH)3
+ FILEMANDIR = $(MANSOURCEPATH)5
+
+ AR = ar clq
+ BOOTSTRAPCFLAGS =
+ CC = gcc
+ AS = as
+
+.SUFFIXES: .cc
+
+ CXX = c++
+ CXXFILT = c++filt
+ CXXLIB =
+ CXXDEBUGFLAGS = -O2 -fno-strength-reduce
+CXXDEPENDINCLUDES =
+ CXXEXTRA_DEFINES =
+CXXEXTRA_INCLUDES =
+ CXXSTD_DEFINES = -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -D_XOPEN_SOURCE=500L -D_BSD_SOURCE -D_SVID_SOURCE $(CXXPROJECT_DEFINES)
+ CXXOPTIONS =
+ CXXINCLUDES = $(INCLUDES) $(TOP_INCLUDES) $(CXXEXTRA_INCLUDES)
+ CXXDEFINES = $(CXXINCLUDES) $(CXXSTD_DEFINES) $(THREADS_CXXDEFINES) $(CXXEXTRA_DEFINES) $(DEFINES)
+ CXXFLAGS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(THREADS_CXXFLAGS) $(CXXDEFINES)
+
+ COMPRESS = compress
+ GZIPCMD = gzip
+ CPP = /lib/cpp $(STD_CPP_DEFINES)
+ PREPROCESSCMD = gcc -E $(STD_CPP_DEFINES)
+ INSTALL = install
+ INSTALLFLAGS = -c
+ LD = ld
+ LEX = flex -l
+ LEXLIB = -lfl
+ YACC = bison -y
+ CCYACC = bison -y
+ LINT = lint
+ LINTLIBFLAG = -C
+ LINTOPTS = -axz
+ LN = ln -s
+ MAKE = make
+ MV = mv -f
+ CP = cp
+
+ RANLIB = ranlib
+ RANLIBINSTFLAGS =
+
+ RM = rm -f
+ MANSUFFIX = 1x
+ LIBMANSUFFIX = 3x
+ FILEMANSUFFIX = 5x
+ TROFF = groff -Tps
+ NROFF = nroff
+ MSMACROS = -ms
+ MANMACROS = -man
+ TBL = tbl
+ EQN = eqn
+ NEQN = neqn
+ COL = col
+ COLFLAGS = -b
+
+ DVIPS = dvips
+ LATEX = latex
+
+ STD_INCLUDES =
+ STD_CPP_DEFINES = -traditional -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -D_XOPEN_SOURCE=500L -D_BSD_SOURCE -D_SVID_SOURCE $(PROJECT_DEFINES)
+ STD_DEFINES = -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -D_XOPEN_SOURCE=500L -D_BSD_SOURCE -D_SVID_SOURCE $(PROJECT_DEFINES)
+ EXTRA_LOAD_FLAGS =
+ EXTRA_LDOPTIONS =
+ EXTRA_LIBRARIES =
+ TAGS = ctags
+
+ PARALLELMFLAGS =
+
+ SHAREDCODEDEF =
+ SHLIBDEF =
+
+ SHLIBLDFLAGS = -shared
+
+ PICFLAGS = -fPIC
+
+ CXXPICFLAGS = -fPIC
+
+ PROTO_DEFINES = -DFUNCPROTO=15 -DNARROWPROTO
+
+ INSTPGMFLAGS = -s
+
+ INSTBINFLAGS = -m 0755
+ INSTUIDFLAGS = -m 4711
+ INSTLIBFLAGS = -m 0644
+ INSTINCFLAGS = -m 0444
+ INSTMANFLAGS = -m 0444
+ INSTDATFLAGS = -m 0444
+ INSTKMEMFLAGS = -m 4711
+
+ PROJECTROOT = /usr/X11R6
+
+ CDEBUGFLAGS = -O2 -fno-strength-reduce
+ CCOPTIONS =
+
+ ALLINCLUDES = $(INCLUDES) $(EXTRA_INCLUDES) $(TOP_INCLUDES) $(STD_INCLUDES)
+ ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(EXTRA_DEFINES) $(PROTO_DEFINES) $(THREADS_DEFINES) $(DEFINES)
+ CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(THREADS_CFLAGS) $(ALLDEFINES)
+ LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) $(DEPEND_DEFINES)
+ LDPRELIB = -L$(USRLIBDIR)
+ LDPOSTLIB =
+ LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_LDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS)
+ CXXLDOPTIONS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_CXXLDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS)
+
+ LDLIBS = $(LDPOSTLIBS) $(THREADS_LIBS) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+
+ CCLINK = $(CC)
+
+ CXXLINK = $(CXX)
+
+ LDSTRIPFLAGS = -x
+ LDCOMBINEFLAGS = -r
+ DEPENDFLAGS =
+
+# Not sure this belongs here
+ TKLIBDIR = /usr/lib
+ TKINCDIR = /usr/include
+ TKLIBNAME = tk
+ TKLIBRARY = -L$(TKLIBDIR) -l$(TKLIBNAME)
+ TCLLIBDIR = /usr/lib
+ TCLINCDIR = /usr/include
+ TCLLIBNAME = tcl
+ TCLLIBRARY = -L$(TCLLIBDIR) -l$(TCLLIBNAME)
+ JPTKLIBDIR =
+ JPTKINCDIR =
+ JPTKLIBNAME =
+ JPTKLIBRARY = -L$(JPTKLIBDIR) -l$(JPTKLIBNAME)
+ JPTCLLIBDIR =
+ JPTCLINCDIR =
+ JPTCLLIBNAME =
+ JPTCLLIBRARY = -L$(JPTCLLIBDIR) -l$(JPTCLLIBNAME)
+
+ MACROFILE = linux.cf
+ RM_CMD = $(RM)
+
+ IMAKE_DEFINES =
+
+ IRULESRC = $(CONFIGDIR)
+ IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+ ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/X11.tmpl $(IRULESRC)/site.def $(IRULESRC)/$(MACROFILE) $(IRULESRC)/xfree86.cf $(IRULESRC)/xf86.rules $(IRULESRC)/xf86site.def $(IRULESRC)/host.def $(EXTRA_ICONFIGFILES)
+
+# $TOG: X11.rules /main/4 1997/04/30 15:23:24 kaleb $
+#
+#
+#
+# $XFree86: xc/config/cf/X11.rules,v 1.1.1.1.2.4 1999/04/21 07:20:58 hohndel Exp $
+
+# ----------------------------------------------------------------------
+# X Window System Build Parameters and Rules
+# $TOG: X11.tmpl /main/292 1997/05/20 10:05:59 kaleb $
+#
+#
+#
+#
+# $XFree86: xc/config/cf/X11.tmpl,v 1.8.2.9 1998/12/30 10:04:09 dawes Exp $
+
+CONNECTION_FLAGS = -DUNIXCONN -DTCPCONN -DHAS_STICKY_DIR_BIT
+
+# -----------------------------------------------------------------------
+# X Window System make variables; these need to be coordinated with rules
+
+ XTOP = $(TOP)
+ BINDIR = /usr/X11R6/bin
+ BUILDINCROOT = $(TOP)/exports
+ BUILDINCDIR = $(BUILDINCROOT)/include
+ BUILDINCTOP = ../..
+ BUILDLIBDIR = $(TOP)/exports/lib
+ BUILDLIBTOP = ../..
+ BUILDBINDIR = $(TOP)/exports/bin
+ BUILDBINTOP = ../..
+ XBUILDINCROOT = $(XTOP)/exports
+ XBUILDINCDIR = $(XBUILDINCROOT)/include/X11
+ XBUILDINCTOP = ../../..
+ XBUILDBINDIR = $(XBUILDINCROOT)/bin
+ INCDIR = $(INCROOT)
+ ADMDIR = /usr/adm
+ LIBDIR = $(USRLIBDIR)/X11
+ TOP_X_INCLUDES =
+
+ VARDIR = /var/X11
+
+ DOCDIR = $(LIBDIR)/doc
+ DOCHTMLDIR = $(DOCDIR)/html
+ DOCPSDIR = $(DOCDIR)/PostScript
+ FONTDIR = $(LIBDIR)/fonts
+ XINITDIR = $(LIBDIR)/xinit
+ XDMDIR = $(LIBDIR)/xdm
+ XDMVARDIR = $(VARLIBDIR)/xdm
+ TWMDIR = $(LIBDIR)/twm
+ XSMDIR = $(LIBDIR)/xsm
+ NLSDIR = $(LIBDIR)/nls
+ XLOCALEDIR = $(LIBDIR)/locale
+ PEXAPIDIR = $(LIBDIR)/PEX
+ LBXPROXYDIR = $(LIBDIR)/lbxproxy
+ PROXYMANAGERDIR = $(LIBDIR)/proxymngr
+ XPRINTDIR = $(LIBDIR)
+ XAPPLOADDIR = $(LIBDIR)/app-defaults
+ FONTCFLAGS = -t
+
+ INSTAPPFLAGS = $(INSTDATFLAGS)
+
+ RGB = rgb
+ FONTC = bdftopcf
+ MKFONTDIR = mkfontdir
+
+ DOCUTILSRC = $(XTOP)/doc/util
+ CLIENTSRC = $(TOP)/clients
+ DEMOSRC = $(TOP)/demos
+ XDOCMACROS = $(DOCUTILSRC)/macros.t
+ XIDXMACROS = $(DOCUTILSRC)/indexmacros.t
+ PROGRAMSRC = $(TOP)/programs
+ LIBSRC = $(XTOP)/lib
+ FONTSRC = $(XTOP)/fonts
+ INCLUDESRC = $(BUILDINCROOT)/include
+ XINCLUDESRC = $(INCLUDESRC)/X11
+ SERVERSRC = $(XTOP)/programs/Xserver
+ CONTRIBSRC = $(XTOP)/../contrib
+ UNSUPPORTEDSRC = $(XTOP)/unsupported
+ DOCSRC = $(XTOP)/doc
+ RGBSRC = $(XTOP)/programs/rgb
+ BDFTOPCFSRC = $(PROGRAMSRC)/bdftopcf
+ MKFONTDIRSRC = $(PROGRAMSRC)/mkfontdir
+ FONTSERVERSRC = $(PROGRAMSRC)/xfs
+ FONTINCSRC = $(XTOP)/include/fonts
+ EXTINCSRC = $(XTOP)/include/extensions
+ TRANSCOMMSRC = $(LIBSRC)/xtrans
+ TRANS_INCLUDES = -I$(TRANSCOMMSRC)
+
+ XENVLIBDIR = $(USRLIBDIR)
+ CLIENTENVSETUP = LD_LIBRARY_PATH=$(XENVLIBDIR)
+
+# $XConsortium: lnxLib.tmpl,v 1.5 95/01/11 21:44:44 kaleb Exp $
+# $XFree86: xc/config/cf/lnxLib.tmpl,v 3.9 1996/02/24 04:32:52 dawes Exp $
+
+ XLIBSRC = $(LIBSRC)/X11
+
+SOXLIBREV = 6.1
+DEPXONLYLIB =
+XONLYLIB = -lX11
+
+LINTXONLY = $(LINTLIBDIR)/llib-lX11.ln
+
+ XLIBONLY = $(XONLYLIB)
+
+ XEXTLIBSRC = $(LIBSRC)/Xext
+
+SOXEXTREV = 6.3
+DEPEXTENSIONLIB =
+EXTENSIONLIB = -lXext
+
+LINTEXTENSION = $(LINTLIBDIR)/llib-lXext.ln
+
+LINTEXTENSIONLIB = $(LINTEXTENSION)
+ DEPXLIB = $(DEPEXTENSIONLIB) $(DEPXONLYLIB)
+ XLIB = $(EXTENSIONLIB) $(XONLYLIB)
+ LINTXLIB = $(LINTXONLYLIB)
+
+ XSSLIBSRC = $(LIBSRC)/Xss
+
+DEPXSSLIB = $(USRLIBDIR)/libXss.a
+XSSLIB = -lXss
+
+LINTXSS = $(LINTLIBDIR)/llib-lXss.ln
+
+ XXF86MISCLIBSRC = $(LIBSRC)/Xxf86misc
+
+DEPXXF86MISCLIB = $(USRLIBDIR)/libXxf86misc.a
+XXF86MISCLIB = -lXxf86misc
+
+LINTXXF86MISC = $(LINTLIBDIR)/llib-lXxf86misc.ln
+
+ XXF86VMLIBSRC = $(LIBSRC)/Xxf86vm
+
+DEPXXF86VMLIB = $(USRLIBDIR)/libXxf86vm.a
+XXF86VMLIB = -lXxf86vm
+
+LINTXXF86VM = $(LINTLIBDIR)/llib-lXxf86vm.ln
+
+ XXF86DGALIBSRC = $(LIBSRC)/Xxf86dga
+
+DEPXXF86DGALIB = $(USRLIBDIR)/libXxf86dga.a
+XXF86DGALIB = -lXxf86dga
+
+LINTXXF86DGA = $(LINTLIBDIR)/llib-lXxf86dga.ln
+
+ XDPMSLIBSRC = $(LIBSRC)/Xdpms
+
+DEPXDPMSLIB = $(USRLIBDIR)/libXdpms.a
+XDPMSLIB = -lXdpms
+
+LINTXDPMS = $(LINTLIBDIR)/llib-lXdpms.ln
+
+ XAUTHSRC = $(LIBSRC)/Xau
+
+DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+XAUTHLIB = -lXau
+
+LINTXAUTH = $(LINTLIBDIR)/llib-lXau.ln
+
+ XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+
+DEPXDMCPLIB = $(USRLIBDIR)/libXdmcp.a
+XDMCPLIB = -lXdmcp
+
+LINTXDMCP = $(LINTLIBDIR)/llib-lXdmcp.ln
+
+ XMUSRC = $(LIBSRC)/Xmu
+
+SOXMUREV = 6.0
+DEPXMULIB =
+XMULIB = -lXmu
+
+LINTXMU = $(LINTLIBDIR)/llib-lXmu.ln
+
+ OLDXLIBSRC = $(LIBSRC)/oldX
+
+DEPOLDXLIB = $(USRLIBDIR)/liboldX.a
+OLDXLIB = -loldX
+
+LINTOLDX = $(LINTLIBDIR)/llib-loldX.ln
+
+ XPLIBSRC = $(LIBSRC)/Xp
+
+SOXPREV = 6.2
+DEPXPLIB =
+XPLIB = -lXp
+
+LINTXP = $(LINTLIBDIR)/llib-lXp.ln
+
+ TOOLKITSRC = $(LIBSRC)/Xt
+
+SOXTREV = 6.0
+DEPXTOOLONLYLIB =
+XTOOLONLYLIB = -lXt
+
+LINTXTOOLONLY = $(LINTLIBDIR)/llib-lXt.ln
+
+ DEPXTOOLLIB = $(DEPXTOOLONLYLIB) $(DEPSMLIB) $(DEPICELIB)
+ XTOOLLIB = $(XTOOLONLYLIB) $(SMLIB) $(ICELIB)
+ LINTXTOOLLIB = $(LINTXTOOLONLYLIB)
+
+ XALIBSRC = $(LIBSRC)/Xa
+
+SOXAREV = 1.0
+DEPXALIB =
+XALIB = -lXa
+
+LINTXA = $(LINTLIBDIR)/llib-lXa.ln
+
+ AWIDGETSRC = $(LIBSRC)/Xaw
+
+SOXAWREV = 6.1
+DEPXAWLIB =
+XAWLIB = -lXaw
+
+LINTXAW = $(LINTLIBDIR)/llib-lXaw.ln
+
+ XILIBSRC = $(LIBSRC)/Xi
+
+SOXINPUTREV = 6.0
+DEPXILIB =
+XILIB = -lXi
+
+LINTXI = $(LINTLIBDIR)/llib-lXi.ln
+
+ XTESTLIBSRC = $(LIBSRC)/Xtst
+
+SOXTESTREV = 6.1
+DEPXTESTLIB =
+XTESTLIB = -lXtst
+
+LINTXTEST = $(LINTLIBDIR)/llib-lXtst.ln
+
+ PEXLIBSRC = $(LIBSRC)/PEX5
+
+SOPEXREV = 6.0
+DEPPEXLIB =
+PEXLIB = -lPEX5
+
+LINTPEX = $(LINTLIBDIR)/llib-lPEX5.ln
+
+ XIELIBSRC = $(LIBSRC)/XIE
+
+SOXIEREV = 6.0
+DEPXIELIB =
+XIELIB = -lXIE
+
+LINTXIE = $(LINTLIBDIR)/llib-lXIE.ln
+
+ PHIGSLIBSRC = $(LIBSRC)/PHIGS
+
+DEPPHIGSLIB = $(USRLIBDIR)/libphigs.a
+PHIGSLIB = -lphigs
+
+LINTPHIGS = $(LINTLIBDIR)/llib-lphigs.ln
+
+DEPXBSDLIB = $(USRLIBDIR)/libXbsd.a
+XBSDLIB = -lXbsd
+
+LINTXBSD = $(LINTLIBDIR)/llib-lXbsd.ln
+
+ ICESRC = $(LIBSRC)/ICE
+
+SOICEREV = 6.3
+DEPICELIB =
+ICELIB = -lICE
+
+LINTICE = $(LINTLIBDIR)/llib-lICE.ln
+
+ SMSRC = $(LIBSRC)/SM
+
+SOSMREV = 6.0
+DEPSMLIB =
+SMLIB = -lSM
+
+LINTSM = $(LINTLIBDIR)/llib-lSM.ln
+
+ XKEYSRC = $(LIBSRC)/Xkey
+
+SOXKEYREV = 6.0
+DEPXKEYLIB =
+XKEYLIB = -lXkey
+
+LINTXKEY = $(LINTLIBDIR)/llib-lXkey.ln
+
+ FSLIBSRC = $(LIBSRC)/FS
+
+DEPFSLIB = $(USRLIBDIR)/libFS.a
+FSLIB = -lFS
+
+LINTFS = $(LINTLIBDIR)/llib-lFS.ln
+
+ FONTLIBSRC = $(LIBSRC)/font
+
+DEPFONTLIB = $(USRLIBDIR)/libfont.a
+FONTLIB = -lfont
+
+LINTFONT = $(LINTLIBDIR)/llib-lfont.ln
+
+ XPMLIBSRC = $(LIBSRC)/Xpm
+
+DEPXPMLIB = $(USRLIBDIR)/libXpm.a
+XPMLIB = -lXpm
+
+LINTXPM = $(LINTLIBDIR)/llib-lXpm.ln
+
+ XKBFILELIBSRC = $(LIBSRC)/xkbfile
+
+DEPXKBFILELIB = $(USRLIBDIR)/libxkbfile.a
+XKBFILELIB = -lxkbfile
+
+LINTXKBFILE = $(LINTLIBDIR)/llib-lxkbfile.ln
+
+ XKBCOMPCMD = xkbcomp
+
+ XKBUILIBSRC = $(LIBSRC)/xkbui
+
+DEPXKBUILIB = $(USRLIBDIR)/libxkbui.a
+XKBUILIB = -lxkbui
+
+LINTXKBUI = $(LINTLIBDIR)/llib-lxkbui.ln
+
+ DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+ DEPLIBS1 = $(DEPLIBS)
+ DEPLIBS2 = $(DEPLIBS)
+ DEPLIBS3 = $(DEPLIBS)
+ DEPLIBS4 = $(DEPLIBS)
+ DEPLIBS5 = $(DEPLIBS)
+ DEPLIBS6 = $(DEPLIBS)
+ DEPLIBS7 = $(DEPLIBS)
+ DEPLIBS8 = $(DEPLIBS)
+ DEPLIBS9 = $(DEPLIBS)
+ DEPLIBS10 = $(DEPLIBS)
+
+XMULIBONLY = -lXmu
+XMULIB = $(XMULIBONLY) $(XTOOLLIB) $(XLIB)
+
+ CONFIGDIR = $(LIBDIR)/config
+
+ USRLIBDIRPATH = $(USRLIBDIR)
+ LDPRELIBS = -L$(USRLIBDIR)
+ LDPOSTLIBS =
+ TOP_INCLUDES = -I$(INCROOT) $(TOP_X_INCLUDES)
+ PROJECT_DEFINES =
+
+CXXPROJECT_DEFINES =
+
+# ----------------------------------------------------------------------
+# start of Imakefile
+
+ INCLUDES = -I$(TOP) -I$(TOP)/X11
+LOCAL_LIBRARIES = $(XLIB)
+ SRCS = xsel.c
+ OBJS = xsel.o
+ CFLAGS += -Wall
+
+ PROGRAM = xsel
+
+all:: xsel
+
+xsel: $(OBJS) $(DEPLIBS)
+ $(RM) $@
+ $(CCLINK) -o $@ $(LDOPTIONS) $(OBJS) $(LOCAL_LIBRARIES) $(LDLIBS) $(EXTRA_LOAD_FLAGS)
+
+install:: xsel
+ @if [ -d $(DESTDIR)$(BINDIR) ]; then set +x; \
+ else (set -x; $(MKDIRHIER) $(DESTDIR)$(BINDIR)); fi
+ $(INSTALL) $(INSTALLFLAGS) $(INSTPGMFLAGS) xsel $(DESTDIR)$(BINDIR)/xsel
+
+install.man:: xsel.man
+ @if [ -d $(DESTDIR)$(MANDIR) ]; then set +x; \
+ else (set -x; $(MKDIRHIER) $(DESTDIR)$(MANDIR)); fi
+ $(INSTALL) $(INSTALLFLAGS) $(INSTMANFLAGS) xsel.man $(DESTDIR)$(MANDIR)/xsel.$(MANSUFFIX)
+
+depend::
+ $(DEPEND) $(DEPENDFLAGS) -- $(ALLDEFINES) $(DEPEND_DEFINES) -- $(SRCS)
+
+lint:
+ $(LINT) $(LINTFLAGS) $(SRCS) $(LINTLIBS)
+lint1:
+ $(LINT) $(LINTFLAGS) $(FILE) $(LINTLIBS)
+
+clean::
+ $(RM) xsel
+
+shar:
+ shar Imakefile xsel.c xsel.h xsel.1x >xsel.shar
+
+# ----------------------------------------------------------------------
+# common rules for all Makefiles - do not edit
+
+.c.i:
+ $(RM) $@
+ $(CC) -E $(CFLAGS) $(_NOOP_) $*.c > $@
+
+emptyrule::
+
+clean::
+ $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut "#"*
+
+Makefile::
+ -@if [ -f Makefile ]; then set -x; \
+ $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+ else exit 0; fi
+ $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+ $(TAGS) -w *.[ch]
+ $(TAGS) -xw *.[ch] > TAGS
+
+man_keywords::
+
+# ----------------------------------------------------------------------
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+ @echo "install in $(CURRENT_DIR) done"
+
+install.man::
+ @echo "install.man in $(CURRENT_DIR) done"
+
+install.linkkit::
+ @echo "install.linkkit in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+depend::
+
+# ----------------------------------------------------------------------
+# dependencies generated by makedepend
+
+# DO NOT DELETE
+xsel.o: xsel.c /usr/include/stdio.h /usr/include/features.h \
+ /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
+ /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h \
+ /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h \
+ /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
+ /usr/include/bits/sched.h /usr/include/libio.h \
+ /usr/include/_G_config.h /usr/include/bits/stdio_lim.h \
+ /usr/include/getopt.h /usr/include/stdlib.h /usr/include/sys/types.h \
+ /usr/include/time.h /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h \
+ /usr/include/alloca.h /usr/include/errno.h /usr/include/bits/errno.h \
+ /usr/include/linux/errno.h /usr/include/asm/errno.h \
+ /usr/include/unistd.h /usr/include/bits/posix_opt.h \
+ /usr/include/bits/environments.h /usr/include/bits/wordsize.h \
+ /usr/include/bits/confname.h /usr/include/string.h /usr/include/pwd.h \
+ /usr/include/sys/stat.h /usr/include/bits/stat.h \
+ /usr/include/sys/time.h /usr/include/bits/time.h /usr/include/fcntl.h \
+ /usr/include/bits/fcntl.h /usr/include/setjmp.h \
+ /usr/include/bits/setjmp.h /usr/include/signal.h \
+ /usr/include/bits/signum.h /usr/include/bits/siginfo.h \
+ /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h \
+ /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/sigthread.h /usr/X11R6/include/X11/StringDefs.h \
+ /usr/X11R6/include/X11/Xlib.h /usr/X11R6/include/X11/X.h \
+ /usr/X11R6/include/X11/Xfuncproto.h /usr/X11R6/include/X11/Xosdefs.h \
+ /usr/X11R6/include/X11/Intrinsic.h /usr/X11R6/include/X11/Xutil.h \
+ /usr/X11R6/include/X11/Xresource.h /usr/X11R6/include/X11/Core.h \
+ /usr/X11R6/include/X11/Composite.h \
+ /usr/X11R6/include/X11/Constraint.h /usr/X11R6/include/X11/Object.h \
+ /usr/X11R6/include/X11/RectObj.h /usr/X11R6/include/X11/Xatom.h \
+ xsel.h
diff --git a/xsel.1x b/xsel.1x
@@ -0,0 +1,134 @@
+.TH XSEL 1 "July 2001"
+.SH NAME
+xsel \- manipulate the X selection.
+.SH SYNOPSIS
+.B xsel
+[\fIOPTION\fR]...
+.SH DESCRIPTION
+.PP
+Retrieve and set the X selection.
+.PP
+The X server maintains three selections, called \fIPRIMARY\fR,
+\fISECONDARY\fR and \fICLIPBOARD\fR. The PRIMARY selection is conventionally
+used to implement copying and pasting via the middle mouse button. The
+SECONDARY and CLIPBOARD selections are less frequently used by application
+programs. This program operates on the PRIMARY selection unless otherwise
+specified.
+.PP
+By default, this program outputs the selection without modification if both
+standard input and standard output are terminals (ttys). Otherwise, the
+current selection is output if standard output is not a terminal
+(tty), and the selection is set from standard input if standard input
+is not a terminal (tty). If any input or output options are given then
+the program behaves only in the requested mode.
+.PP
+If both input and output is required then the previous selection is
+output before being replaced by the contents of standard input.
+
+.PP
+\fBInput options\fR
+.TP
+\fB\-a\fR, \fB\-\-append\fR
+append standard input to the selection. Implies \fB\-i\fR.
+.TP
+\fB\-f\fR, \fB\-\-follow\fR
+append to selection as standard input grows. Implies \fB\-i\fR.
+.TP
+\fB\-i\fR, \fB\-\-input\fR
+read standard input into the selection.
+
+.PP
+\fBOutput options\fR
+.TP
+\fB\-o\fR, \fB\-\-output\fR
+write the selection to standard output.
+
+.PP
+\fBAction options\fR
+.TP
+\fB\-c\fR, \fB\-\-clear\fR
+clear the selection. Overrides all \fIinput\fR options.
+.TP
+\fB\-d\fR, \fB\-\-delete\fR
+Request that the current selection be deleted. This not only clears the
+selection, but also requests to the program in which the selection
+resides that the selected contents be deleted. Overrides all \fIinput\fR
+options.
+
+.PP
+\fBSelection options\fR
+.TP
+\fB\-p\fR, \fB\-\-primary\fR
+operate on the PRIMARY selection (default).
+.TP
+\fB\-s\fR, \fB\-\-secondary\fR
+operate on the SECONDARY selection.
+.TP
+\fB\-b\fR, \fB\-\-clipboard\fR
+operate on the CLIPBOARD selection.
+
+.TP
+\fB\-k\fR, \fB\-\-keep\fR
+Do not modify the selections, but make the PRIMARY and SECONDARY selections
+persist even after the programs they were selected in exit. (Conventionally,
+the CLIPBOARD selection is persisted by a separate special purpose program
+such as \fIxclipboard(1)\fR). Ignores all \fIinput\fR and \fIoutput\fR
+options.
+.TP
+\fB\-x\fR, \fB\-\-exchange\fR
+exchange the PRIMARY and SECONDARY selections. Ignores all \fIinput\fR
+and \fIoutput\fR options.
+
+.PP
+\fBX options\fR
+.TP
+\fB\-d\fR \fIdisplayname\fR, \fB\-\-display\fR \fIdisplayname\fR
+specify the server to use; see \fIX(1)\fP.
+.TP
+\fB\-t\fR \fIms\fR, \fB\-\-selectionTimeout\fR \fIms\fR
+Specify the timeout in milliseconds within which the selection must be
+retrieved. A value of \fB0\fR (zero) specifies no timeout (default).
+
+.PP
+\fBMiscellaneous options\fR
+.TP
+\fB\-l\fR \fIfilename\fR, \fB\-\-logfile\fR \fIfilename\fR
+Specify the file to log errors to when detached. (Default
+\fI$HOME/.xsel.log\fR)
+.TP
+\fB\-n\fR, \fB\-\-nodetach\fR
+Do not detach from the controlling terminal. Without this option, xsel will
+fork to become a background process in \fIinput\fR, \fIexchange\fR and
+\fIkeep\fR modes.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+display usage information and exit
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Print informative messages. Additional instances of \fI-v\fR raise the
+debugging level, ie. print more information.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+output version information and exit
+.PP
+.SH NOTES
+.PP
+\fIThere is no X selection buffer.\fR The selection mechanism in X11 is
+an interclient communication mediated by the X server each time any
+program wishes to know the selection contents, eg. to perform a middle
+mouse button paste. In order to implement modification of the selection(s)
+(in \fIinput\fR, \fIkeep\fR and \fIexchange\fR modes) this program detaches
+from the terminal, spawning a child process to supply the new selection(s)
+on demand. This child exits immediately when any other program takes over
+the selection(s), eg. when the user next selects some text in a terminal
+window or by running \fBxsel -c\fR.
+.PP
+.SH STANDARDS
+xsel conforms to the Inter-Client Communication Conventions Manual
+Version 2.0 (ICCCM2).
+.PP
+.SH "SEE ALSO"
+X(1), Xserver(1), xset(1), xclipboard(1), xpaste(1)
+.BR
+.SH AUTHOR
+Conrad Parker <conrad@vergenet.net>, July 2001
diff --git a/xsel.c b/xsel.c
@@ -0,0 +1,1954 @@
+/*
+ * xsel -- manipulate the X selection
+ * Copyright (C) 2001 Conrad Parker <conrad@vergenet.net>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <X11/StringDefs.h>
+#include <X11/Xlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/Xatom.h>
+
+#include "xsel.h"
+
+
+/* The name we were invoked as (argv[0]) */
+static char * progname;
+
+/* Verbosity level for debugging */
+static int debug_level = DEBUG_LEVEL;
+
+/* Our X Display and Window */
+static Display * display;
+static Window window;
+
+/* Maxmimum request size supported by this X server */
+static long max_req;
+
+/* Our timestamp for all operations */
+static Time timestamp;
+
+static Atom timestamp_atom; /* The TIMESTAMP atom */
+static Atom multiple_atom; /* The MULTIPLE atom */
+static Atom targets_atom; /* The TARGETS atom */
+static Atom delete_atom; /* The DELETE atom */
+static Atom incr_atom; /* The INCR atom */
+static Atom null_atom; /* The NULL atom */
+static Atom text_atom; /* The TEXT atom */
+
+/* Number of selection targets served by this.
+ * (MULTIPLE, INCR, TARGETS, TIMESTAMP, DELETE, TEXT and STRING) */
+#define NUM_TARGETS 7
+static Atom supported_targets[NUM_TARGETS];
+
+/* do_follow: Follow mode for output */
+static Boolean do_follow = False;
+
+/* nodaemon: Disable daemon mode if True. */
+static Boolean no_daemon = False;
+
+/* logfile: name of file to log error messages to when detached */
+static char logfile[MAXFNAME];
+
+/* fstat() on stdin and stdout */
+static struct stat in_statbuf, out_statbuf;
+
+static int total_input = 0;
+static int current_alloc = 0;
+
+static long timeout = 0;
+static struct itimerval timer;
+
+/*
+ * usage ()
+ *
+ * print usage information.
+ */
+static void
+usage (void)
+{
+ printf ("Usage: xsel [options]\n");
+ printf ("Manipulate the X selection.\n\n");
+ printf ("By default the current selection is output and not modified if both\n");
+ printf ("standard input and standard output are terminals (ttys). Otherwise,\n");
+ printf ("the current selection is output if standard output is not a terminal\n");
+ printf ("(tty), and the selection is set from standard input if standard input\n");
+ printf ("is not a terminal (tty). If any input or output options are given then\n");
+ printf ("the program behaves only in the requested mode.\n\n");
+ printf ("If both input and output is required then the previous selection is\n");
+ printf ("output before being replaced by the contents of standard input.\n\n");
+ printf ("Input options\n");
+ printf (" -a, --append Append standard input to the selection\n");
+ printf (" -f, --follow Append to selection as standard input grows\n");
+ printf (" -i, --input Read standard input into the selection\n\n");
+ printf ("Output options\n");
+ printf (" -o, --output Write the selection to standard output\n\n");
+ printf ("Action options\n");
+ printf (" -c, --clear Clear the selection\n");
+ printf (" -d, --delete Request that the selection be cleared and that\n");
+ printf (" the application owning it delete its contents\n\n");
+ printf ("Selection options\n");
+ printf (" -p, --primary Operate on the PRIMARY selection (default)\n");
+ printf (" -s, --secondary Operate on the SECONDARY selection\n");
+ printf (" -b, --clipboard Operate on the CLIPBOARD selection\n\n");
+ printf (" -k, --keep Do not modify the selections, but make the PRIMARY\n");
+ printf (" and SECONDARY selections persist even after the\n");
+ printf (" programs they were selected in exit.\n");
+ printf (" -x, --exchange Exchange the PRIMARY and SECONDARY selections\n\n");
+ printf ("X options\n");
+ printf (" --display displayname\n");
+ printf (" Specify the connection to the X server\n");
+ printf (" -t ms, --selectionTimeout ms\n");
+ printf (" Specify the timeout in milliseconds within which the\n");
+ printf (" selection must be retrieved. A value of 0 (zero)\n");
+ printf (" specifies no timeout (default)\n\n");
+ printf ("Miscellaneous options\n");
+ printf (" -l, --logfile Specify file to log errors to when detached.\n");
+ printf (" -n, --nodetach Do not detach from the controlling terminal. Without\n");
+ printf (" this option, xsel will fork to become a background\n");
+ printf (" process in input, exchange and keep modes.\n\n");
+ printf (" -h, --help Display this help and exit\n");
+ printf (" -v, --verbose Print informative messages\n");
+ printf (" --version Output version information and exit\n\n");
+ printf ("Please report bugs to <conrad@vergenet.net>.\n");
+}
+
+/*
+ * exit_err (fmt)
+ *
+ * Print a formatted error message and errno information to stderr,
+ * then exit with return code 1.
+ */
+static void
+exit_err (const char * fmt, ...)
+{
+ va_list ap;
+ int errno_save;
+ char buf[MAXLINE];
+ int n;
+
+ errno_save = errno;
+
+ va_start (ap, fmt);
+
+ snprintf (buf, MAXLINE, "%s: ", progname);
+ n = strlen (buf);
+
+ vsnprintf (buf+n, MAXLINE-n, fmt, ap);
+ n = strlen (buf);
+
+ snprintf (buf+n, MAXLINE-n, ": %s\n", strerror (errno_save));
+
+ fflush (stdout); /* in case stdout and stderr are the same */
+ fputs (buf, stderr);
+ fflush (NULL);
+
+ va_end (ap);
+ exit (1);
+}
+
+/*
+ * print_err (fmt)
+ *
+ * Print a formatted error message to stderr.
+ */
+static void
+print_err (const char * fmt, ...)
+{
+ va_list ap;
+ int errno_save;
+ char buf[MAXLINE];
+ int n;
+
+ errno_save = errno;
+
+ va_start (ap, fmt);
+
+ snprintf (buf, MAXLINE, "%s: ", progname);
+ n = strlen (buf);
+
+ vsnprintf (buf+n, MAXLINE-n, fmt, ap);
+ n = strlen (buf);
+
+ fflush (stdout); /* in case stdout and stderr are the same */
+ fputs (buf, stderr);
+ fputc ('\n', stderr);
+ fflush (NULL);
+
+ va_end (ap);
+}
+
+/*
+ * print_debug (level, fmt)
+ *
+ * Print a formatted debugging message of level 'level' to stderr
+ */
+#define print_debug(x,y...) {if (x <= debug_level) print_err (y);}
+
+/*
+ * get_atom_name (atom)
+ *
+ * Returns a string with a printable name for the Atom 'atom'.
+ */
+static unsigned char *
+get_atom_name (Atom atom)
+{
+ if (atom == None) return "None";
+ if (atom == XA_STRING) return "STRING";
+ if (atom == XA_PRIMARY) return "PRIMARY";
+ if (atom == XA_SECONDARY) return "SECONDARY";
+ if (atom == timestamp_atom) return "TIMESTAMP";
+ if (atom == multiple_atom) return "MULTIPLE";
+ if (atom == targets_atom) return "TARGETS";
+ if (atom == delete_atom) return "DELETE";
+ if (atom == incr_atom) return "INCR";
+ if (atom == null_atom) return "NULL";
+ if (atom == text_atom) return "TEXT";
+ if (atom == XInternAtom (display, "XSEL_DATA", True)) return "XSEL_DATA";
+
+ return "<unknown atom>";
+}
+
+/*
+ * debug_property (level, requestor, property, target, length)
+ *
+ * Print debugging information (at level 'level') about a property received.
+ */
+static void
+debug_property (int level, Window requestor, Atom property, Atom target,
+ int length)
+{
+ print_debug (level, "Got window property: requestor 0x%x, property 0x%x, target 0x%x %s, length %d bytes", requestor, property, target, get_atom_name (target), length);
+}
+
+/*
+ * xs_malloc (size)
+ *
+ * Malloc wrapper. Always returns a successful allocation. Exits if the
+ * allocation didn't succeed.
+ */
+static void *
+xs_malloc (size_t size)
+{
+ void * ret;
+
+ if ((ret = malloc (size)) == NULL) {
+ exit_err ("malloc error");
+ }
+
+ return ret;
+}
+
+/*
+ * get_homedir ()
+ *
+ * Get the user's home directory.
+ */
+static char *
+get_homedir (void)
+{
+ uid_t uid;
+ char * username, * homedir;
+ struct passwd * pw;
+
+ if ((homedir = getenv ("HOME")) != NULL) {
+ return homedir;
+ }
+
+ /* else ... go hunting for it */
+ uid = getuid ();
+
+ username = getenv ("LOGNAME");
+ if (!username) username = getenv ("USER");
+
+ if (username) {
+ pw = getpwnam (username);
+ if (pw && pw->pw_uid == uid) goto gotpw;
+ }
+
+ pw = getpwuid (uid);
+
+gotpw:
+
+ if (!pw) {
+ exit_err ("error retrieving passwd entry");
+ }
+
+ homedir = strdup (pw->pw_dir);
+
+ return homedir;
+}
+
+/*
+ * become_daemon ()
+ *
+ * Perform the required procedure to become a daemon process, as
+ * outlined in the Unix programming FAQ:
+ * http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+ * and open a logfile.
+ */
+static void
+become_daemon (void)
+{
+ pid_t pid;
+ int null_r_fd, null_w_fd, log_fd;
+ char * homedir;
+
+ if (no_daemon) return;
+
+ homedir = get_homedir ();
+
+ /* Check that we can open a logfile before continuing */
+
+ /* If the user has specified a --logfile, use that ... */
+ if (logfile[0] == '\0') {
+ /* ... otherwise use the default logfile */
+ snprintf (logfile, MAXFNAME, "%s/.xsel.log", homedir);
+ }
+
+ /* Make sure to create the logfile with sane permissions */
+ log_fd = open (logfile, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (log_fd == -1) {
+ exit_err ("error opening logfile %s for writing", logfile);
+ }
+ print_debug (D_INFO, "opened logfile %s", logfile);
+
+ if ((pid = fork()) == -1) {
+ exit_err ("error forking");
+ } else if (pid > 0) {
+ _exit (0);
+ }
+
+ if (setsid () == -1) {
+ exit_err ("setsid error");
+ }
+
+ if ((pid = fork()) == -1) {
+ exit_err ("error forking");
+ } else if (pid > 0) {
+ _exit (0);
+ }
+
+ umask (0);
+
+ if (chdir (homedir) == -1) {
+ print_debug (D_WARN, "Could not chdir to %s\n", homedir);
+ if (chdir ("/") == -1) {
+ exit_err ("Error chdir to /");
+ }
+ }
+
+ /* dup2 /dev/null on stdin unless following input */
+ if (!do_follow) {
+ null_r_fd = open ("/dev/null", O_RDONLY);
+ if (null_r_fd == -1) {
+ exit_err ("error opening /dev/null for reading");
+ }
+ if (dup2 (null_r_fd, 0) == -1) {
+ exit_err ("error duplicating /dev/null on stdin");
+ }
+ }
+
+ /* dup2 /dev/null on stdout */
+ null_w_fd = open ("/dev/null", O_WRONLY|O_APPEND);
+ if (null_w_fd == -1) {
+ exit_err ("error opening /dev/null for writing");
+ }
+ if (dup2 (null_w_fd, 1) == -1) {
+ exit_err ("error duplicating /dev/null on stdout");
+ }
+
+ /* dup2 logfile on stderr */
+ if (dup2 (log_fd, 2) == -1) {
+ exit_err ("error duplicating logfile %s on stderr", logfile);
+ }
+}
+
+/*
+ * get_timestamp ()
+ *
+ * Get the current X server time.
+ *
+ * This is done by doing a zero-length append to a random property of the
+ * window, and checking the time on the subsequent PropertyNotify event.
+ *
+ * PRECONDITION: the window must have PropertyChangeMask set.
+ */
+static Time
+get_timestamp (void)
+{
+ XEvent event;
+
+ XChangeProperty (display, window, XA_WM_NAME, XA_STRING, 8,
+ PropModeAppend, NULL, 0);
+
+ while (1) {
+ XNextEvent (display, &event);
+
+ if (event.type == PropertyNotify)
+ return event.xproperty.time;
+ }
+}
+
+/*
+ * SELECTION RETRIEVAL
+ * ===================
+ *
+ * The following functions implement retrieval of an X selection,
+ * optionally within a user-specified timeout.
+ *
+ *
+ * Selection timeout handling.
+ * ---------------------------
+ *
+ * The selection retrieval can time out if no response is received within
+ * a user-specified time limit. In order to ensure we time the entire
+ * selection retrieval, we use an interval timer and catch SIGVTALRM.
+ * [Calling select() on the XConnectionNumber would only provide a timeout
+ * to the first XEvent.]
+ */
+
+/* The jmp_buf to longjmp out of the signal handler */
+static jmp_buf env_alrm;
+
+/*
+ * alarm_handler (sig)
+ *
+ * Signal handler for catching SIGVTALRM.
+ */
+static void
+alarm_handler (int sig)
+{
+ siglongjmp (env_alrm, 1);
+}
+
+/*
+ * get_append_property ()
+ *
+ * Get a window property and append its data to a buffer at a given offset
+ * pointed to by *offset. 'offset' is modified by this routine to point to
+ * the end of the data.
+ *
+ * Returns True if more data is available for receipt.
+ *
+ * If an error is encountered, the buffer is free'd.
+ */
+static Boolean
+get_append_property (XSelectionEvent * xsl, unsigned char ** buffer,
+ int * offset, int * alloc)
+{
+ unsigned char * ptr;
+ Atom target;
+ int format;
+ unsigned long bytesafter, length;
+ unsigned char * value;
+
+ XGetWindowProperty (xsl->display, xsl->requestor, xsl->property,
+ 0L, 1000000, True, (Atom)AnyPropertyType,
+ &target, &format, &length, &bytesafter, &value);
+
+ debug_property (D_TRACE, xsl->requestor, xsl->property, target, length);
+
+ if (target != XA_STRING) {
+ print_debug (D_OBSC, "target %s not XA_STRING in get_append_property()",
+ get_atom_name (target));
+ free (*buffer);
+ *buffer = NULL;
+ return False;
+ } else if (length == 0) {
+ /* A length of 0 indicates the end of the transfer */
+ print_debug (D_TRACE, "Got zero length property; end of INCR transfer");
+ return False;
+ } else if (format == 8) {
+ if (*offset + length > *alloc) {
+ *alloc = *offset + length;
+ if ((*buffer = realloc (*buffer, *alloc)) == NULL) {
+ exit_err ("realloc error");
+ }
+ }
+ ptr = *buffer + *offset;
+ strncpy (ptr, value, length);
+ *offset += length;
+ print_debug (D_TRACE, "Appended %d bytes to buffer\n", length);
+ } else {
+ print_debug (D_WARN, "Retrieved non-8-bit data\n");
+ }
+
+ return True;
+}
+
+
+/*
+ * wait_incr_selection (selection)
+ *
+ * Retrieve a property of target type INCR. Perform incremental retrieval
+ * and return the resulting data.
+ */
+static unsigned char *
+wait_incr_selection (Atom selection, XSelectionEvent * xsl, int init_alloc)
+{
+ XEvent event;
+ unsigned char * incr_base = NULL, * incr_ptr = NULL;
+ int incr_alloc = 0, incr_xfer = 0;
+ Boolean wait_prop = True;
+
+ print_debug (D_TRACE, "Initialising incremental retrieval of at least %d bytes\n", init_alloc);
+
+ /* Take an interest in the requestor */
+ XSelectInput (xsl->display, xsl->requestor, PropertyChangeMask);
+
+ incr_alloc = init_alloc;
+ incr_base = xs_malloc (incr_alloc);
+ incr_ptr = incr_base;
+
+ print_debug (D_TRACE, "Deleting property that informed of INCR transfer");
+ XDeleteProperty (xsl->display, xsl->requestor, xsl->property);
+
+ print_debug (D_TRACE, "Waiting on PropertyNotify events");
+ while (wait_prop) {
+ XNextEvent (xsl->display, &event);
+
+ switch (event.type) {
+ case PropertyNotify:
+ if (event.xproperty.state != PropertyNewValue) break;
+
+ wait_prop = get_append_property (xsl, &incr_base, &incr_xfer,
+ &incr_alloc);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* when zero length found, finish up & delete last */
+ XDeleteProperty (xsl->display, xsl->requestor, xsl->property);
+
+ print_debug (D_TRACE, "Finished INCR retrieval");
+
+ return incr_base;
+}
+
+/*
+ * wait_selection (selection, request_target)
+ *
+ * Block until we receive a SelectionNotify event, and return its
+ * contents; or NULL in the case of a deletion or error. This assumes we
+ * have already called XConvertSelection, requesting a string (explicitly
+ * XA_STRING) or deletion (delete_atom).
+ */
+static unsigned char *
+wait_selection (Atom selection, Atom request_target)
+{
+ XEvent event;
+ Atom target;
+ int format;
+ unsigned long bytesafter, length;
+ unsigned char * value, * retval = NULL;
+ Boolean keep_waiting = True;
+
+ while (keep_waiting) {
+ XNextEvent (display, &event);
+
+ switch (event.type) {
+ case SelectionNotify:
+ if (event.xselection.selection != selection) break;
+
+ if (event.xselection.property == None) {
+ print_debug (D_WARN, "Conversion refused");
+ value = NULL;
+ keep_waiting = False;
+ } else if (event.xselection.property == null_atom &&
+ request_target == delete_atom) {
+ } else {
+ XGetWindowProperty (event.xselection.display,
+ event.xselection.requestor,
+ event.xselection.property, 0L, 1000000,
+ False, (Atom)AnyPropertyType, &target,
+ &format, &length, &bytesafter, &value);
+
+ debug_property (D_TRACE, event.xselection.requestor,
+ event.xselection.property, target, length);
+
+ if (target == incr_atom) {
+ /* Handle INCR transfers */
+ retval = wait_incr_selection (selection, &event.xselection,
+ *(int *)value);
+ keep_waiting = False;
+ } else if (target != XA_STRING && request_target != delete_atom) {
+ /* Report non-TEXT atoms */
+ print_debug (D_WARN, "Selection (type %s) is not a string.",
+ get_atom_name (target));
+ free (retval);
+ retval = NULL;
+ keep_waiting = False;
+ } else {
+ retval = strdup (value);
+ XFree (value);
+ keep_waiting = False;
+ }
+
+ XDeleteProperty (event.xselection.display,
+ event.xselection.requestor,
+ event.xselection.property);
+
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Now that we've received the SelectionNotify event, clear any
+ * remaining timeout. */
+ if (timeout > 0) {
+ setitimer (ITIMER_VIRTUAL, (struct itimerval *)0, (struct itimerval *)0);
+ }
+
+ return retval;
+}
+
+/*
+ * get_selection (selection, request_target)
+ *
+ * Retrieves the specified selection and returns its value.
+ *
+ * If a non-zero timeout is specified then set a virtual interval
+ * timer. Return NULL and print an error message if the timeout
+ * expires before the selection has been retrieved.
+ */
+static unsigned char *
+get_selection (Atom selection, Atom request_target)
+{
+ Atom prop;
+ unsigned char * retval;
+
+ prop = XInternAtom (display, "XSEL_DATA", False);
+ XConvertSelection (display, selection, request_target, prop, window,
+ timestamp);
+ XSync (display, False);
+
+ if (timeout > 0) {
+ if (signal (SIGVTALRM, alarm_handler) == SIG_ERR) {
+ exit_err ("error setting timeout handler");
+ }
+
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = timeout;
+ timer.it_value.tv_sec = 0;
+ timer.it_value.tv_usec = timeout;
+
+ if (sigsetjmp (env_alrm, 0) == 0) {
+ setitimer (ITIMER_VIRTUAL, &timer, (struct itimerval *)0);
+ retval = wait_selection (selection, request_target);
+ } else {
+ print_debug (D_WARN, "selection timed out");
+ retval = NULL;
+ }
+ } else {
+ retval = wait_selection (selection, request_target);
+ }
+
+ return retval;
+}
+
+/*
+ * SELECTION SETTING
+ * =================
+ *
+ * The following functions allow a given selection to be set, appended to
+ * or cleared, or to exchange the primary and secondary selections.
+ */
+
+/*
+ * copy_sel (s)
+ *
+ * Copy a string into a new selection buffer, and intitialise
+ * current_alloc and total_input to exactly its length.
+ */
+static unsigned char *
+copy_sel (unsigned char * s)
+{
+ unsigned char * new_sel = NULL;
+
+ new_sel = strdup (s);
+ current_alloc = total_input = strlen (s);
+
+ return new_sel;
+}
+
+/*
+ * read_input (read_buffer, do_select)
+ *
+ * Read input from stdin into the specified read_buffer.
+ *
+ * read_buffer must have been dynamically allocated before calling this
+ * function, or be NULL. Input is read until end-of-file is reached, and
+ * read_buffer will be reallocated to accomodate the entire contents of
+ * the input. read_buffer, which may have been reallocated, is returned
+ * upon completion.
+ *
+ * If 'do_select' is True, this function will first check if any data
+ * is available for reading, and return immediately if not.
+ */
+static unsigned char *
+read_input (unsigned char * read_buffer, Boolean do_select)
+{
+ int insize = in_statbuf.st_blksize;
+ unsigned char * new_buffer = NULL;
+ int d, fatal = 0, nfd;
+ ssize_t n;
+ fd_set fds;
+ struct timeval select_timeout;
+
+ if (do_select) {
+try_read:
+ /* Check if data is available for reading -- if not, return immediately */
+ FD_ZERO (&fds);
+ FD_SET (0, &fds);
+
+ select_timeout.tv_sec = (time_t)0;
+ select_timeout.tv_usec = (time_t)0;
+
+ nfd = select (1, &fds, NULL, NULL, &select_timeout);
+ if (nfd == -1) {
+ if (errno == EINTR) goto try_read;
+ else exit_err ("select error");
+ } else if (nfd == 0) {
+ print_debug (D_TRACE, "No data available for reading");
+ return read_buffer;
+ }
+ }
+
+ do {
+ /* check if buffer is full */
+ if (current_alloc == total_input) {
+ if ((d = (current_alloc % insize)) != 0) current_alloc += (insize-d);
+ current_alloc *= 2;
+ new_buffer = realloc (read_buffer, current_alloc);
+ if (new_buffer == NULL) {
+ exit_err ("realloc error");
+ }
+ read_buffer = new_buffer;
+ }
+
+ /* read the remaining data, up to the optimal block length */
+ n = read (0, &read_buffer[total_input],
+ MIN(current_alloc - total_input, insize));
+ if (n == -1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ default:
+ perror ("read error");
+ fatal = 1;
+ break;
+ }
+ }
+ total_input += n;
+ } while (n != 0 && !fatal);
+
+ read_buffer[total_input] = '\0';
+
+ print_debug (D_TRACE, "Accumulated %d bytes input", total_input);
+
+ return read_buffer;
+}
+
+/*
+ * initialise_read (read_buffer)
+ *
+ * Initialises the read_buffer and the state variable current_alloc.
+ * read_buffer is reallocated to accomodate either the entire input
+ * if stdin is a regular file, or at least one block of input otherwise.
+ * If the supplied read_buffer is NULL, a new buffer will be allocated.
+ */
+static unsigned char *
+initialise_read (unsigned char * read_buffer)
+{
+ int insize = in_statbuf.st_blksize;
+ unsigned char * new_buffer = NULL;
+
+ if (S_ISREG (in_statbuf.st_mode)) {
+ current_alloc += in_statbuf.st_size;
+ } else {
+ current_alloc += insize;
+ }
+
+ if ((new_buffer = realloc (read_buffer, current_alloc)) == NULL) {
+ exit_err ("realloc error");
+ }
+
+ read_buffer = new_buffer;
+
+ return read_buffer;
+}
+
+/* Forward declaration of refuse_all_incr () */
+static void
+refuse_all_incr (void);
+
+/*
+ * handle_x_errors ()
+ *
+ * XError handler.
+ */
+static int
+handle_x_errors (Display * display, XErrorEvent * eev)
+{
+ char err_buf[MAXLINE];
+
+ /* Make sure to send a refusal to all waiting INCR requests
+ * and delete the corresponding properties. */
+ if (eev->error_code == BadAlloc) refuse_all_incr ();
+
+ XGetErrorText (display, eev->error_code, err_buf, MAXLINE);
+ exit_err (err_buf);
+
+ return 0;
+}
+
+/*
+ * clear_selection (selection)
+ *
+ * Clears the specified X selection 'selection'. This requests that no
+ * process should own 'selection'; thus the X server will respond to
+ * SelectionRequests with an empty property and we don't need to leave
+ * a daemon hanging around to service this selection.
+ */
+static void
+clear_selection (Atom selection)
+{
+ XSetSelectionOwner (display, selection, None, timestamp);
+ /* Call XSync to ensure this operation completes before program
+ * termination, especially if this is all we are doing. */
+ XSync (display, False);
+}
+
+/*
+ * own_selection (selection)
+ *
+ * Requests ownership of the X selection. Returns True if ownership was
+ * granted, and False otherwise.
+ */
+static Boolean
+own_selection (Atom selection)
+{
+ Window owner;
+
+ XSetSelectionOwner (display, selection, window, timestamp);
+ /* XGetSelectionOwner does a round trip to the X server, so there is
+ * no need to call XSync here. */
+ owner = XGetSelectionOwner (display, selection);
+ if (owner != window) {
+ return False;
+ } else {
+ XSetErrorHandler (handle_x_errors);
+ return True;
+ }
+}
+
+
+static IncrTrack * incrtrack_list = NULL;
+
+/*
+ * add_incrtrack (it)
+ *
+ * Add 'it' to the head of incrtrack_list.
+ */
+static void
+add_incrtrack (IncrTrack * it)
+{
+ if (incrtrack_list) {
+ incrtrack_list->prev = it;
+ }
+ it->prev = NULL;
+ it->next = incrtrack_list;
+ incrtrack_list = it;
+}
+
+/*
+ * remove_incrtrack (it)
+ *
+ * Remove 'it' from incrtrack_list.
+ */
+static void
+remove_incrtrack (IncrTrack * it)
+{
+ if (it->prev) {
+ it->prev->next = it->next;
+ }
+ if (it->next) {
+ it->next->prev = it->prev;
+ }
+
+ if (incrtrack_list == it) {
+ incrtrack_list = it->next;
+ }
+}
+
+/*
+ * fresh_incrtrack ()
+ *
+ * Create a new incrtrack, and add it to incrtrack_list.
+ */
+static IncrTrack *
+fresh_incrtrack (void)
+{
+ IncrTrack * it;
+
+ it = xs_malloc (sizeof (IncrTrack));
+ add_incrtrack (it);
+
+ return it;
+}
+
+/*
+ * trash_incrtrack (it)
+ *
+ * Remove 'it' from incrtrack_list, and free it.
+ */
+static void
+trash_incrtrack (IncrTrack * it)
+{
+ remove_incrtrack (it);
+ free (it);
+}
+
+/*
+ * find_incrtrack (atom)
+ *
+ * Find the IncrTrack structure within incrtrack_list pertaining to 'atom',
+ * if it exists.
+ */
+static IncrTrack *
+find_incrtrack (Atom atom)
+{
+ IncrTrack * iti;
+
+ for (iti = incrtrack_list; iti; iti = iti->next) {
+ if (atom == iti->property) return iti;
+ }
+
+ return NULL;
+}
+
+/* Forward declaration of handle_multiple() */
+static HandleResult
+handle_multiple (Display * display, Window requestor, Atom property,
+ unsigned char * sel, Atom selection, Time time,
+ MultTrack * mparent);
+
+/* Forward declaration of process_multiple() */
+static HandleResult
+process_multiple (MultTrack * mt, Boolean do_parent);
+
+/*
+ * confirm_incr (it)
+ *
+ * Confirm the selection request of ITER tracked by 'it'.
+ */
+static void
+notify_incr (IncrTrack * it, HandleResult hr)
+{
+ XSelectionEvent ev;
+
+ /* Call XSync here to make sure any BadAlloc errors are caught before
+ * confirming the conversion. */
+ XSync (it->display, False);
+
+ print_debug (D_TRACE, "Confirming conversion");
+
+ /* Prepare a SelectionNotify event to send, placing the selection in the
+ * requested property. */
+ ev.type = SelectionNotify;
+ ev.display = it->display;
+ ev.requestor = it->requestor;
+ ev.selection = it->selection;
+ ev.time = it->time;
+ ev.target = it->target;
+
+ if (hr & HANDLE_ERR) ev.property = None;
+ else ev.property = it->property;
+
+ XSendEvent (display, ev.requestor, False,
+ (unsigned long)NULL, (XEvent *)&ev);
+}
+
+/*
+ * refuse_all_incr ()
+ *
+ * Refuse all INCR transfers in progress. ASSUMES that this is called in
+ * response to an error, and that the program is about to bail out;
+ * ie. incr_track is not cleaned out.
+ */
+static void
+refuse_all_incr (void)
+{
+ IncrTrack * it;
+
+ for (it = incrtrack_list; it; it = it->next) {
+ XDeleteProperty (it->display, it->requestor, it->property);
+ notify_incr (it, HANDLE_ERR);
+ /* Don't bother trashing and list-removing these; we are about to
+ * bail out anyway. */
+ }
+}
+
+/*
+ * complete_incr (it)
+ *
+ * Finish off an INCR retrieval. If it was part of a multiple, continue
+ * that; otherwise, send confirmation that this completed.
+ */
+static void
+complete_incr (IncrTrack * it, HandleResult hr)
+{
+ MultTrack * mparent = it->mparent;
+
+ if (mparent) {
+ trash_incrtrack (it);
+ process_multiple (mparent, True);
+ } else {
+ notify_incr (it, hr);
+ trash_incrtrack (it);
+ }
+}
+
+/*
+ * notify_multiple (mt, hr)
+ *
+ * Confirm the selection request initiated with MULTIPLE tracked by 'mt'.
+ */
+static void
+notify_multiple (MultTrack * mt, HandleResult hr)
+{
+ XSelectionEvent ev;
+
+ /* Call XSync here to make sure any BadAlloc errors are caught before
+ * confirming the conversion. */
+ XSync (mt->display, False);
+
+ /* Prepare a SelectionNotify event to send, placing the selection in the
+ * requested property. */
+ ev.type = SelectionNotify;
+ ev.display = mt->display;
+ ev.requestor = mt->requestor;
+ ev.selection = mt->selection;
+ ev.time = mt->time;
+ ev.target = multiple_atom;
+
+ if (hr & HANDLE_ERR) ev.property = None;
+ else ev.property = mt->property;
+
+ XSendEvent (display, ev.requestor, False,
+ (unsigned long)NULL, (XEvent *)&ev);
+}
+
+/*
+ * complete_multiple (mt, do_parent, hr)
+ *
+ * Complete a MULTIPLE transfer. Iterate to its parent MULTIPLE if
+ * 'do_parent' is true. If there is not parent MULTIPLE, send notification
+ * of its completion with status 'hr'.
+ */
+static void
+complete_multiple (MultTrack * mt, Boolean do_parent, HandleResult hr)
+{
+ MultTrack * mparent = mt->mparent;
+
+ if (mparent) {
+ free (mt);
+ if (do_parent) process_multiple (mparent, True);
+ } else {
+ notify_multiple (mt, hr);
+ free (mt);
+ }
+}
+
+/*
+ * change_property (display, requestor, property, target, format, mode,
+ * data, nelements)
+ *
+ * Wrapper to XChangeProperty that performs INCR transfer if required and
+ * returns status of entire transfer.
+ */
+static HandleResult
+change_property (Display * display, Window requestor, Atom property,
+ Atom target, int format, int mode,
+ unsigned char * data, int nelements,
+ Atom selection, Time time, MultTrack * mparent)
+{
+ XSelectionEvent ev;
+ int nr_bytes;
+ IncrTrack * it;
+
+ print_debug (D_TRACE, "change_property ()");
+
+ nr_bytes = nelements * format / 8;
+
+ if (nr_bytes <= max_req) {
+ print_debug (D_TRACE, "data within maximum request size");
+ XChangeProperty (display, requestor, property, target, format, mode,
+ data, nelements);
+
+ return HANDLE_OK;
+ }
+
+ /* else */
+ print_debug (D_TRACE, "large data transfer");
+
+
+ /* Send a SelectionNotify event of type INCR */
+ ev.type = SelectionNotify;
+ ev.display = display;
+ ev.requestor = requestor;
+ ev.selection = selection;
+ ev.time = time;
+ ev.target = incr_atom; /* INCR */
+ ev.property = property;
+
+ XSelectInput (ev.display, ev.requestor, PropertyChangeMask);
+
+ XChangeProperty (ev.display, ev.requestor, ev.property, incr_atom, 32,
+ PropModeReplace, (unsigned char *)&nr_bytes, 1);
+
+ XSendEvent (display, requestor, False,
+ (unsigned long)NULL, (XEvent *)&ev);
+
+ /* Set up the IncrTrack to track this */
+ it = fresh_incrtrack ();
+
+ it->mparent = mparent;
+ it->state = S_INCR_1;
+ it->display = display;
+ it->requestor = requestor;
+ it->property = property;
+ it->selection = selection;
+ it->time = time;
+ it->target = target;
+ it->format = format;
+ it->data = data;
+ it->nelements = nelements;
+ it->offset = 0;
+
+ /* Maximum nr. of elements that can be transferred in one go */
+ it->max_elements = max_req * 8 / format;
+
+ /* Nr. of elements to transfer in this instance */
+ it->chunk = MIN (it->max_elements, it->nelements - it->offset);
+
+ /* Wait for that property to get deleted */
+ print_debug (D_TRACE, "Waiting on intial property deletion (%s)",
+ get_atom_name (it->property));
+
+ return HANDLE_INCOMPLETE;
+}
+
+static HandleResult
+incr_stage_1 (IncrTrack * it)
+{
+ /* First pass: PropModeReplace, from data, size chunk */
+ print_debug (D_TRACE, "Writing first chunk (%d bytes) (target 0x%x %s) to property 0x%x of requestor 0x%x", it->chunk, it->target, get_atom_name(it->target), it->property, it->requestor);
+ XChangeProperty (it->display, it->requestor, it->property, it->target,
+ it->format, PropModeReplace, it->data, it->chunk);
+
+ it->offset += it->chunk;
+
+ /* wait for PropertyNotify events */
+ print_debug (D_TRACE, "Waiting on subsequent deletions ...");
+
+ it->state = S_INCR_2;
+
+ return HANDLE_INCOMPLETE;
+}
+
+static HandleResult
+incr_stage_2 (IncrTrack * it)
+{
+ it->chunk = MIN (it->max_elements, it->nelements - it->offset);
+
+ if (it->chunk <= 0) {
+
+ /* Now write zero-length data to the property */
+ XChangeProperty (it->display, it->requestor, it->property, it->target,
+ it->format, PropModeAppend, NULL, 0);
+ it->state = S_NULL;
+ print_debug (D_TRACE, "Set si to state S_NULL");
+ return HANDLE_OK;
+ } else {
+ print_debug (D_TRACE, "Writing chunk (%d bytes) to property",
+ it->chunk);
+ XChangeProperty (it->display, it->requestor, it->property, it->target,
+ it->format, PropModeAppend, it->data+it->offset,
+ it->chunk);
+ it->offset += it->chunk;
+ print_debug (D_TRACE, "%d bytes remaining",
+ it->nelements - it->offset);
+ return HANDLE_INCOMPLETE;
+ }
+}
+
+
+/*
+ * handle_timestamp (display, requestor, property)
+ *
+ * Handle a TIMESTAMP request.
+ */
+static HandleResult
+handle_timestamp (Display * display, Window requestor, Atom property,
+ Atom selection, Time time, MultTrack * mparent)
+{
+ return
+ change_property (display, requestor, property, XA_INTEGER, 32,
+ PropModeReplace, (unsigned char *)×tamp, 1,
+ selection, time, mparent);
+}
+
+/*
+ * handle_targets (display, requestor, property)
+ *
+ * Handle a TARGETS request.
+ */
+static HandleResult
+handle_targets (Display * display, Window requestor, Atom property,
+ Atom selection, Time time, MultTrack * mparent)
+{
+ Atom * targets_cpy;
+
+ targets_cpy = malloc (sizeof (supported_targets));
+ memcpy (targets_cpy, supported_targets, sizeof (supported_targets));
+
+ return
+ change_property (display, requestor, property, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *)targets_cpy,
+ NUM_TARGETS, selection, time, mparent);
+}
+
+/*
+ * handle_string (display, requestor, property, sel)
+ *
+ * Handle a STRING request; setting 'sel' as the data
+ */
+static HandleResult
+handle_string (Display * display, Window requestor, Atom property,
+ unsigned char * sel, Atom selection, Time time,
+ MultTrack * mparent)
+{
+ return
+ change_property (display, requestor, property, XA_STRING, 8,
+ PropModeReplace, sel, strlen(sel),
+ selection, time, mparent);
+}
+
+/*
+ * handle_delete (display, requestor, property)
+ *
+ * Handle a DELETE request.
+ */
+static HandleResult
+handle_delete (Display * display, Window requestor, Atom property)
+{
+ XChangeProperty (display, requestor, property, null_atom, 0,
+ PropModeReplace, NULL, 0);
+
+ return DID_DELETE;
+}
+
+/*
+ * process_multiple (mt, do_parent)
+ *
+ * Iterate through a MultTrack until it completes, or until one of its
+ * entries initiates an interated selection.
+ *
+ * If 'do_parent' is true, and the actions proscribed in 'mt' are
+ * completed during the course of this call, then process_multiple
+ * is iteratively called on mt->mparent.
+ */
+static HandleResult
+process_multiple (MultTrack * mt, Boolean do_parent)
+{
+ HandleResult retval = HANDLE_OK;
+ int i;
+
+ if (!mt) return retval;
+
+ for (; mt->index < mt->length; mt->index += 2) {
+ i = mt->index;
+ if (mt->atoms[i] == timestamp_atom) {
+ retval |= handle_timestamp (mt->display, mt->requestor, mt->atoms[i+1],
+ mt->selection, mt->time, mt);
+ } else if (mt->atoms[i] == targets_atom) {
+ retval |= handle_targets (mt->display, mt->requestor, mt->atoms[i+1],
+ mt->selection, mt->time, mt);
+ } else if (mt->atoms[i] == multiple_atom) {
+ retval |= handle_multiple (mt->display, mt->requestor, mt->atoms[i+1],
+ mt->sel, mt->selection, mt->time, mt);
+ } else if (mt->atoms[i] == XA_STRING || mt->atoms[i] == text_atom) {
+ retval |= handle_string (mt->display, mt->requestor, mt->atoms[i+1],
+ mt->sel, mt->selection, mt->time, mt);
+ } else if (mt->atoms[i] == delete_atom) {
+ retval |= handle_delete (mt->display, mt->requestor, mt->atoms[i+1]);
+ } else if (mt->atoms[i] == None) {
+ /* the only other thing we know to handle is None, for which we
+ * do nothing. This block is, like, __so__ redundant. Welcome to
+ * Over-engineering 101 :) This comment is just here to keep the
+ * logic documented and separate from the 'else' block. */
+ } else {
+ /* for anything we don't know how to handle, we fail the conversion
+ * by setting this: */
+ mt->atoms[i] = None;
+ }
+
+ /* If any of the conversions failed, signify this by setting that
+ * atom to None ...*/
+ if (retval & HANDLE_ERR) {
+ mt->atoms[i] = None;
+ }
+ /* ... but don't propogate HANDLE_ERR */
+ retval &= (~HANDLE_ERR);
+
+ if (retval & HANDLE_INCOMPLETE) break;
+ }
+
+ if ((retval & HANDLE_INCOMPLETE) == 0) {
+ complete_multiple (mt, do_parent, retval);
+ }
+
+ return retval;
+}
+
+/*
+ * continue_incr (it)
+ *
+ * Continue an incremental transfer of IncrTrack * it.
+ *
+ * NB. If the incremental transfer was part of a multiple request, this
+ * function calls process_multiple with do_parent=True because it is
+ * assumed we are continuing an interrupted ITER, thus we must continue
+ * the multiple as its original handler did not complete.
+ */
+static HandleResult
+continue_incr (IncrTrack * it)
+{
+ HandleResult retval = HANDLE_OK;
+
+ if (it->state == S_INCR_1) {
+ retval = incr_stage_1 (it);
+ } else if (it->state == S_INCR_2) {
+ retval = incr_stage_2 (it);
+ }
+
+ /* If that completed the INCR, deal with completion */
+ if ((retval & HANDLE_INCOMPLETE) == 0) {
+ complete_incr (it, retval);
+ }
+
+ return retval;
+}
+
+/*
+ * handle_multiple (display, requestor, property, sel, selection, time)
+ *
+ * Handle a MULTIPLE request; possibly setting 'sel' if any STRING
+ * requests are processed within it. Return value has DID_DELETE bit set
+ * if any delete requests are processed.
+ *
+ * NB. This calls process_multiple with do_parent=False because it is
+ * assumed we are "handling" the multiple request on behalf of a
+ * multiple already in progress, or (more likely) directly off a
+ * SelectionRequest event.
+ */
+static HandleResult
+handle_multiple (Display * display, Window requestor, Atom property,
+ unsigned char * sel, Atom selection, Time time,
+ MultTrack * mparent)
+{
+ MultTrack * mt;
+ int format;
+ unsigned long bytesafter;
+ HandleResult retval = HANDLE_OK;
+
+ mt = xs_malloc (sizeof (MultTrack));
+
+ XGetWindowProperty (display, requestor, property, 0L, 1000000,
+ False, (Atom)AnyPropertyType, &mt->property,
+ &format, &mt->length, &bytesafter,
+ (unsigned char **)&mt->atoms);
+
+ /* Make sure we got the Atom list we want */
+ if (format != 32) return HANDLE_OK;
+
+
+ mt->mparent = mparent;
+ mt->display = display;
+ mt->requestor = requestor;
+ mt->sel = sel;
+ mt->selection = selection;
+ mt->time = time;
+ mt->index = 0;
+
+ retval = process_multiple (mt, False);
+
+ return retval;
+}
+
+/*
+ * handle_selection_request (event, sel)
+ *
+ * Processes a SelectionRequest event 'event' and replies to its
+ * sender appropriately, eg. with the contents of the string 'sel'.
+ * Returns False if a DELETE request is processed, indicating to
+ * the calling function to delete the corresponding selection.
+ * Returns True otherwise.
+ */
+static Boolean
+handle_selection_request (XEvent event, unsigned char * sel)
+{
+ XSelectionRequestEvent * xsr = &event.xselectionrequest;
+ XSelectionEvent ev;
+ HandleResult hr = HANDLE_OK;
+ Boolean retval = True;
+
+ print_debug (D_TRACE, "handle_selection_request, property=0x%x (%s), target=0x%x (%s)",
+ xsr->property, get_atom_name (xsr->property),
+ xsr->target, get_atom_name (xsr->target));
+
+ /* Prepare a SelectionNotify event to send, either as confirmation of
+ * placing the selection in the requested property, or as notification
+ * that this could not be performed. */
+ ev.type = SelectionNotify;
+ ev.display = xsr->display;
+ ev.requestor = xsr->requestor;
+ ev.selection = xsr->selection;
+ ev.time = xsr->time;
+ ev.target = xsr->target;
+
+ if (xsr->property == None && ev.target != multiple_atom) {
+ /* Obsolete requestor */
+ xsr->property = xsr->target;
+ }
+
+ if (ev.time != CurrentTime && ev.time < timestamp) {
+ /* If the time is outside the period we have owned the selection,
+ * which is any time later than timestamp, or if the requested target
+ * is not a string, then refuse the SelectionRequest. NB. Some broken
+ * clients don't set a valid timestamp, so we have to check against
+ * CurrentTime here. */
+ ev.property = None;
+ } else if (ev.target == timestamp_atom) {
+ /* Return timestamp used to acquire ownership if target is TIMESTAMP */
+ ev.property = xsr->property;
+ hr = handle_timestamp (ev.display, ev.requestor, ev.property,
+ ev.selection, ev.time, NULL);
+ } else if (ev.target == targets_atom) {
+ /* Return a list of supported targets (TARGETS)*/
+ ev.property = xsr->property;
+ hr = handle_targets (ev.display, ev.requestor, ev.property,
+ ev.selection, ev.time, NULL);
+ } else if (ev.target == multiple_atom) {
+ if (xsr->property == None) { /* Invalid MULTIPLE request */
+ ev.property = None;
+ } else {
+ /* Handle MULTIPLE request */
+ hr = handle_multiple (ev.display, ev.requestor, ev.property, sel,
+ ev.selection, ev.time, NULL);
+ }
+ } else if (ev.target == XA_STRING || ev.target == text_atom) {
+ /* Received STRING or TEXT request */
+ ev.property = xsr->property;
+ hr = handle_string (ev.display, ev.requestor, ev.property, sel,
+ ev.selection, ev.time, NULL);
+ } else if (ev.target == delete_atom) {
+ /* Received DELETE request */
+ ev.property = xsr->property;
+ hr = handle_delete (ev.display, ev.requestor, ev.property);
+ retval = False;
+ } else {
+ /* Cannot convert to requested target. This includes most non-string
+ * datatypes, and INSERT_SELECTION, INSERT_PROPERTY */
+ ev.property = None;
+ }
+
+ /* Return False if a DELETE was processed */
+ retval = (hr & DID_DELETE) ? False : True;
+
+ /* If there was an error in the transfer, it should be refused */
+ if (hr & HANDLE_ERR) {
+ print_debug (D_TRACE, "Error in transfer");
+ ev.property = None;
+ }
+
+ if ((hr & HANDLE_INCOMPLETE) == 0) {
+ if (ev.property == None) {print_debug (D_TRACE, "Refusing conversion");}
+ else { print_debug (D_TRACE, "Confirming conversion");}
+
+ XSendEvent (display, ev.requestor, False,
+ (unsigned long)NULL, (XEvent *)&ev);
+
+ /* If we return False here, we may quit immediately, so sync out the
+ * X queue. */
+ if (!retval) XSync (display, False);
+ }
+
+ return retval;
+}
+
+/*
+ * set_selection (selection, sel)
+ *
+ * Takes ownership of the selection 'selection', then loops waiting for
+ * its SelectionClear or SelectionRequest events.
+ *
+ * Handles SelectionRequest events, first checking for additional
+ * input if the user has specified 'follow' mode. Returns when a
+ * SelectionClear event is received for the specified selection.
+ */
+static void
+set_selection (Atom selection, unsigned char * sel)
+{
+ XEvent event;
+ IncrTrack * it;
+
+ if (own_selection (selection) == False) return;
+
+ for (;;) {
+ XNextEvent (display, &event);
+
+ switch (event.type) {
+ case SelectionClear:
+ if (event.xselectionclear.selection == selection) return;
+ break;
+ case SelectionRequest:
+ if (event.xselectionrequest.selection != selection) break;
+
+ if (do_follow)
+ sel = read_input (sel, True);
+
+ if (!handle_selection_request (event, sel)) return;
+
+ break;
+ case PropertyNotify:
+ if (event.xproperty.state != PropertyDelete) break;
+
+ it = find_incrtrack (event.xproperty.atom);
+
+ if (it != NULL) {
+ continue_incr (it);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * set_selection__daemon (selection, sel)
+ *
+ * Creates a daemon process to handle selection requests for the
+ * specified selection 'selection', to respond with selection text 'sel'.
+ * If 'sel' is an empty string (NULL or "") then no daemon process is
+ * created and the specified selection is cleared instead.
+ */
+static void
+set_selection__daemon (Atom selection, unsigned char * sel)
+{
+ if (empty_string (sel)) {
+ clear_selection (selection);
+ return;
+ }
+
+ become_daemon ();
+
+ set_selection (selection, sel);
+}
+
+/*
+ * set_selection_pair (sel_p, sel_s)
+ *
+ * Handles SelectionClear and SelectionRequest events for both the
+ * primary and secondary selections. Returns once SelectionClear events
+ * have been received for both selections. Responds to SelectionRequest
+ * events for the primary selection with text 'sel_p' and for the
+ * secondary selection with text 'sel_s'.
+ */
+static void
+set_selection_pair (unsigned char * sel_p, unsigned char * sel_s)
+{
+ XEvent event;
+ IncrTrack * it;
+
+ if (sel_p) {
+ if (own_selection (XA_PRIMARY) == False)
+ free_string (sel_p);
+ } else {
+ clear_selection (XA_PRIMARY);
+ }
+
+ if (sel_s) {
+ if (own_selection (XA_SECONDARY) == False)
+ free_string (sel_s);
+ } else {
+ clear_selection (XA_SECONDARY);
+ }
+
+ for (;;) {
+ XNextEvent (display, &event);
+
+ switch (event.type) {
+ case SelectionClear:
+ if (event.xselectionclear.selection == XA_PRIMARY) {
+ free_string (sel_p);
+ if (sel_s == NULL) return;
+ } else if (event.xselectionclear.selection == XA_SECONDARY) {
+ free_string (sel_s);
+ if (sel_p == NULL) return;
+ }
+ break;
+ case SelectionRequest:
+ if (event.xselectionrequest.selection == XA_PRIMARY) {
+ if (!handle_selection_request (event, sel_p)) {
+ free_string (sel_p);
+ if (sel_s == NULL) return;
+ }
+ } else if (event.xselectionrequest.selection == XA_SECONDARY) {
+ if (!handle_selection_request (event, sel_s)) {
+ free_string (sel_s);
+ if (sel_p == NULL) return;
+ }
+ }
+ break;
+ case PropertyNotify:
+ if (event.xproperty.state != PropertyDelete) break;
+
+ it = find_incrtrack (event.xproperty.atom);
+
+ if (it != NULL) {
+ continue_incr (it);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * set_selection_pair__daemon (sel_p, sel_s)
+ *
+ * Creates a daemon process to handle selection requests for both the
+ * primary and secondary selections with texts 'sel_p' and 'sel_s'
+ * respectively.
+ *
+ * If both 'sel_p' and 'sel_s' are empty strings (NULL or "") then no
+ * daemon process is created, and both selections are cleared instead.
+ */
+static void
+set_selection_pair__daemon (unsigned char * sel_p, unsigned char * sel_s)
+{
+ if (empty_string (sel_p) && empty_string (sel_s)) {
+ clear_selection (XA_PRIMARY);
+ clear_selection (XA_SECONDARY);
+ return;
+ }
+
+ become_daemon ();
+
+ set_selection_pair (sel_p, sel_s);
+}
+
+/*
+ * keep_selections ()
+ *
+ * Takes ownership of both the primary and secondary selections. The current
+ * selection texts are retrieved and a new daemon process is created to
+ * handle both selections unmodified.
+ */
+static void
+keep_selections (void)
+{
+ unsigned char * text1, * text2;
+
+ text1 = get_selection (XA_PRIMARY, XA_STRING);
+ text2 = get_selection (XA_SECONDARY, XA_STRING);
+
+ set_selection_pair__daemon (text1, text2);
+}
+
+/*
+ * exchange_selections ()
+ *
+ * Exchanges the primary and secondary selections. The current selection
+ * texts are retrieved and a new daemon process is created to handle both
+ * selections with their texts exchanged.
+ */
+static void
+exchange_selections (void)
+{
+ unsigned char * text1, * text2;
+
+ text1 = get_selection (XA_PRIMARY, XA_STRING);
+ text2 = get_selection (XA_SECONDARY, XA_STRING);
+
+ set_selection_pair__daemon (text2, text1);
+}
+
+
+/*
+ * main (argc, argv)
+ * =================
+ *
+ * Parse user options and set behaviour.
+ *
+ * By default the current selection is output and not modified if both
+ * standard input and standard output are terminals (ttys). Otherwise,
+ * the current selection is output if standard output is not a terminal
+ * (tty), and the selection is set from standard input if standard input
+ * is not a terminal (tty). If any input or output options are given then
+ * the program behaves only in the requested mode.
+ *
+ * If both input and output is required then the previous selection is
+ * output before being replaced by the contents of standard input.
+ */
+int
+main(int argc, char *argv[])
+{
+ Boolean show_version = False;
+ Boolean show_help = False;
+ Boolean do_append = False, do_clear = False;
+ Boolean do_keep = False, do_exchange = False;
+ Boolean do_input = False, do_output = False;
+ Boolean dont_input = True, dont_output = False;
+ Boolean want_clipboard = False, do_delete = False;
+ Window root;
+ Atom selection = XA_PRIMARY, test_atom;
+ int black;
+ int i, s=0;
+ unsigned char * old_sel = NULL, * new_sel = NULL;
+ char * display_name = NULL;
+ long timeout_ms = 0L;
+
+ progname = argv[0];
+
+ /* Specify default behaviour based on input and output file types */
+ if (isatty(0) && isatty(1)) {
+ do_input = False; dont_input = True;
+ do_output = False; dont_output = False;
+ } else {
+ do_input = !isatty(0); dont_input = !do_input;
+ do_output = !isatty(1); dont_output = !do_output;
+ }
+
+#define OPT(s) (strcmp (argv[i], (s)) == 0)
+
+ /* Parse options; modify behaviour according to user-specified options */
+ for (i=1; i < argc; i++) {
+ if (OPT("--help") || OPT("-h")) {
+ show_help = True;
+ } else if (OPT("--version")) {
+ show_version = True;
+ } else if (OPT("--verbose") || OPT("-v")) {
+ debug_level++;
+ } else if (OPT("--append") || OPT("-a")) {
+ do_append = True;
+ dont_output = True;
+ } else if (OPT("--input") || OPT("-i")) {
+ do_input = True;
+ dont_output = True;
+ } else if (OPT("--clear") || OPT("-c")) {
+ do_clear = True;
+ dont_output = True;
+ } else if (OPT("--output") || OPT("-o")) {
+ do_output = True;
+ dont_input = True;
+ } else if (OPT("--follow") || OPT("-f")) {
+ do_follow = True;
+ dont_output = True;
+ } else if (OPT("--primary") || OPT("-p")) {
+ selection = XA_PRIMARY;
+ } else if (OPT("--secondary") || OPT("-s")) {
+ selection = XA_SECONDARY;
+ } else if (OPT("--clipboard") || OPT("-b")) {
+ want_clipboard = True;
+ } else if (OPT("--keep") || OPT("-k")) {
+ do_keep = True;
+ } else if (OPT("--exchange") || OPT("-x")) {
+ do_exchange = True;
+ } else if (OPT("--display")) {
+ i++; if (i >= argc) goto usage_err;
+ display_name = argv[i];
+ } else if (OPT("--selectionTimeout") || OPT("-t")) {
+ i++; if (i >= argc) goto usage_err;
+ timeout_ms = strtol(argv[i], (char **)NULL, 10);
+ if (timeout_ms < 0) timeout_ms = 0;
+ } else if (OPT("--nodetach") || OPT("-n")) {
+ no_daemon = True;
+ } else if (OPT("--delete") || OPT("-d")) {
+ do_delete = True;
+ dont_output = True;
+ } else if (OPT("--logfile") || OPT("-l")) {
+ i++; if (i >= argc) goto usage_err;
+ strncpy (logfile, argv[i], MAXFNAME);
+ } else {
+ goto usage_err;
+ }
+ }
+
+ if (show_version) {
+ printf ("xsel version " VERSION " by " AUTHOR "\n");
+ }
+
+ if (show_help) {
+ usage ();
+ }
+
+ if (show_version || show_help) {
+ exit (0);
+ }
+
+ if (fstat (0, &in_statbuf) == -1) {
+ exit_err ("fstat error on stdin");
+ }
+ if (fstat (1, &out_statbuf) == -1) {
+ exit_err ("fstat error on stdout");
+ }
+
+ if (S_ISDIR(in_statbuf.st_mode)) {
+ exit_err ("-: Is a directory\n");
+ }
+ if (S_ISDIR(out_statbuf.st_mode)) {
+ exit_err ("stdout: Is a directory\n");
+ }
+
+ timeout = timeout_ms * 1000;
+
+ display = XOpenDisplay (display_name);
+ if (display==NULL) {
+ exit_err ("Can't open display: %s\n", display_name);
+ }
+ root = XDefaultRootWindow (display);
+
+ /* Create an unmapped window for receiving events */
+ black = BlackPixel (display, DefaultScreen (display));
+ window = XCreateSimpleWindow (display, root, 0, 0, 1, 1, 0, black, black);
+
+ print_debug (D_INFO, "Window id: 0x%x (unmapped)", window);
+
+ /* Get a timestamp */
+ XSelectInput (display, window, PropertyChangeMask);
+ timestamp = get_timestamp ();
+
+ print_debug (D_OBSC, "Timestamp: %lu", timestamp);
+
+ /* Get the maximum incremental selection size in bytes */
+ /*max_req = MAX_SELECTION_INCR (display);*/
+ max_req = 4000;
+
+ print_debug (D_OBSC, "Maximum request size: %ld bytes", max_req);
+
+ /* Consistency check */
+ test_atom = XInternAtom (display, "PRIMARY", False);
+ if (test_atom != XA_PRIMARY)
+ print_debug (D_WARN, "XA_PRIMARY not named \"PRIMARY\"\n");
+ test_atom = XInternAtom (display, "SECONDARY", False);
+ if (test_atom != XA_SECONDARY)
+ print_debug (D_WARN, "XA_SECONDARY not named \"SECONDARY\"\n");
+
+ /* Get the TIMESTAMP atom */
+ timestamp_atom = XInternAtom (display, "TIMESTAMP", False);
+ supported_targets[s++] = timestamp_atom;
+
+ /* Get the MULTIPLE atom */
+ multiple_atom = XInternAtom (display, "MULTIPLE", False);
+ supported_targets[s++] = multiple_atom;
+
+ /* Get the TARGETS atom */
+ targets_atom = XInternAtom (display, "TARGETS", False);
+ supported_targets[s++] = targets_atom;
+
+ /* Get the DELETE atom */
+ delete_atom = XInternAtom (display, "DELETE", False);
+ supported_targets[s++] = delete_atom;
+
+ /* Get the INCR atom */
+ incr_atom = XInternAtom (display, "INCR", False);
+ supported_targets[s++] = incr_atom;
+
+ /* Get the NULL atom */
+ null_atom = XInternAtom (display, "NULL", False);
+
+ /* Get the TEXT atom */
+ text_atom = XInternAtom (display, "TEXT", False);
+ supported_targets[s++] = text_atom;
+
+ supported_targets[s++] = XA_STRING;
+
+ /* handle selection keeping and exit if so */
+ if (do_keep) {
+ keep_selections ();
+ _exit (0);
+ }
+
+ /* handle selection exchange and exit if so */
+ if (do_exchange) {
+ exchange_selections ();
+ _exit (0);
+ }
+
+ /* Find the "CLIPBOARD" selection if required */
+ if (want_clipboard) {
+ selection = XInternAtom (display, "CLIPBOARD", False);
+ }
+
+ /* handle output modes */
+ if (do_output || !dont_output) {
+ /* Get the current selection */
+ old_sel = get_selection (selection, XA_STRING);
+ if (old_sel) printf ("%s", old_sel);
+ }
+
+ /* handle input and clear modes */
+ if (do_delete) {
+ get_selection (selection, delete_atom);
+ } else if (do_clear) {
+ clear_selection (selection);
+ }
+ else if (do_input || !dont_input) {
+ if (do_append) {
+ if (!old_sel) old_sel = get_selection (selection, XA_STRING);
+ new_sel = copy_sel (old_sel);
+ }
+ new_sel = initialise_read (new_sel);
+ new_sel = read_input (new_sel, False);
+ set_selection__daemon (selection, new_sel);
+ }
+
+ exit (0);
+
+usage_err:
+ usage ();
+ exit (0);
+}
diff --git a/xsel.h b/xsel.h
@@ -0,0 +1,99 @@
+/*
+ * xsel -- manipulate the X selection
+ * Copyright (C) 2001 Conrad Parker <conrad@vergenet.net>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#define VERSION "0.9"
+#define AUTHOR "Conrad Parker <conrad@vergenet.net>"
+
+/* Default debug level (ship at 0) */
+#define DEBUG_LEVEL 0
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#define empty_string(s) (s==NULL||s[0]=='\0')
+#define free_string(s) { free(s); s=NULL; }
+
+/* Maximum line length for error messages */
+#define MAXLINE 4096
+
+/* Maximum filename length */
+#define MAXFNAME 1024
+
+/* Maximum incremental selection size. (Ripped from Xt) */
+#define MAX_SELECTION_INCR(dpy) (((65536 < XMaxRequestSize(dpy)) ? \
+ (65536 << 2) : (XMaxRequestSize(dpy) << 2))-100)
+
+/*
+ * Debug levels (for print_debug()):
+ *
+ * 0 - Fatal errors (default/unmaskable)
+ * 1 - Non-fatal warning (essential debugging info)
+ * 2 - Informative (generally useful debugging info)
+ * 3 - Obscure (more detailed debugging info)
+ * 4 - Trace (sequential trace of progress)
+ */
+
+#define D_FATAL 0
+#define D_WARN 1
+#define D_INFO 2
+#define D_OBSC 3
+#define D_TRACE 4
+
+/* An instance of a MULTIPLE SelectionRequest being served */
+typedef struct _MultTrack MultTrack;
+
+struct _MultTrack {
+ MultTrack * mparent;
+ Display * display;
+ Window requestor;
+ Atom property;
+ Atom selection;
+ Time time;
+ Atom * atoms;
+ unsigned long length;
+ unsigned long index;
+ unsigned char * sel;
+};
+
+/* Selection serving states */
+typedef enum {
+ S_NULL=0,
+ S_INCR_1,
+ S_INCR_2
+} IncrState;
+
+/* An instance of a selection being served */
+typedef struct _IncrTrack IncrTrack;
+
+struct _IncrTrack {
+ MultTrack * mparent;
+ IncrTrack * prev, * next;
+ IncrState state;
+ Display * display;
+ Window requestor;
+ Atom property;
+ Atom selection;
+ Time time;
+ Atom target;
+ int format;
+ unsigned char * data;
+ int nelements; /* total */
+ int offset, chunk, max_elements; /* all in terms of nelements */
+};
+
+/* Status of request handling */
+typedef int HandleResult;
+#define HANDLE_OK 0
+#define HANDLE_ERR (1<<0)
+#define HANDLE_INCOMPLETE (1<<1)
+#define DID_DELETE (1<<2)