Introduction
Introduction Statistics Contact Development Disclaimer Help
Initial git import. - sam - An updated version of the sam text editor.
git clone git://vernunftzentrum.de/sam.git
Log
Files
Refs
LICENSE
---
commit f8d68c3f2e8aa987821894c9b82aa63bb411e714
Author: Rob King <[email protected]>
Date: Fri, 31 Jul 2015 23:02:18 -0500
Initial git import.
Diffstat:
LICENSE | 18 ++++++++++++++++++
Makefile | 40 +++++++++++++++++++++++++++++++
README | 118 +++++++++++++++++++++++++++++++
config.mk | 23 +++++++++++++++++++++++
doc/B | 43 ++++++++++++++++++++++++++++++
doc/Makefile | 18 ++++++++++++++++++
doc/keyboard | 206 +++++++++++++++++++++++++++++++
doc/sam.1 | 1027 +++++++++++++++++++++++++++++++
doc/sam.1.pdf | 0
doc/sam.ps | 8261 +++++++++++++++++++++++++++++++
doc/sam.tut.ms | 1776 ++++++++++++++++++++++++++++++
doc/se.ps | 1487 ++++++++++++++++++++++++++++++
include/frame.h | 72 +++++++++++++++++++++++++++++++
include/libc.h | 54 +++++++++++++++++++++++++++++++
include/libg.h | 225 +++++++++++++++++++++++++++++++
include/regexp.h | 65 +++++++++++++++++++++++++++++++
include/u.h | 117 +++++++++++++++++++++++++++++++
libXg/Gwin.h | 43 ++++++++++++++++++++++++++++++
libXg/GwinP.h | 45 +++++++++++++++++++++++++++++++
libXg/Makefile | 58 ++++++++++++++++++++++++++++++
libXg/arc.c | 45 +++++++++++++++++++++++++++++++
libXg/arith.c | 191 +++++++++++++++++++++++++++++++
libXg/balloc.c | 60 +++++++++++++++++++++++++++++++
libXg/bitblt.c | 59 +++++++++++++++++++++++++++++++
libXg/bitbltclip.c | 68 +++++++++++++++++++++++++++++++
libXg/border.c | 28 ++++++++++++++++++++++++++++
libXg/bscreenrect.c | 18 ++++++++++++++++++
libXg/circle.c | 23 +++++++++++++++++++++++
libXg/clipline.c | 225 +++++++++++++++++++++++++++++++
libXg/clipr.c | 21 +++++++++++++++++++++
libXg/copymasked.c | 49 +++++++++++++++++++++++++++++++
libXg/cursorset.c | 16 ++++++++++++++++
libXg/cursorswitch.c | 66 +++++++++++++++++++++++++++++++
libXg/disc.c | 23 +++++++++++++++++++++++
libXg/ellipse.c | 23 +++++++++++++++++++++++
libXg/font.c | 18 ++++++++++++++++++
libXg/gcs.c | 401 +++++++++++++++++++++++++++++++
libXg/getrect.c | 73 +++++++++++++++++++++++++++++++
libXg/gwin.c | 466 +++++++++++++++++++++++++++++++
libXg/latin1.c | 313 +++++++++++++++++++++++++++++++
libXg/ldconvert.c | 55 +++++++++++++++++++++++++++++++
libXg/libgint.h | 93 +++++++++++++++++++++++++++++++
libXg/menuhit.c | 236 +++++++++++++++++++++++++++++++
libXg/point.c | 21 +++++++++++++++++++++
libXg/polysegment.c | 27 +++++++++++++++++++++++++++
libXg/rdbitmap.c | 63 +++++++++++++++++++++++++++++++
libXg/rdbitmapfile.c | 65 +++++++++++++++++++++++++++++++
libXg/rectclip.c | 25 +++++++++++++++++++++++++
libXg/rune.c | 213 +++++++++++++++++++++++++++++++
libXg/segment.c | 25 +++++++++++++++++++++++++
libXg/string.c | 38 +++++++++++++++++++++++++++++++
libXg/strwidth.c | 23 +++++++++++++++++++++++
libXg/test.c | 237 +++++++++++++++++++++++++++++++
libXg/texture.c | 42 +++++++++++++++++++++++++++++++
libXg/wrbitmap.c | 55 +++++++++++++++++++++++++++++++
libXg/wrbitmapfile.c | 50 +++++++++++++++++++++++++++++++
libXg/xtbinit.c | 830 ++++++++++++++++++++++++++++++
libframe/Makefile | 47 +++++++++++++++++++++++++++++++
libframe/frbox.c | 156 +++++++++++++++++++++++++++++++
libframe/frdelete.c | 104 +++++++++++++++++++++++++++++++
libframe/frdraw.c | 59 +++++++++++++++++++++++++++++++
libframe/frinit.c | 42 +++++++++++++++++++++++++++++++
libframe/frinsert.c | 247 +++++++++++++++++++++++++++++++
libframe/frptofchar.c | 116 ++++++++++++++++++++++++++++++
libframe/frselect.c | 94 +++++++++++++++++++++++++++++++
libframe/frstr.c | 41 +++++++++++++++++++++++++++++++
libframe/frutil.c | 107 +++++++++++++++++++++++++++++++
libframe/misc.c | 93 +++++++++++++++++++++++++++++++
rsam/Makefile | 18 ++++++++++++++++++
rsam/rsam.c | 147 +++++++++++++++++++++++++++++++
sam/B.rc | 51 +++++++++++++++++++++++++++++++
sam/B.sh | 56 +++++++++++++++++++++++++++++++
sam/Makefile | 89 +++++++++++++++++++++++++++++++
sam/address.c | 241 +++++++++++++++++++++++++++++++
sam/buffer.c | 179 +++++++++++++++++++++++++++++++
sam/cmd.c | 591 +++++++++++++++++++++++++++++++
sam/disc.c | 335 +++++++++++++++++++++++++++++++
sam/error.c | 132 +++++++++++++++++++++++++++++++
sam/errors.h | 63 +++++++++++++++++++++++++++++++
sam/file.c | 465 +++++++++++++++++++++++++++++++
sam/io.c | 257 +++++++++++++++++++++++++++++++
sam/list.c | 48 +++++++++++++++++++++++++++++++
sam/mesg.c | 762 +++++++++++++++++++++++++++++++
sam/mesg.h | 102 +++++++++++++++++++++++++++++++
sam/moveto.c | 168 +++++++++++++++++++++++++++++++
sam/multi.c | 91 +++++++++++++++++++++++++++++++
sam/parse.h | 69 ++++++++++++++++++++++++++++++
sam/plan9.c | 174 +++++++++++++++++++++++++++++++
sam/rasp.c | 276 ++++++++++++++++++++++++++++++
sam/regexp.c | 820 ++++++++++++++++++++++++++++++
sam/sam.c | 682 +++++++++++++++++++++++++++++++
sam/sam.h | 408 +++++++++++++++++++++++++++++++
sam/samsave | 14 ++++++++++++++
sam/shell.c | 155 +++++++++++++++++++++++++++++++
sam/string.c | 179 +++++++++++++++++++++++++++++++
sam/sys.c | 61 +++++++++++++++++++++++++++++++
sam/unix.c | 220 +++++++++++++++++++++++++++++++
sam/xec.c | 492 +++++++++++++++++++++++++++++++
samterm/Makefile | 49 +++++++++++++++++++++++++++++++
samterm/flayer.c | 436 +++++++++++++++++++++++++++++++
samterm/flayer.h | 48 +++++++++++++++++++++++++++++++
samterm/icons.c | 54 +++++++++++++++++++++++++++++++
samterm/io.c | 204 +++++++++++++++++++++++++++++++
samterm/main.c | 592 +++++++++++++++++++++++++++++++
samterm/menu.c | 380 ++++++++++++++++++++++++++++++
samterm/mesg.c | 794 +++++++++++++++++++++++++++++++
samterm/plan9.c | 113 +++++++++++++++++++++++++++++++
samterm/rasp.c | 263 +++++++++++++++++++++++++++++++
samterm/samterm.h | 158 +++++++++++++++++++++++++++++++
samterm/scroll.c | 145 +++++++++++++++++++++++++++++++
samterm/unix.c | 159 +++++++++++++++++++++++++++++++
111 files changed, 29826 insertions(+), 0 deletions(-)
---
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,18 @@
+
+/*
+ * The authors of this software are Rob Pike and Howard Trickey.
+ * Copyright (c) 1998 by Lucent Technologies.
+ *
+ * Rob King made some changes.
+ * Copyright (c) 2015 by Rob King.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE …
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
diff --git a/Makefile b/Makefile
@@ -0,0 +1,40 @@
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+# Changes Copyright (c) 2014-2015 Rob King
+#
+# master makefile for sam. configure sub-makefiles first.
+#
+
+all: lXg lframe rsamdir samdir samtermdir docdir
+
+lXg:
+ cd libXg; $(MAKE)
+lframe:
+ cd libframe; $(MAKE)
+
+docdir:
+ cd doc; $(MAKE)
+
+rsamdir:
+ cd rsam; $(MAKE)
+
+samdir:
+ cd sam; $(MAKE)
+
+samtermdir:
+ cd samterm; $(MAKE)
+
+install:
+ cd libXg; $(MAKE) install
+ cd libframe; $(MAKE) install
+ cd sam; $(MAKE) install
+ cd samterm; $(MAKE) install
+ cd doc; $(MAKE) install
+ cd rsam; $(MAKE) install
+
+clean:
+ cd libXg; $(MAKE) clean
+ cd libframe; $(MAKE) clean
+ cd sam; $(MAKE) clean
+ cd samterm; $(MAKE) clean
+ cd rsam; $(MAKE) clean
+
diff --git a/README b/README
@@ -0,0 +1,118 @@
+
+ * The authors of this software are Rob Pike and Howard Trickey.
+ * Copyright (c) 1998 by Lucent Technologies.
+ *
+ * Rob King made some changes.
+ * Those changes, Copyright (c) 2014-2015 by Rob King.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE …
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+
+This is an X11 version of Rob Pike's editor, sam. Documentation describing
+its use and construction are provided in subdirectory doc. The file
+doc/sam.1 contains the manual page; doc/sam.1.pdf is a PDF version of that
+page. doc/sam.tut.ms is a tutorial that can be formatted with troff -ms.
+It substitutes Bold and Italics for the fonts named CW and CS; if your system
+has these fonts, remove the CW and CS macros at the beginning of the file. The
+files doc/sam.ps and doc/se.ps are postscript versions of published papers
+describing sam and structural regular expressions. These papers reflect
+sam's capabilities at the time of publication several years ago; while the
+general description remains accurate, some functions may have changed or been
+removed. The file doc/keyboard is an example of a Unicode input configuration
+file; it serves as a good starting point for a customized version (see the
+manual page for more information).
+
+Sam is composed of three programs: sam itself, which does the command
+processing and file manipulation; and samterm, which controls the display
+and interacts with the user. You can run sam on one machine and samterm on
+another connected via remote execution. A third program, rsam, is automatically
+interpolated between the terminal and the remote instance of sam by default
+to allow an additional control channel on the remote machine.
+
+This version of sam is based on the Plan 9 implementation. Its
+design and expression reflect the functionality of the Plan 9 environment;
+most notably, characters are represented internally by 16-bit values called
+Runes. Header files include/u.h and include/libc.h and source files
+libframe/misc.c, libXg/rune.c, sam/unix.c and samterm/unix.c contain
+code that insinuates sam into the Unix world. Two other files,
+sam/plan9.c and samterm/plan9.c, contain Plan 9-specific code; they
+are not used in the Unix version of sam and are provided as examples
+of the Plan 9 interface.
+
+The typedefs for uchar, ushort, and ulong defined in include/u.h, may conflict
+with exisiting definitions in include file <sys/types.h> on some systems.
+If this occurs, remove the offending definitions from include/u.h.
+
+The distribution consists of several directories:
+
+ sam - The source for sam. It loads against libXg to pick
+ up some utility routines.
+
+ samterm - The source for samterm. It loads against libframe,
+ libXg, and your local X11 libraries.
+
+ rsam - The source for rsam.
+
+ libframe - The source for the frame library. This library is used
+ by samterm so it must be made first.
+
+ libXg - The source code of the graphics library and some general
+ utility modules. Header file u.h provides much of the
+ interface between sam and the local environment. It is
+ included in every source file. Sam's graphics
+ operations are implemented by Plan 9 libg functions. This
+ library emulates those functions using X11 operations.
+
+ include - header files.
+
+ doc - The documentation for sam.
+
+Each source directory contains a makefile.
+The master makefile in the top directory builds the subdirectories in
+proper order.
+
+Most customization effort is confined to configuring the makefiles.
+They are configured by editing config.mk; see that file for details.
+
+After configuring the makefiles, change to the top-level directory and
+type "make". Typing "make install" installs B, sam, samterm and samsave in
+their permanent homes as well as their on-line documentation.
+
+During testing, the path of samterm may be specified using the -t command line
+option to sam. Similarly, the path of sam itself may be specified using the
+-s command line option; this is handy for testing the remote execution feature.
+
+The script doc/B is a Bourne shell-compatible script that sends a 'B' command
+to a running instance of sam.
+
+The original protocol between sam and samterm assumed that memory addresses
+and longs were 32 bits. Dave Hanson removed this dependency from the
+protocol allowing sam to run on 64-bit processors. However, other
+dependencies on structure alignment remain. The USE64BITS configuration
+variable in config.mk can be set to cope with 64-bit systems.
+
+Note that sam seems to have some issues when running under a compositing windo…
+
+Rob Pike designed and implemented the original Unix version of sam and
+the current version ported from Plan 9. Howard Trickey provided the X
+version of the graphics library, libXg. Matty Farrow and his colleagues
+at the University of Sydney extended libXg to support Runes. Boyd Roberts
+supplied the external command interface and the shell scripts for the 'B'
+command. Doug Gwyn contributed many useful ideas to the X implementation of
+sam. James Clark provided the simulations of the V10 Unix Man macros at the
+beginning of the original manual pages. Rob King modified sam to support
+scalable fonts, environment variable-based configration, the ability to
+control remote sam processes via the B script on remote machines, dynamic
+key substitutions from a configuration file, handle 64-bit architectures
+better, and to compile cleanly on modern Linux systems.
+
+Please send bug fixes and comments to:
+
+Rob King, [email protected]
diff --git a/config.mk b/config.mk
@@ -0,0 +1,23 @@
+# config.mk - makefile configuration for sam
+# copyright 2015 Rob King <[email protected]>
+
+# DESTDIR is the root of the installation tree
+DESTDIR=/usr/local
+
+# BINDIR is the directory where binaries go
+BINDIR=$(DESTDIR)/bin
+
+# MANDIR is where manual pages go
+MANDIR=$(DESTDIR)/share/man/man1
+
+# USE64BITS should be 1 for little-endian architectures with 64-bit pointers,
+# 2 for big-endian architectures with 64-bit pointers, and 0 otherwisew.
+# x86_64 systems would generally use 1 here, while DEC Alpha systems would
+# generally use 2.
+USE64BITS=1
+
+# FREETYPEINC should name the directory of your freetype2 includes.
+FREETYPEINC=/usr/include/freetype2
+
+# TMPDIR should be set to a directory for temporary files with lots of room
+TMPDIR=/tmp
diff --git a/doc/B b/doc/B
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+if [ $# = 0 ] ; then
+ echo "usage: B [-r machine] files..." 1>&2
+ exit 1
+fi
+
+machine=localhost
+if [ $1 = "-r" ] ; then
+ shift
+ machine=$1
+ shift
+
+ if [ "$machine" = "" ] ; then
+ echo "usage: B [-r machine] files..." 1>&2
+ exit 1
+ fi
+ echo "machine = $machine"
+fi
+
+pipe="${HOME}/.sam.${machine}"
+dir=`/bin/pwd`
+files=
+for i in $*
+do
+ case "$i" in
+ /*) files="$files $i"
+ ;;
+ *) files="$files $dir/$i"
+ ;;
+ esac
+done
+
+if [ ! -w "$pipe" ] ; then
+ pipe="${HOME}/.sam.fifo" # created by rsam
+fi
+
+if [ ! -w "$pipe" ]; then
+ sam $files & # start sam if it's not already running
+else
+ echo "B $files" >> $pipe
+fi
+
diff --git a/doc/Makefile b/doc/Makefile
@@ -0,0 +1,18 @@
+# Copyright 2014-2014 Rob King <[email protected]>
+
+include ../config.mk
+
+all: sam.1.pdf
+
+sam.1.pdf: sam.1
+ tbl $< | groff -mdoc -Tpdf > $@
+
+install: sam.1 B
+ cp sam.1 "$(MANDIR)"
+ cp B "$(BINDIR)"
+ ln -sf "$(MANDIR)/sam.1" "$(MANDIR)/B.1"
+ ln -sf "$(MANDIR)/sam.1" "$(MANDIR)/samterm.1"
+ ln -sf "$(MANDIR)/sam.1" "$(MANDIR)/rsam.1"
+ ln -sf "$(MANDIR)/sam.1" "$(MANDIR)/sam.save.1"
+ ln -sf "$(MANDIR)/sam.1" "$(MANDIR)/samsave.1"
+
diff --git a/doc/keyboard b/doc/keyboard
@@ -0,0 +1,206 @@
+!! 0xa1
+c$ 0xa2
+l$ 0xa3
+g$ 0xa4
+y$ 0xa5
+|| 0xa6
+SS 0xa7
+"" 0xa8
+cO 0xa9
+sa 0xaa
+<< 0xab
+no 0xac
+-- 0xad
+rO 0xae
+__ 0xaf
+de 0xb0
++- 0xb1
+s2 0xb2
+s3 0xb3
+'' 0xb4
+mi 0xb5
+pg 0xb6
+.. 0xb7
+,, 0xb8
+s1 0xb9
+so 0xba
+>> 0xbb
+14 0xbc
+12 0xbd
+34 0xbe
+?? 0xbf
+`A 0xc0
+'A 0xc1
+^A 0xc2
+~A 0xc3
+"A 0xc4
+oA 0xc5
+AE 0xc6
+,C 0xc7
+`E 0xc8
+'E 0xc9
+^E 0xca
+"E 0xcb
+`I 0xcc
+'I 0xcd
+^I 0xce
+"I 0xcf
+D- 0xd0
+~N 0xd1
+`O 0xd2
+'O 0xd3
+^O 0xd4
+~O 0xd5
+"O 0xd6
+mu 0xd7
+/O 0xd8
+`U 0xd9
+'U 0xda
+^U 0xdb
+"U 0xdc
+'Y 0xdd
+|P 0xde
+ss 0xdf
+`a 0xe0
+'a 0xe1
+^a 0xe2
+~a 0xe3
+"a 0xe4
+oa 0xe5
+ae 0xe6
+,c 0xe7
+`e 0xe8
+'e 0xe9
+^e 0xea
+"e 0xeb
+`i 0xec
+'i 0xed
+^i 0xee
+"i 0xef
+d- 0xf0
+~n 0xf1
+`o 0xf2
+'o 0xf3
+^o 0xf4
+~o 0xf5
+"o 0xf6
+-: 0xf7
+/o 0xf8
+`u 0xf9
+'u 0xfa
+^u 0xfb
+"u 0xfc
+'y 0xfd
+|p 0xfe
+"y 0xff
+wk 0x2654
+wq 0x2655
+wr 0x2656
+wb 0x2657
+wn 0x2658
+wp 0x2659
+bk 0x265a
+bq 0x265b
+br 0x265c
+bb 0x265d
+bn 0x265e
+bp 0x265f
+*a 0x3b1 Greek letter alpha
+*b 0x3b2
+*g 0x3b3
+*d 0x3b4
+*e 0x3b5
+*z 0x3b6
+*y 0x3b7
+*h 0x3b8
+*i 0x3b9
+*k 0x3ba
+*l 0x3bb
+*m 0x3bc
+*n 0x3bd
+*c 0x3be
+*o 0x3bf
+*p 0x3c0
+*r 0x3c1
+ts 0x3c2
+*s 0x3c3
+*t 0x3c4
+*u 0x3c5
+*f 0x3c6
+*x 0x3c7
+*q 0x3c8
+*w 0x3c9
+*A 0x391
+*B 0x392
+*G 0x393
+*D 0x394
+*E 0x395
+*Z 0x396
+*Y 0x397
+*H 0x398
+*I 0x399
+*K 0x39a
+*L 0x39b
+*M 0x39c
+*N 0x39d
+*C 0x39e
+*O 0x39f
+*P 0x3a0
+*R 0x3a1
+*S 0x3a3
+*T 0x3a4
+*U 0x3a5
+*F 0x3a6
+*X 0x3a7
+*Q 0x3a8
+*W 0x3a9
+<- 0x2190
+ua 0x2191
+-> 0x2192
+da 0x2193
+ab 0x2194
+V= 0x21d0
+=V 0x21d2
+fa 0x2200
+te 0x2203
+pd 0x2202
+es 0x2205
+De 0x2206
+gr 0x2207
+mo 0x2208
+!m 0x2209
+st 0x220d
+** 0x2217
+bu 0x2219
+sr 0x221a
+pt 0x221d
+if 0x221e
+an 0x2220
+l& 0x2227
+l| 0x2228
+ca 0x2229
+cu 0x222a
+is 0x222b
+tf 0x2234
+~= 0x2243
+cg 0x2245
+~~ 0x2248
+!= 0x2260
+== 0x2261
+<= 0x2266
+>= 0x2267
+sb 0x2282
+sp 0x2283
+!b 0x2284
+ib 0x2286
+ip 0x2287
+O+ 0x2295
+O- 0x2296
+Ox 0x2297
+tu 0x22a2
+Tu 0x22a8
+lz 0x22c4 Lozenge
+el 0x22ef Elipsis
+:( 0x2639 Frowny
+:) 0x263a Smiley
+;) 0x263b Dark smiley
diff --git a/doc/sam.1 b/doc/sam.1
@@ -0,0 +1,1026 @@
+.Dd $Mdocdate$
+.Dt SAM 1
+.Os
+.Sh NAME
+.Nm sam
+.Nd screen editor with structural regular expressions
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl t Ar terminal
+.Ar
+.Nm
+.Fl r Ar machine
+.Op Fl s Ar file
+.Op Fl t Ar terminal
+.Ar
+.Nm sam.save
+.Nm B
+.Op Fl r Ar machine
+.Ar
+.Sh DESCRIPTION
+.Nm sam
+is a multi-file editor.
+It modifies a local copy of an external file.
+The copy is here called a
+.Em file "."
+The files are listed in a menu available through mouse button 3 or the
+.Li n
+command.
+Each file has an associated name, usually the name of the external file from w…
+.Dq modified
+bit that indicates whether the editor's file agrees with the external file.
+The external file is not read into the editor's file until it first becomes th…
+The options are
+.Bl -tag -width Ds
+.It Fl d
+Do not download the terminal part of
+.Nm "."
+Editing will be done with the command language only, as in
+.Xr ed 1 "."
+.It Fl r Ar machine
+Run the host part remotely on the specified machine, the terminal part locally…
+.Nm
+associated with
+.Ar machine "."
+.It Fl s Ar file
+Start the host part from the indicated file on the remote host.
+By default, this is
+.Pa rsam ","
+which is a program that interposes itself between the host and terminal parts …
+.Nm
+and allows
+.Nm
+to be controlled via commands on the remote system.
+.It Fl t Ar file
+Start the terminal part from the indicated file.
+Useful for debugging.
+.El
+.Ss Regular expressions
+Regular expressions are more-or-less as they are in
+.Xr regex 7 ","
+with the addition of
+.Li \[rs]n
+to represent newlines.
+A regular expression may never contain a literal newline character.
+The elements of regular expressions are:
+.Bl -tag -width Ds
+.It Li "."
+Match any character except newline.
+.It Li \[rs]n
+Match newline.
+.It Li \[rs]x
+For any character except
+.Li n
+match the character
+.Po
+here
+.Sy x
+.Pc "."
+.It Li "[abc]"
+Match any character in the square brackets.
+.Li \[rs]n
+may be mentioned.
+.It Li "[^abc]"
+Match any character not in the square brackets, but never a newline.
+Both this and the positive form above accept a range of ASCII characters indic…
+.Li "a-z" "."
+.It Li "^"
+Match the null string immediately after a newline.
+.It Li "$"
+Match the null string immediately before a newline.
+.El
+.Pp
+Any other character except newline matches itself.
+.Pp
+In the following,
+.Sy r1
+and
+.Sy r2
+are regular expressions.
+.Bl -tag -width Ds
+.It Pq Sy r1
+Match what
+.Sy r1
+matches.
+.It Sy r1|r2
+Match what
+.Sy r1
+or
+.Sy r2
+matches.
+.It Sy r1*
+Match zero or more adjacent matches of
+.Sy r1 "."
+.It Sy r1+
+Match one or more adjacent matches of
+.Sy r1 "."
+.It Sy "r1?"
+Match zero or one matches of
+.Sy r1 "."
+.El
+.Pp
+The operators
+.Li "*" ","
+.Li "+" ","
+and
+.Li "?"
+are highest precedence, then catenation, then
+.Li "|"
+is lowest.
+The empty regular expression stands for the last complete expression encounter…
+A regular expression in
+.Nm
+matches the longest leftmost substring formally matched by the expression.
+Searching in the reverse direction is equivalent to search backwards with the …
+.Ss Addresses
+An address identifies a substring in a file.
+In the following
+.Do
+character
+.Sy n
+.Dc
+means the null string after the
+.Sy n\fR-th
+character in the file, with 1 the first character in the file.
+.Do
+Line
+.Sy n
+.Dc
+means the
+.Sy n\fR-th
+match, starting at the beginning of the file, of the regular expression
+.Li ".*\[rs]n?" "."
+.Po
+The peculiar properties of a last line without a newline are temporarily undef…
+.Pc
+All files always have a current substring, called
+.Sy dot ","
+that is the default address.
+.Ss Simple addresses
+.Bl -tag -width Ds
+.It Li # Sy n
+The empty string after character
+.Sy n ";"
+.Li #0
+is the beginning of the file.
+.It Sy n
+Line
+.Sy n "."
+.It Li / Sy regexp Li /
+.It Li ? Sy regexp Li ?
+The substring that matches the regular expression, found by looking toward the…
+.Pq Li /
+or beginning
+.Pq Li ?
+of the file, and if necessary continuing the search from the other end to the …
+The matched substring may straddle the starting point.
+When entering a pattern containing a literal question mark for a backward sear…
+.It Li 0
+The string before the first full line.
+This is not necessarily the null string; see
+.Li +
+and
+.Li -
+below.
+.It Li $
+The null string at the end of the file.
+.It Li "."
+Dot.
+.It Li "'"
+The mark in the file
+.Po
+see the
+.Sy k
+command below
+.Pc "."
+.It Sy "regexp"
+.Do
+A regular expression in double quotes.
+.Dc
+Preceding a simple address
+.Do
+default
+.Li "."
+.Dc ","
+refers to the address evaluated in the unique file whose menu line matches the…
+.El
+.Ss Compound addresses
+In the following,
+.Sy a1
+and
+.Sy a2
+are addresses.
+.Bl -tag -width Ds
+.It Sy a1+a2
+The address
+.Sy a2
+evaulated starting at the end of
+.Sy a1 "."
+.It Sy a1-a2
+The address
+.Sy a2
+evaluated looking the reverse direction starting at the beginning of
+.Sy a1 "."
+.It Sy "a1,a2"
+The substring from the beinning of
+.Sy a1
+to the end of
+.Sy a2 "."
+If
+.Sy a1
+is missing,
+.Li 0
+is substituted.
+If
+.Sy a2
+is missing,
+.Li $
+is substituted.
+.It Sy a1;a2
+Like
+.Dq Sy a1,a2
+but with
+.Sy a2
+evaluated at the end of, and dot set to,
+.Sy a1 "."
+.El
+.Pp
+The operators
+.Li +
+and
+.Li -
+are high precedence, while
+.Li ,
+and
+.Li ;
+are low precedence.
+.Pp
+In both
+.Li +
+and
+.Li -
+forms, if
+.Sy a2
+is a line or character address with a missing number, the number defaults to 1.
+If
+.Sy a1
+is missing,
+.Li "."
+is subtituted.
+If both
+.Sy a1
+and
+.Sy a2
+are present and distinguishable,
+.Li +
+may be elided.
+.Sy a2
+may be a regular expression; if it is delimited by
+.Li "?"
+characters, the effect of the
+.Li +
+or
+.Li -
+is reversed.
+.Pp
+It is an error for a compound address to represent a malformed substring.
+.Pp
+Some useful idioms:
+.Bl -tag -width Ds
+.It Sy a1+- Po Sy a1-+ Pc
+selects the line containing the end
+.Dq beginning
+of
+.Sy a1 "."
+.It Sy 0/regexp/
+locates the first match of the expression in the file.
+.Do
+The form
+.Li 0;//
+sets dot unnecessarily.
+.Dc
+.It Sy "./regexp///"
+find the second following occurence of the expression, and
+.Sy ".,/regexp/"
+extends dot.
+.El
+.Ss Commands
+In the following, text demarcated by slashes represnets text delimited by any …
+Any number of trailing delimiters may be elided, with multiple elisions then r…
+In any delimited text, newline may not appear literally;
+.Li \[rs]n
+may be typed for newline; and
+.Li \[rs]/
+quotes the delimiter, here
+.Li / "."
+Backslash is other interpreted literally, except in
+.Sy s
+commands.
+.Pp
+Most commands may be prefixed with an address to indicate their range of opera…
+Those that may not are marked with a
+.Sy "*"
+below.
+If a command takes an address and none is supplised, dot is used.
+The sole exception is the
+.Sy w
+command, which defaults to
+.Li "0,$" "."
+In the description,
+.Dq range
+is used to represent whatever address is supplied.
+Many commands set the value of dot as a side effect.
+If so, it is always to the
+.Dq result
+of the change: the empty string for a deletion, the new text for an insertion,…
+.Po
+but see the
+.Sy s
+and
+.Sy e
+commands
+.Pc "."
+.Ss Text commands
+.Bl -tag -width Ds
+.It Sy a/text/
+Insert the text into the file after the range.
+Set dot.
+.Pp
+May also be written as
+.Bd -literal -offset indent
+ a
+ lines
+ of
+ text
+ .
+.Ed
+.It Sy c \fR or Sy i
+Same as
+.Sy a ","
+but
+.Sy c
+replaces the text, while
+.Sy i
+inserts
+.Em before
+the range.
+.It Sy d
+Delete the text in range.
+Set dot.
+.It Sy s/regexp/text/
+Substitute
+.Sy text
+for the first match to the regular expression in the range.
+Set dot to the modified range.
+In
+.Sy text
+the character
+.Li "&"
+stands for the string that matched the expression.
+Backslash behaves as usual unless followed by a digit:
+.Sy \[rs]d
+stands for the string that matched the subexpression begun by the
+.Sy d\fR-th
+left parenthesis.
+If
+.Sy s
+is followed immediately by a number
+.Sy n ","
+as in
+.Li "s2/x/y/" ","
+the
+.Sy n\fR-th
+match in the range is substituted.
+If the command is followed by
+.Sy g ","
+as in
+.Li "s/x/y/g" ","
+all matches in the range are substituted.
+.It Sy "m a1"
+Move the range to after
+.Sy a1 "."
+Set dot.
+.It Sy "t a1"
+Copy the range to after
+.Sy a1 "."
+Set dot.
+.El
+.Ss Display commands
+.Bl -tag -width Ds
+.It Sy p
+Print the text in the range.
+Set dot.
+.It Sy =
+Print the line address and character address of the range.
+.It Sy =#
+Print just the character address of the range.
+.El
+.Ss File commands.
+.Bl -tag -width Ds
+.It * Sy "b file-list"
+Set the current file to the first file named in the list that
+.Nm
+also has in its menu.
+The list may be expressed
+.Sy "<shell-command"
+in which case the file names are taken as words
+.Pq "in the shell sense"
+generated by the shell command.
+.It * Sy "B file-list"
+Same as
+.Sy b ","
+except that filenames not in the menu are entered there, and all file names in…
+.It * Sy n
+Print a menu of files.
+The format is:
+.Bl -tag -width Ds
+.It "' or blank"
+indicating the file is modified or clean,
+.It "- or +"
+indicating the file is unread or has been read
+.Po
+in the terminal,
+.Li "*"
+means more than one window is open
+.Pc ","
+.It ". or blank"
+indicating the current file,
+.El
+a blank,
+and the filename.
+.It "*" Sy "D file-list"
+Delete the named files from the menu.
+If no files are named, the current file is deleted.
+It is an error to delete a modified file, but a subsequent
+.Sy D
+will delete such a file.
+.El
+.Ss I/O commands
+.Bl -tag -width Ds
+.It "*" Sy "e filename"
+Replace the file by the contents of the named external file.
+Set dot to the beginning of the file.
+.It Sy "r filename"
+Replace the text in the range by the contents of the named external file.
+Set dot.
+.It Sy "w filename"
+Write the range
+.Po
+default
+.Li 0,$
+.Pc
+to the named external file.
+.It "*" Sy "f filename"
+Set the file name and print the resulting menu entry.
+.El
+.Pp
+If the file name argument is absent from any of these, the current file name i…
+.Sy e
+always sets the file name,
+.Sy r
+and
+.Sy w
+will do so if the file has no name.
+.Bl -tag -width Ds
+.It Sy "< shell-command"
+Replace the range by the standard output of the shell command.
+.It Sy "> shell-command"
+Sends the range to the standard input of the shell command.
+.It Sy "| shell-command"
+Send the range to the standard input, and replace it by the standard output, o…
+.It "*" Sy "! shell-command"
+Run the shell command.
+.It "*" Sy "cd directory"
+Change working directory.
+If no directory is specified,
+.Ev "$HOME"
+is used.
+.El
+.Pp
+In any of
+.Sy "<" ","
+.Sy ">" ","
+.Sy "|" ", or"
+.Sy "!" ","
+if the shell command is omitted, the last shell command
+.Pq "of any type"
+is substituted.
+If
+.Nm
+is downloaded,
+.Sy "!"
+sets standard input to
+.Pa "/dev/null" ","
+and otherwise unassigned output
+.Po
+.Pa stdout
+for
+.Sy "!"
+and
+.Sy ">" ","
+.Pa stderr
+for all
+.Pc
+is placed in
+.Pa "${HOME}/sam.err"
+and the first few lines are printed.
+.Ss Loops and conditionals
+.Bl -tag -width Ds
+.It Sy "x/regexp/ command"
+For each match of the regular expression in the range, run the command with do…
+Set dot to the last match.
+If the regular expression and its slashes are omitted,
+.Li "/.*\[rs]n/"
+is assumed.
+Null string matches potentially occur before every character of the range and …
+.It Sy "y/regexp/ command"
+Like
+.Sy x ","
+but run the command for each substring that lies before, between, or after the…
+.Sy x "."
+There is no default behavior.
+Null substrings potentially occur before every character in the range.
+.It "*" Sy "X/regexp/ command"
+For each file whose menu entry matches the regular expression, make that the c…
+If the expression is omitted, the command is run in every file.
+.It "*" Sy "Y/regexp/ command"
+Same as
+.Sy X ","
+but for files that do not match the regular expression, and the expression is …
+.It Sy "g/regexp/ command"
+.It Sy "v/regexp/ command"
+If the range contains
+.Po
+.Sy g
+.Pc
+or does not contain
+.Po
+.Sy v
+.Pc
+a match for the expression, set dot to the range and run the command.
+.El
+.Pp
+These may be nested arbitrarily deeply, but only one instance of either
+.Sy X
+or
+.Sy Y
+may appear in a single command.
+An empty command in an
+.Sy x
+or
+.Sy y
+defaults to
+.Sy p ";"
+an empty command in
+.Sy X
+or
+.Sy Y
+defaults to
+.Sy f "."
+.Sy g
+and
+.Sy v
+do not have defaults.
+.Ss Miscellany
+.Bl -tag -width Ds
+.It Sy k
+Set the current file's mark to the range.
+Does not set dot.
+.It "*" Sy q
+Quit.
+It is an error to quit with modified files, but a second
+.Sy q
+will succeed.
+.It "*" Sy "u n"
+Undo the last
+.Sy n
+.Pq "default 1"
+top-level commands that changed the contents or name of the current file, and …
+Successive
+.Sy u
+commands move further back in time.
+The only commands for which
+.Sy u
+is ineffective are
+.Sy cd ","
+.Sy u ","
+.Sy q ","
+.Sy w ","
+and
+.Sy D "."
+.It Sy empty
+If the range is explicit, set dot to the range.
+If
+.Nm
+is downloaded, the resulting dot is selected on the screen; otherwise it is pr…
+If no address is specified
+.Pq "the command is a newline"
+dot is extended in either direction to the line boundaries and printed.
+If dot is thereby unchanges, i is set to
+.Li ".+1"
+and printed.
+.El
+.Ss Grouping and multiple changes
+Commands may be groups by enclosing them in curly braces.
+Commands within the braces must appear on separate lines
+.Pq "no backslashes are required between commands" "."
+Semantically, the opening brance is like a command: it takes an
+.Pq optional
+address and sets dot for each sub-command.
+Commands within the braces are executed sequentially, but changes made by one …
+.Pq "see the next paragraph" "."
+Braces may be nested arbitrarily.
+.Pp
+When a command makes a number of changes to a file, as in
+.Li "x/re/c/text/" ","
+the addresses of all changes to the file are computed in the original file.
+If the changes are in sequence, they are applied to the file.
+Successive insertions at the same address are catenated into a single insertio…
+.Ss The terminal
+What follows refers to the behavior of
+.Nm
+when downloaded, that is, when operating as a display editor on a bitmap displ…
+This is the default behavior; invoking
+.Nm
+with the
+.Fl d
+.Pq "no download"
+option provides access to the command language only.
+.Pp
+Each file may have zero or more windows open.
+Each window is equivalent and is updated simultaneously with changes in other …
+Each window has an independent value of dot, indicated by a highlighted substr…
+Dot may be in a region not within the window.
+There is usually a
+.Dq "current window" ","
+marked with a dark border, to which typed text and editing commands apply.
+The escape key selects
+.Pq "sets dot to"
+text typed since the last mouse button hit.
+.Pp
+The button 3 menu controls window operations.
+The top of the menu provides the following operators, each of which uses one o…
+.Bl -tag -width Ds
+.It Sy new
+Create a new empty file:
+Depress button 3 where one corner of the new rectangle should appear
+.Pq "box cursor" ","
+and move the mouse while holding down button 3 to the diagonally opposite corn…
+.Dq Sweeping
+a null rectangle gets a large window disjoint from the command window or the w…
+.Nm
+window, depending on where the null rectangle is.
+.It Sy zerox
+Create a copy of an existing window.
+After selecting the window to copy with button 1,
+sweep out the window for the copy.
+.It Sy reshape
+Change the size and location of a window.
+First click button 3 in the window to be changed
+.Pq "gunsight cursor" "."
+Then sweep out a window as for the
+.Sy new
+menu selection.
+.It Sy close
+Delete the window.
+In the last window of a file,
+.Sy close
+is equivalent to a
+.Sy D
+for the file.
+.It Sy write
+Equivalent to a
+.Sy w
+for the file.
+.El
+.Pp
+Below these operators is a list of available files, starting with
+.Sy "~~sam~~" ","
+the command window.
+Selecting a file from the list makes the most recently used window on that fil…
+If no windows are open on the file, the user is prompted to open one.
+Files other than
+.Sy "~~sam~~"
+are marked with one of the characters
+.Li "-+*"
+according as zero, one, or more windows are open on the file.
+A further mark,
+.Li "." ","
+appears on the file in the current window and a single quote,
+.Li "'" ","
+on a file modified since last write.
+.Pp
+The command window, created automatically when
+.Nm
+starts, is an ordinary window except that text typed to it is interpreted as c…
+There is an
+.Dq "output point"
+that separates commands being typed from previous output.
+Commands typed in the command window apply to the current open file\[en]the fi…
+.Ss Manipulating text
+Typed characters replace the current selection
+.Pq dot
+in the current window.
+Backspace deletes the previous character.
+Escape selects
+.Pq "sets dot to"
+everything typed since the last mouse hit.
+.Pp
+Button 1 changes selection.
+Pointing to a non-current window with button 1 makes it current; within the cu…
+Double-clicking selects text to the boundaries of words, lines, quoted strings…
+.Pp
+Button 2 provides a menu of editing commands:
+.Bl -tag -width Ds
+.It Sy cut
+Delete dot and save the delted text in the snarf buffer.
+.It Sy paste
+Replace the text in dot by the contents of the snarf buffer.
+.It Sy snarf
+Save the text in dot in the snarf buffer.
+.It Sy look
+Search forward for the next occurence of the literal text in dot.
+If dot is the null string, the text in the snarf buffer is used.
+The snarf buffer is unaffected.
+.It Sy <exch>
+Exchange the snarf buffer with the current system-wide text selection.
+The exchange of a large amount of selected text is truncated to the size of th…
+.Pq "currently 4K"
+without warning.
+.It Sy "/regexp"
+Search forward for the next match of the last regular expression typed in a co…
+.Pq "Not in command window."
+.It Sy send
+Send the text in dot, or the snarf buffer if dot is the null string, as if it …
+Saves the sent text in the snarf buffer.
+.Pq "Command window only."
+.El
+.Ss Abnormal termination
+If
+.Nm
+terminates other than by a
+.Sy q
+command
+.Pq "by hangup, deleting its window, etc." ","
+modified files are saved in an executable file,
+.Pq "${HOME}/sam.save" "."
+This program, when executed, asks whether to write each file back to an extern…
+The answer
+.Sy y
+causes writing; anything else skips the file.
+If a machine crash prevents the creation of a
+.P "sam.save"
+file, all changes are lost.
+If an editing session is difficult to replicate, writing changed files often i…
+.Ss Remote execution
+.Nm sam
+allows the host and terminal parts of the editor to run on diffrent machines, …
+.Dq downloading "."
+This process can be suppressed with the
+.Fl d
+option, which then runs only the host part in the manner of
+.Xr ed 1 "."
+.Pp
+Running the host part on another machine is accomplished using the
+.Fl r
+option, which is used to specify a remote machine name suitable for passing to…
+.Ev RSH
+environment variable.
+.Pp
+By default,
+.Nm sam
+will run a command called
+.Nm rsam
+as the host-part on the remote machine.
+.Nm rsam
+opens up an additional control channel on the remote machine, allowing
+.Nm sam
+to be controlled via the
+.Nm B
+command on the remote machine as well.
+.Pp
+The only components of
+.Nm sam
+that need to be on the remote machine are
+.Nm rsam
+and
+.Nm sam ","
+and any command specified as the argument to the
+.Fl s
+option.
+Users may also like to have the
+.Nm B
+command present on the remote system; invoking this command on the remote syst…
+.Po
+if
+.Nm sam
+was invoked with its default remote host command, i.e.
+.Nm rsam
+.Pc
+open files in the local terminal.
+This allows users to run the terminal part of
+.Nm sam
+locally while controlling it via a remote shell connection.
+.Ss Controlling running instances of Nm
+.Nm B
+is a shell command that causes a downloaded instance of
+.Nm sam
+to load the named files.
+The
+.Fl r
+option causes the instance of
+.Nm sam
+connected to
+.Ar machine
+to load the named files; the default is the most-recently started local instan…
+.Pp
+.Nm B
+may also be called on a remote machine, causing the downloaded instance of sam…
+.Ss Unicode Text Input
+.Nm sam
+allows the input of arbitrary Unicode characters from the Basic Multilingual P…
+.Pq BMP
+via five-character and two-character sequences.
+These sequences are entered while holding down the system compose key
+.Po
+on most keyboards, this key is labeled
+.Sy Alt
+.Pc "."
+.Pp
+The first method allows the entry of any code point in the BMP.
+While holding down the compose key, an uppercase
+.Li X
+character is typed, followed by exactly four lowercase hexadecimal digits nami…
+.Pp
+Commonly used codepoints can be entered with an abbreviated two-character sequ…
+These sequence definitions are read from a file called
+.Pa ".keyboard"
+in the user's home directory.
+Each line in this file consists of two characters, followed by any number of s…
+Holding down the compose key and typing the two listed characters will insert …
+For example, given the
+.Pa ".keyboard"
+line:
+.Bd -literal -offset indent
+12 0x00BD
+.Ed
+.Pp
+then typing the characters 1 and 2 while holding down the compose key will ins…
+.Pq \[u00BD]
+into the file.
+.Pp
+After the hexadecimal codepoint specification, the rest of the line is ignored…
+.Pp
+If no
+.Pa ".keyboard"
+file is present, the following key sequences are defined by default:
+.Pp
+.TS
+box;
+c | c | c | c | c | c | c | c
+- | - | - | - | - | - | - | -
+c | c | c | c | c | c | c | c.
+Keys Codepoint Keys Codepoint Keys Codepoin…
+!! \[u00A1] c$ \[u00A2] l$ \[u00A3] …
+y$ \[u00A5] || \[u00A6] SS \[u00A7] …
+cO \[u00A9] sa \[u00AA] << \[u00AB] …
+-- \[u00AD] rO \[u00AE] __ \[u00AF] …
++- \[u00B1] s2 \[u00B2] s3 \[u00B3] …
+mi \[u00B5] pg \[u00B6] .. \[u00B7] …
+s1 \[u00B9] so \[u00BA] >> \[u00BB] …
+12 \[u00BD] 34 \[u00BE] ?? \[u00BF] …
+'A \[u00C1] ^A \[u00C2] ~A \[u00C3] …
+oA \[u00C5] AE \[u00C6] ,C \[u00C7] …
+'E \[u00C9] ^E \[u00CA] "E \[u00CB] …
+'I \[u00CD] ^I \[u00CE] "I \[u00CF] …
+~N \[u00D1] `O \[u00D2] 'O \[u00D3] …
+~O \[u00D5] "O \[u00D6] mu \[u00D7] …
+`U \[u00D9] 'U \[u00DA] ^U \[u00DB] …
+'Y \[u00DD] |P \[u00DE] ss \[u00DF] …
+'a \[u00E1] ^a \[u00E2] ~a \[u00E3] …
+oa \[u00E5] ae \[u00E6] ,c \[u00E7] …
+'e \[u00E9] ^e \[u00EA] "e \[u00EB] …
+'i \[u00ED] ^i \[u00EE] "i \[u00EF] …
+~n \[u00F1] `o \[u00F2] 'o \[u00F3] …
+~o \[u00F5] "o \[u00F6] -: \[u00F7] …
+`u \[u00F9] 'u \[u00FA] ^u \[u00FB] …
+'y \[u00FD] |p \[u00FE] "y \[u00FF] …
+.TE
+.TS
+box;
+c | c | c | c | c | c | c | c
+- | - | - | - | - | - | - | -
+c | c | c | c | c | c | c | c.
+Keys Codepoint Keys Codepoint Keys Codepoin…
+wq \[u2655] wr \[u2656] wb \[u2657] …
+wp \[u2659] bk \[u265A] bq \[u265B] …
+bb \[u265D] bn \[u265E] bp \[u265F] …
+*b \[u03B2] *g \[u03B3] *d \[u03B4] …
+*z \[u03B6] *y \[u03B7] *h \[u03B8] …
+*k \[u03BA] *l \[u03BB] *m \[u03BC] …
+*c \[u03BE] *o \[u03BF] *p \[u03C0] …
+ts \[u03C2] *s \[u03C3] *t \[u03C4] …
+*f \[u03C6] *x \[u03C7] *q \[u03C8] …
+*A \[u0391] *B \[u0392] *G \[u0393] …
+*E \[u0395] *Z \[u0396] *Y \[u0397] …
+*I \[u0399] *K \[u039A] *L \[u039B] …
+*N \[u039D] *C \[u039E] *O \[u039F] …
+*R \[u03A1] *S \[u03A3] *T \[u03A4] …
+*F \[u03A6] *X \[u03A7] *Q \[u03A8] …
+<- \[u2190] ua \[u2191] -> \[u2192] …
+ab \[u2194] V= \[u21D0] =V \[u21D2] …
+te \[u2203] pd \[u2202] es \[u2205] …
+gr \[u2207] mo \[u2208] !m \[u2209] …
+** \[u2217] bu \[u2219] sr \[u221A] …
+if \[u221E] an \[u2220] l& \[u2227] …
+ca \[u2229] cu \[u222A] is \[u222B] …
+~= \[u2243] cg \[u2245] ~~ \[u2248] …
+== \[u2261] <= \[u2266] >= \[u2267] …
+sp \[u2283] !b \[u2284] ib \[u2286] …
+O+ \[u2295] O- \[u2296] Ox \[u2297] …
+Tu \[u22A8] lz \[u22C4] el \[u22EF] …
+:) \[u263A] ;) \[u263B]
+.TE
+.Sh ENVIRONMENT
+The following environment variables affect the operation of
+.Nm sam ":"
+.Bl -tag -width Ds
+.It Ev FOREGROUND
+Sets the foreground color used by
+.Nm
+to draw its terminal.
+Common English color names can be used
+.Po
+see
+.Xr rgb 5
+.Pc ","
+or exact colors can be specified as
+.Sy "#rrggbb" ","
+where
+.Sy "rr" ","
+.Sy "gg" ","
+and
+.Sy "bb"
+are hexadecimal digits describing the red, green, and blue components of the c…
+By default, this is the string
+.Dq black "."
+.It Ev BACKGROUND
+As
+.Ev FOREGROUND ","
+but describing the background color used to draw the terminal.
+By default, this is the string
+.Dq white "."
+.It Ev FONT
+A string representing a
+.Xr fc-match 1
+compatible font pattern.
+The font described by this pattern will be used to render text in the terminal.
+By default, this is the string
+.Dq "monospace" "."
+.It Ev RSH
+The name of a command to be used to connect to a remote machine when
+.Nm
+is invoked with the
+.Fl r
+option.
+It will be passed at least two arguments: the name of the machine to connect t…
+.Po
+e.g.
+.Nm rsam
+.Pc "."
+Any additional arguments should be passed to the command on the remote machine.
+By default, this is the string
+.Dq "ssh" "."
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa "${HOME}/.keyboard"
+Provides a mapping of two-character sequences to Unicode code points.
+Note that the code points must be in the Basic Multilingual Plane.
+.It Pa "${HOME}/sam.save"
+Created if
+.Nm
+terminates abnormally.
+Executing this file will prompt the user to restore the files that were being …
+.It Pa "${HOME}/sam.err"
+Stores output of shell commands executed by
+.Nm "."
+.El
+.Sh SEE ALSO
+.Xr ed 1
+.Sh BUGS
+When a
+.Nm sam
+window is resized, the command window may have the wrong size.
+.Pp
+Under some window managers, resizing the window may cause a panic.
+.Pp
+.Nm
+has issues with compositing window managers like compiz, resulting in some ren…
+.Pp
+The only human language in which colors may be specified is English.
+.Pp
+The only human language in which output is generated is English.
+.Pp
+There is no support for right-to-left text, ligatures, composed characters, or…
+\ No newline at end of file
diff --git a/doc/sam.1.pdf b/doc/sam.1.pdf
Binary files differ.
diff --git a/doc/sam.ps b/doc/sam.ps
@@ -0,0 +1,8261 @@
+%!PS
+%%Version: 3.3.2
+%%DocumentFonts: (atend)
+%%Pages: (atend)
+%%EndComments
+%
+% Version 3.3.2 prologue for troff files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/linewidth .3 def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/resolution 720 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/R /Times-Roman def
+/I /Times-Italic def
+/B /Times-Bold def
+/BI /Times-BoldItalic def
+/H /Helvetica def
+/HI /Helvetica-Oblique def
+/HB /Helvetica-Bold def
+/HX /Helvetica-BoldOblique def
+/CW /Courier def
+/CO /Courier def
+/CI /Courier-Oblique def
+/CB /Courier-Bold def
+/CX /Courier-BoldOblique def
+/PA /Palatino-Roman def
+/PI /Palatino-Italic def
+/PB /Palatino-Bold def
+/PX /Palatino-BoldItalic def
+/Hr /Helvetica-Narrow def
+/Hi /Helvetica-Narrow-Oblique def
+/Hb /Helvetica-Narrow-Bold def
+/Hx /Helvetica-Narrow-BoldOblique def
+/KR /Bookman-Light def
+/KI /Bookman-LightItalic def
+/KB /Bookman-Demi def
+/KX /Bookman-DemiItalic def
+/AR /AvantGarde-Book def
+/AI /AvantGarde-BookOblique def
+/AB /AvantGarde-Demi def
+/AX /AvantGarde-DemiOblique def
+/NR /NewCenturySchlbk-Roman def
+/NI /NewCenturySchlbk-Italic def
+/NB /NewCenturySchlbk-Bold def
+/NX /NewCenturySchlbk-BoldItalic def
+/ZD /ZapfDingbats def
+/ZI /ZapfChancery-MediumItalic def
+/S /S def
+/S1 /S1 def
+/GR /Symbol def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/show {show} bind def % so later references don't bind
+/widthshow {widthshow} bind def
+/stringwidth {stringwidth} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+ /scaling 72 resolution div def
+ linewidth setlinewidth
+ 1 setlinecap
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ margin 2 div dup neg translate
+ magnification dup aspectratio mul scale
+ scaling scaling scale
+
+ addmetrics
+ 0 0 moveto
+} def
+
+/pagedimensions {
+ useclippath userdict /gotpagebbox known not and {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox}…
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/addmetrics {
+ /Symbol /S null Sdefs cf
+ /Times-Roman /S1 StandardEncoding dup length array copy S1defs cf
+} def
+
+/pagesetup {
+ /page exch def
+ currentdict /pagedict known currentdict page known and {
+ page load pagedict exch get cvx exec
+ } if
+} def
+
+/decodingdefs [
+ {counttomark 2 idiv {y moveto show} repeat}
+ {neg /y exch def counttomark 2 idiv {y moveto show} repeat}
+ {neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll width…
+ {neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat}
+ {counttomark 2 idiv {y moveto show} repeat}
+ {neg setfunnytext}
+] def
+
+/setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def
+
+/w {neg moveto show} bind def
+/m {neg dup /y exch def moveto} bind def
+/done {/lastpage where {pop lastpage} if} def
+
+/f {
+ dup /font exch def findfont exch
+ dup /ptsize exch def scaling div dup /size exch def scalefont setfont
+ linewidth ptsize mul scaling 10 mul div setlinewidth
+ /spacewidth ( ) stringwidth pop def
+} bind def
+
+/changefont {
+ /fontheight exch def
+ /fontslant exch def
+ currentfont [
+ 1 0
+ fontheight ptsize div fontslant sin mul fontslant cos div
+ fontheight ptsize div
+ 0 0
+ ] makefont setfont
+} bind def
+
+/sf {f} bind def
+
+/cf {
+ dup length 2 idiv
+ /entries exch def
+ /chtab exch def
+ /newencoding exch def
+ /newfont exch def
+
+ findfont dup length 1 add dict
+ /newdict exch def
+ {1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall
+
+ newencoding type /arraytype eq {newdict /Encoding newencoding put} if
+
+ newdict /Metrics entries dict put
+ newdict /Metrics get
+ begin
+ chtab aload pop
+ 1 1 entries {pop def} for
+ newfont newdict definefont pop
+ end
+} bind def
+
+%
+% A few arrays used to adjust reference points and character widths in some
+% of the printer resident fonts. If square roots are too high try changing
+% the lines describing /radical and /radicalex to,
+%
+% /radical [0 -75 550 0]
+% /radicalex [-50 -75 500 0]
+%
+% Move braceleftbt a bit - default PostScript character is off a bit.
+%
+
+/Sdefs [
+ /bracketlefttp [201 500]
+ /bracketleftbt [201 500]
+ /bracketrighttp [-81 380]
+ /bracketrightbt [-83 380]
+ /braceleftbt [203 490]
+ /bracketrightex [220 -125 500 0]
+ /radical [0 0 550 0]
+ /radicalex [-50 0 500 0]
+ /parenleftex [-20 -170 0 0]
+ /integral [100 -50 500 0]
+ /infinity [10 -75 730 0]
+] def
+
+/S1defs [
+ /underscore [0 80 500 0]
+ /endash [7 90 650 0]
+] def
+%
+% Tries to round clipping path dimensions, as stored in array pagebbox, so they
+% match one of the known sizes in the papersizes array. Lower left coordinates
+% are always set to 0.
+%
+
+/roundpagebbox {
+ 7 dict begin
+ /papersizes [8.5 inch 11 inch 14 inch 17 inch] def
+
+ /mappapersize {
+ /val exch def
+ /slop .5 inch def
+ /diff slop def
+ /j 0 def
+ 0 1 papersizes length 1 sub {
+ /i exch def
+ papersizes i get val sub abs
+ dup diff le {/diff exch def /j i def} {pop} ifelse
+ } for
+ diff slop lt {papersizes j get} {val} ifelse
+ } def
+
+ pagebbox 0 0 put
+ pagebbox 1 0 put
+ pagebbox dup 2 get mappapersize 2 exch put
+ pagebbox dup 3 get mappapersize 3 exch put
+ end
+} bind def
+
+%%EndProlog
+%%BeginSetup
+mark
+/linewidth 0.5 def
+/#copies 1 store
+/landscape false def
+/resolution 720 def
+setup
+2 setdecoding
+%%EndSetup
+%%Page: 1 1
+/saveobj save def
+mark
+1 pagesetup
+12 B f
+(The Text Editor)2 827 1 2343 1230 t
+12 CW f
+(sam)3200 1230 w
+10 I f
+(ROB PIKE)1 441 1 2659 1470 t
+10 R f
+(AT&T Bell Laboratories)2 993 1 2383 1650 t
+(Murray Hill, New Jersey 07974)4 1267 1 2246 1770 t
+10 I f
+(ABSTRACT)2643 2150 w
+10 CW f
+(Sam)1080 2446 w
+10 R f
+( textual com-)2 541( A)1 129( text editor intended for bitmap displays.)6 166…
+( the mouse-driven, cut-and-paste interface to make complex)7 2450(mand langua…
+( language is characterized by the composi-)6 1719( The)1 208( editing tasks e…
+( treat-)1 237( The)1 207( regular expressions to describe the structure of th…
+(ment of files as a database, with changes logged as atomic transactions, guid…
+(mentation and makes a general `undo' mechanism straightforward.)7 2672 1 1080…
+10 CW f
+(Sam)1330 3202 w
+10 R f
+( a low-bandwidth stream, one)4 1224(is implemented as two processes connected…
+( it can run)3 435( Therefore)1 453( the other the editing algorithms.)5 1360(…
+( with both pro-)3 600(with the display process in a bitmap terminal and the e…
+( process in the terminal and the edi-)7 1442(cesses on a bitmap-equipped host…
+( can even run without a bitmap)6 1243( suppressing the display process, it)5 …
+(terminal.)1080 3802 w
+( 17, number)2 502(This paper is reprinted from Software\320Practice and Exper…
+(11, pp. 813-845.)2 658 1 1080 4078 t
+6 R f
+(KEY WORDS)1 354 1 1080 4318 t
+8 R f
+( Undo)1 258( expressions)1 391( Caches Regular)2 642(Text editors)1 382 4 149…
+10 B f
+(Introduction)720 4438 w
+10 CW f
+(Sam)720 4594 w
+10 R f
+( that combines cut-and-paste interactive editing with an unusual command)9 30…
+( is written as two programs: one, the `host)8 1775( It)1 123( composition of …
+( the command language and provides file access; the other,)9 2428(part,' runs…
+( bitmap display and supports the)5 1337(the `terminal part,' runs asynchronou…
+( host part may be even run in isolation on an ordinary terminal to edit)14 28…
+( command language, much like a traditional line editor, without assistance fr…
+( runs on a Blit)4 583( often, the terminal part)4 952(display. Most)1 573 3 7…
+6 R f
+(1)2828 5264 w
+10 R f
+(terminal \(actually on a Teletype DMD 5620, the pro-)8 2153 1 2887 5314 t
+( Sun com-)2 423(duction version of the Blit\), whose host connection is an or…
+(puter the host and display processes run on a single machine, connected by a …
+10 CW f
+(Sam)970 5710 w
+10 R f
+( unlike)1 282( has no facilities for multiple fonts, graphics or tables,)9 22…
+(MacWrite,)720 5830 w
+6 R f
+(2)1149 5780 w
+10 R f
+(Bravo,)1211 5830 w
+6 R f
+(3)1480 5780 w
+10 R f
+(Tioga)1542 5830 w
+6 R f
+(4)1775 5780 w
+10 R f
+(or Lara.)1 322 1 1837 5830 t
+6 R f
+(5)2159 5780 w
+10 R f
+( this)1 176( \(Throughout)1 561( has a rich command language.)5 1266(Also unl…
+(paper, the phrase)2 708 1 720 5950 t
+10 I f
+(command language)1 800 1 1468 5950 t
+10 R f
+( commands activated from the mouse)5 1575(refers to textual commands;)3 1157 …
+(form the)1 344 1 720 6070 t
+10 I f
+(mouse language.)1 679 1 1092 6070 t
+10 R f
+(\))1771 6070 w
+10 CW f
+(Sam)1856 6070 w
+10 R f
+(developed as an editor for use by programmers, and tries to join the styles)1…
+(of the Unix text editor)4 968 1 720 6190 t
+10 CW f
+(ed)1733 6190 w
+6 R f
+(6,7)1853 6140 w
+10 R f
+( cut-and-paste editors by providing a comfortable)6 2100(with that of interac…
+( The)1 214( language driven by regular expressions.)5 1658(mouse-driven inter…
+( language, and acquired a notation for describing the)8 2186(command language…
+( a dataflow-like syntax for specifying)5 1594(structure of files more richly …
+(changes.)720 6670 w
+(The interactive style was influenced by)5 1576 1 970 6826 t
+10 CW f
+(jim)2573 6826 w
+10 R f
+(,)2753 6826 w
+6 R f
+(1)2778 6776 w
+10 R f
+( for the Blit, and by)5 798(an early cut-and-paste editor)3 1144 2 2835 6826 t
+10 CW f
+(mux)4805 6826 w
+10 R f
+(,)4985 6826 w
+6 R f
+(8)5010 6776 w
+10 R f
+(the Blit window system.)3 1000 1 720 6946 t
+10 CW f
+(Mux)1779 6946 w
+10 R f
+(merges the original Blit window system,)5 1662 1 1993 6946 t
+10 CW f
+(mpx)3688 6946 w
+10 R f
+(,)3868 6946 w
+6 R f
+(1)3893 6896 w
+10 R f
+(with cut-and-paste editing,)2 1084 1 3956 6946 t
+8 S1 f
+(__________________)720 7046 w
+8 R f
+(* Unix is a registered trademark of AT&T.)7 1365 1 720 7146 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 1 1
+%%Page: 2 2
+/saveobj save def
+mark
+2 pagesetup
+10 R f
+(- 2 -)2 166 1 2797 480 t
+( like a multiplexed version of)5 1199(forming something)1 768 2 720 840 t
+10 CW f
+(jim)2717 840 w
+10 R f
+(that edits the output of \(and input to\) command ses-)9 2113 1 2927 840 t
+(sions rather than files.)3 882 1 720 960 t
+( paper describes the command language, then the mouse language, and explains)…
+( first of the host part, then of the)8 1290( is followed by a description of …
+( influenced the design of)4 991( principle that)2 555( A)1 122(terminal part.…
+10 CW f
+(sam)2952 1356 w
+10 R f
+(is that it should have no explicit limits, such as)9 1882 1 3158 1356 t
+( honor these two)3 663( To)1 161( secondary consideration is that it be effic…
+( breaking them into)3 783(goals together requires a method for efficiently ma…
+( control of the command language.)5 1407(lines, perhaps while making thousand…
+10 CW f
+(Sam)4460 1716 w
+10 R f
+('s method)1 400 1 4640 1716 t
+( updates may)2 541( These)1 295( as a transaction database, implementing chan…
+( is achieved through a collection of caches that minimizes)9 2328( Efficiency…
+(disc traffic and data motion, both within the two parts of the program and be…
+(The terminal part of)3 804 1 970 2232 t
+10 CW f
+(sam)1800 2232 w
+10 R f
+( interesting is how the two halves of the edi-)9 1791( More)1 268(is fairly s…
+( data structure that)3 746( is achieved through a)4 888( This)1 231(tor stay …
+(organizes the communications and is maintained in parallel by both halves.)10…
+(The last part of the paper chronicles the writing of)9 2067 1 970 2628 t
+10 CW f
+(sam)3069 2628 w
+10 R f
+( lessons that were learned)4 1057(and discusses the)2 702 2 3281 2628 t
+(through its development and use.)4 1324 1 720 2748 t
+( is composed largely of two papers of reasonable length: a description of the…
+(user interface of)2 653 1 720 3024 t
+10 CW f
+(sam)1401 3024 w
+10 R f
+( are combined because the implementa-)5 1597( They)1 258(and a discussion of …
+(tion is strongly influenced by the user interface, and vice versa.)10 2528 1 …
+10 B f
+(The Interface)1 579 1 720 3384 t
+10 CW f
+(Sam)720 3540 w
+10 R f
+( names may be provided when it is invoked:)8 1765( File)1 206(is a text edito…
+9 CW f
+(sam file1 file2 ...)3 1026 1 1008 3710 t
+10 R f
+( are not read until necessary to)6 1255( Files)1 251( files and discard unnee…
+( file is read; the)4 673( operations apply to an internal copy made when the)…
+( simplify the discussion, the)4 1124( To)1 162( is changed only by an explici…
+(internal copy is here called a)5 1144 1 720 4250 t
+10 I f
+(file)1889 4250 w
+10 R f
+(, while the disc-resident original is called a)7 1714 1 2017 4250 t
+10 I f
+(disc file.)1 339 1 3756 4250 t
+10 CW f
+(Sam)970 4406 w
+10 R f
+( connected to a bitmap display that presents a cut-and-paste editor driven by…
+( special window, called the)4 1116( this mode, the command language is still …
+10 CW f
+(sam)720 4646 w
+10 I f
+(window,)930 4646 w
+10 R f
+( editing may be)3 637( Cut-and-paste)1 616( as commands to be executed in the…
+(used in any window \320 even in the)7 1467 1 720 4766 t
+10 CW f
+(sam)2223 4766 w
+10 R f
+( mode of operation,)3 812( other)1 241( The)1 216(window to construct command…
+(invoked by starting)2 774 1 720 4886 t
+10 CW f
+(sam)1520 4886 w
+10 R f
+(with the option)2 608 1 1726 4886 t
+10 CW f
+(-d)2360 4886 w
+10 R f
+( the mouse or bitmap display,)5 1193(\(for `no download'\), does not use)5 13…
+( even on an ordinary terminal, interactively or)7 1849(but still permits edit…
+(from a script.)2 535 1 720 5126 t
+(The following sections describe first the command language \(under)8 2783 1 9…
+10 CW f
+(sam -d)1 360 1 3789 5282 t
+10 R f
+(and in the)2 418 1 4185 5282 t
+10 CW f
+(sam)4640 5282 w
+10 R f
+(win-)4857 5282 w
+( two languages are nearly independent, but connect through the)9 2573( These)…
+10 I f
+(current text,)1 488 1 720 5522 t
+10 R f
+(described below.)1 676 1 1233 5522 t
+10 B f
+(The Command Language)2 1090 1 720 5762 t
+10 R f
+( array of characters \(that is, a string\); the)8 1644(A file consists of its…
+10 I f
+(name)4125 5918 w
+10 R f
+(of the associated)2 671 1 4369 5918 t
+(disc file; the)2 498 1 720 6038 t
+10 I f
+(modified bit)1 483 1 1245 6038 t
+10 R f
+( those of the disc file; and a substring of the)10 1758(that states whether t…
+(contents, called the)2 786 1 720 6158 t
+10 I f
+(current text)1 472 1 1540 6158 t
+10 R f
+(or)2046 6158 w
+10 I f
+(dot)2163 6158 w
+10 R f
+( the current text is a null string, dot falls)9 1689( If)1 126( and 2\).)2 32…
+( The)1 211(between characters.)1 791 2 720 6278 t
+10 I f
+(value)1753 6278 w
+10 R f
+( the location of the current text; the)7 1441(of dot is)2 340 2 2000 6278 t
+10 I f
+(contents)3811 6278 w
+10 R f
+(of dot are the charac-)4 866 1 4174 6278 t
+(ters it contains.)2 610 1 720 6398 t
+10 CW f
+(Sam)1381 6398 w
+10 R f
+(imparts to the text no two-dimensional interpretation such as columns or fiel…
+( the idea of a `line' of text as understood by most Unix programs \320 a)15 3…
+(sequence of characters terminated by a newline character \320 is only weakly …
+(The)970 6794 w
+10 I f
+(current file)1 453 1 1156 6794 t
+10 R f
+( in the)2 264( current text is therefore dot)5 1141( The)1 211(is the file to…
+( explicitly name a particular file or piece of text, the command is)12 2827( …
+( presence of multiple files and consider)6 1629( the moment, ignore the)4 988…
+(editing a single file.)3 794 1 720 7154 t
+( for non-editing commands such as writing the file to disc,)10 2354( Except)1…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 2 2
+%%Page: 3 3
+/saveobj save def
+mark
+3 pagesetup
+10 R f
+(- 3 -)2 166 1 2797 480 t
+cleartomark
+saveobj restore
+%ps_include: begin
+save
+/ed {exch def} def
+{} /showpage ed
+{} /copypage ed
+{} /erasepage ed
+{} /letter ed
+currentdict /findfont known systemdict /findfont known and {
+ /findfont systemdict /findfont get def
+} if
+36 dict dup /PS-include-dict-dw ed begin
+/context ed
+count array astore /o-stack ed
+%ps_include: variables begin
+/llx 24 def
+/lly 241 def
+/urx 587.76 def
+/ury 550.6 def
+/w 0 def
+/o 0 def
+/s 0 def
+/cx 2880 def
+/cy -2220 def
+/sx 4320 def
+/sy 2520 def
+/ax 0.5 def
+/ay 0.5 def
+/rot 0 def
+%ps_include: variables end
+{llx lly urx ury} /bbox ed
+{newpath 2 index exch 2 index exch dup 6 index exch
+ moveto 3 {lineto} repeat closepath} /boxpath ed
+{dup mul exch dup mul add sqrt} /len ed
+{2 copy gt {exch} if pop} /min ed
+{2 copy lt {exch} if pop} /max ed
+{transform round exch round exch A itransform} /nice ed
+{6 array} /n ed
+n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed
+urx llx sub 0 A dtransform len /Sx ed
+0 ury lly sub A dtransform len /Sy ed
+llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed
+rot dup sin abs /S ed cos abs /C ed
+Sx S mul Sy C mul add /H ed
+Sx C mul Sy S mul add /W ed
+sy H div /Scaley ed
+sx W div /Scalex ed
+s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if
+sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed
+sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed
+urx llx sub 0 A dtransform exch atan rot exch sub /rot ed
+n currentmatrix initgraphics setmatrix
+cx cy translate
+Scalex Scaley scale
+rot rotate
+Cx neg Cy neg translate
+A concat
+bbox boxpath clip newpath
+w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if
+end
+gsave
+%ps_include: inclusion begin
+/picstr 98 string def
+24 241 translate
+563.76 309.60 scale
+
+783 430 1 [783 0 0 -430 0 430]
+{currentfile picstr readhexstring pop} image
+
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+02001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfffffffe7fffffffe227ff0feffffffffffffff3ff87ff11ffcffffffff
+fffffffe7fe7ffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffde01ffffe7fffffffe227fe7f9f833900fffffef3ff3e0711ffcffdffff1
+e0fffffe7fe7ffe7f3e0ffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfcfffffe7fffffffe233fe7f3f3b39e7fffffcf9ff3e7311ffcff9fffe9
+ce7fffffffe7ffc7e3ce7fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfcfe3ffe4f879cfff773f80f3e7f3de7f0e73019fc0673bbffc8e03ffe9
+ce7ff3907f07ffa7d3ce7fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfcfe3ffe27339cfff779fe7e7e7f25e7e6673cfcff3e73bbffc479fffd9
+ce7ff3de7e67ff67b3fe7fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfcfe3ffe7279cdfff779fe7e7e7f25e7ce737cfcff3e73bbffce79fffd9
+ce7ff25e7ce7ffe7f3fe7fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02ffdfcfffffe7279c3fffffcfe7e7e7f25e7ce70fcfe7f3e67ffffce79fffb9
+ce7ff25e7ce7ffe7f3fcffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfffffe7279f7fffffcfe7e7e7f93e7c07dfcfe7f3e0fffffce79fff80
+ce7ff25e7ce7ffe7f3f9ffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfffffe7279e1fffffe7e7e7e7f93e7cff87cff3f3e7fffffce79fff80
+ce7ff93e7ce7ffe7f3f3ffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe3ffe7279d9fffffe7e7e7e7f93e7cff67cff3f3e7fffffce79ffff9
+ce7ff93e7ce7ffe7f3e7ffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfe3ffe67339cffffff3e7e7f3b93e7e6673cff9f3e7fffffce79ffff9
+ce7ff93e7e47ffe7f3c07fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe3ffe0f879cffffff381f3f8393e7f0e73e1f9c0e7fffffce7c3ffe0
+e0fff9300f27ff81c0c07fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889ffffffffffffffffffff9fff3fffffffffffffffcffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221ffffffffffffffffffff9fff9fffffffffffffffcffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffe7fffffffe227ff0feffffffffffffff3ff87ff11ffcffffffff
+fffffffe7fe7fffffffffffcfffcffffffffffffffffffefffffffffffffffff
+fffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e07ffffe7fffffffe227fe7f9f833903fffffff3ff3e0711ffcffdffff1
+e0fffffe7fe7ff83c1fffffcff7cfffffffffffffeffff9c03ffffffffffff3e
+0fffff00ffffffffff3fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e73ffffe7fffffffe233fe7f3f3b3939fffffff9ff3e7311ffcff9fffe9
+ce7fffffffe7ff399cfffffffe7cfffffffffffffcffff3f9ffffffffffffe3c
+e7ffffe7ffffffffff9fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e73e3ffe4f879cfff773f80f3e7f3d39e0f07279fc0673bbffc8e03ffe9
+ce7ff3907f07ff399cffe720f80c8fffff9cfff0701fff3f9fffe1ffe73cfd3c
+ffffffe7fff87ff9cf9fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e73e3ffe27339cfff779fe7e7e7f2539ce67313cff3e73bbffc479fffd9
+ce7ff3de7e67ff399cffe7bcfe7c47ffff9effe73cfffe7f9fffccffe73cfb3c
+ffffffe7fff33ff9cfcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e77e3ffe7279cdfff779fe7e7e7f253bfe67f39cff3e73bbffce79fffd9
+ce7ff25e7ce7ff399cffe4bcfe7ce7ffff92ffff3cfffe7f9fff9cfff37cff3c
+ffffffe7ffe73ff9efcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e0fffffe7279c3fffffcfe7e7e7f2507fe63f39e7f3e67ffffce79fffb9
+ce7ff25e7ce7ff819cffe4bcfe7ce7ffff92ffff3cfffe7f9fff9cfff0e01f3c
+0fffffe7ffe73ffccfcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e67ffffe7279f7fffffcfe7e7e7f9333e070739e7f3e0fffffce79fff80
+ce7ff25e7ce7fff99cffe4bcfe7ce7ffff92fff03cfffe7f9fff80fffde01f3c
+e7ffffe7ffe03ffcdfcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e67ffffe7279e1fffffe7e7e7e7f9333ce7e339f3f3e7fffffce79fff80
+ce7ff93e7ce7fff99cfff27cfe7ce7ffffc9ffe73cfffe7f9fff9ffff87cff3c
+e7ffffe7ffe7fffcdfcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e73e3ffe7279d9fffffe7e7e7e7f9339ce7f339f3f3e7fffffce79ffff9
+ce7ff93e7ce7fff99cfff27cfe7ce7ffc7c9ffe73cfffe7f9fc79ff1f67cff3c
+e78fffe7f1e7fc7e3fcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e73e3ffe67339cffffff3e7e7f3b9339cc67333f9f3e7fffffce79ffff9
+ce7ff93e7e47ff399cfff27cfe7ce7ffc7c9ffe63cfffe7f9fc7ccf1e73cff3c
+e78fffe7f1f33c7e3fcfffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e71e3ffe0f879cffffff381f3f839338e270707f9c0e7fffffce7c3ffe0
+e0fff9300f27ff83c1fff2601f0ce7ffc7c9fff13e1fff3f9fc7e1f1e73ffc0e
+0f8fffe7f1f87c7f3f9fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889ffffffffffffffffffff9fff3ffffffffffff3ffcffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff3fffffffffffffffff
+ffcfffffffffffff7f9fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221ffffffffffffffffffff9fff9ffffffffffff3ffcffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff9fffffffffffffffff
+ffcffffffffffffe7f3fffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889ffffffffffffffffffffffffeffffffffffff3fffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffff
+ff9ffffffffffffc7effffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889e0fe7ffffffff0fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe7fffffffe7fffffffff807fffffffbfffff03ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcffffffffffe7ffffffffff3ffffffff3fffff39ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcf07c8f87ff80c670e5bfff3fffc3ffc070fff39ffe73fffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfe7c4733ffe7e066601fff3fff99fff3e67ff39ffe7bfffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe7ce673ffe7e64f249fff3fff39fff3cf3ff3bffe4bfffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfe7ce673ffe7e7cf249fff3fff39fff3cf3ff07ffe4bfffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe7ce603ffe7e7cf249fff3fff01fff3cf3ff33ffe4bfffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfe7ce67fffe7e7cf249fff3fff3ffff3cf3ff33fff27fffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fcfe7ce67fffe7e7cf249fff3f8f3ffff3cf3ff39f1f27fffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fcfe7ce733ffe7e7e6649fff3f8f99fff3e67ff39f1f27fffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221e0100ce787ff81c1f0e49fff3f8fc3fff870fff38f1f27fffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffff80000000800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c00000000001fffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff3f80000001800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889c00000000001fff87ffffffffffffffffffffcfffcfffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0f80078c67f00000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c00000000001fff3fffffffffc0ffffffffffcfffcffdfffc1f8fffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff1f800ccc61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889c00000000001fff3fffffffffce7fffffffffffffcff9fff9cf4fffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff3f8018cc61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c7c73398f0c7ffc0633872dffce7ffe1ffe320fe0c8e03ff9cf4fffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7d80180c61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889cc63f1f998c3fff3f033300ffce7ffccfff03cfccc479ffffcecfffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffff980180c61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c063319b0cdbfff3f327924ffcefff9cfff33cf9cce79ffffcecfffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffef080180c61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889c06301830cdbfff3f3e7924ffc1fff9cfff3fcf9cce79ffff9dcfffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffce080180c61800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c7e301830cdbfff3f3e7924ffccfff80fff3fcf9cce79ffff3c07ffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fff8c0000ccee1800000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889cc6301830c6dfff3f3e7924ffccfff9ffff3fcf9cce79fffe7c07ffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fff08000078760f00000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221cc6301830c6dfff3f3e7924ffce7c79ffff3fcf9cce79fffcffcfffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe00000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889cce30181986dfff3f3f3324ffce7c7ccfff3fcfc8ce79fff80fcfffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffc10000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c767c3e0f06dffc0e0f8724ffce3c7e1ffe0e01e4ce7c3ff80f07ffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ff837ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889c00000000001ffffffffffffffffffffffffffffcffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffc77ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221c00000000001fffffffffffffffffffffffffff9cffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffef7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889c00000000001fffffffffffffffffffffffffffc1ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fffffffff7ffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffe7ffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f3ffffffffcffffff9fffffc47ffffffcffffffffc47ffffe7ffe7fffff
+fffffffffffffffffcfffffe1ff83fffffffffffffffffffffffffffffffffff
+ffff7fc9f078380f0ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f3ffffffffcffffff9fffffc47dfffffcfffffff7c47ffffe7fbe7fffff
+ffffffff7ffffffffcfffffcffff3ffffff7ffffffffffffffffffffffffffff
+ffff7fc4e7339e7e67feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f3fffffffffffffffffffffc479fffffcffffffe7c47fffffff3e7fffff
+fffffffe7ffffffffcfffffcffff3fffffe7ffffffffffffffffffffffffffff
+ffff7fce7f33fe7ce7feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f27c3ce7fe0f91ce41f83ffeee0387ffc8f0f8380eefff3907c0647ffff
+ce7ff8380fff8723e0fff0f01fff3f078380fff07198ce1e73ffffffffffffff
+ffff7fce7f31fe7ce7feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f1399ce7ffcf88ce79f39ffeef9f33ffc466739e7eefff3de7f3e23ffff
+cf7ff39e7fff3311ccffe67cffff3e7339e7ffe7381c0cce7bffffffffffffff
+ffff7fce70383e7c07feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f393ce6fffcf9ccf79f3fffeef9e79ffce4f33fe7eefff25e7f3e73ffff
+c97fff9e7ffe73399cffcf3cffff3ff33fe7ffff399cc9e64bffffffffffffff
+ffff7fce673f1e7cfffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f393ce1fffcf9ce679f1ffffff9e79ffce4f31fe7fffff25e7f3e73ffff
+c97fff9e7ffe73399cffcf3cffff3ff31fe7ffff39fcf9e64bffffffffffffff
+ffff7fce673f9e7cfffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f393cfbfffcf9ce6f9f83fffff9e79ffce4f383e7fffff25e7f3e73ffff
+c97ff81e7ffe03399cffcf3cffff3f0383e7fff039fcf9e64bffffffffffffff
+ffff7fcce6339e7e67feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f393cf0fffcf9ce6f9ff1fffff9e79ffce4f3f1e7fffff93e7f3e73ffff
+e4fff39e7ffe7f399cffcf3cffff3e73f1e7ffe739fcf9e727ffffffffffffff
+ffff7fc1f1383f0f0ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f393cecfffcf9cf1f9ff9fffff9e79ffce4f3f9e7fffff93e7f3e73ffe3
+e4fff39e7ffe7f399cffcf3cffff3e73f9e7ffe739fcf9e727ffffffffffffff
+ffff7fcffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889f3399ce7ffcf9cf1f9f39fffff9f33ffce66739e7fffff93e7f3e73ffe3
+e4fff31e7fff3339c8ffe67cffff3e6339e7ffe639fcfccf27ffffffffffffff
+ffff7fcffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221f07c3ce7fe019cf1c0383fffffc387ffce70f83f0fffff9300f8673ffe3
+e4fff89f0fff8739e4fff0f03ff8071383f0fff130783e1f27ffffffffffffff
+ffff7fcffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fffffffffff87feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fffffffffff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fffffffffff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe0e47838cc07feffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fce62339c0f3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fcfe73f9ccf3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fc7e73f9cff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe0e7381cff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffc67339cff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffe67339cff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+02001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fce67331cff3ffeffffffffffffffffffffffffffffffffffffffffffff
+ff81
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00007fe0e738983c0ffe00000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00007ffffffffffffffe00000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00007ffffffffffffffe00000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00007ffffffffffffffe00000000000000000000000000000000000000000000
+0001
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffc1fffffcffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9fffffcffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffe1e7ffffffffff3ffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9fffffcffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffcfe7fff03fffff3ffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9f87c3ce7ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffcffffff03ffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9f3399cefffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffff0107f077fff9383f87fffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9e793ccdfffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffcfe7e677fff89f3f33fffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9e793ccbfffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffcfe7ce71fff9cf3e73fffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9e793cc3fffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fff80fffffcfe7ce7c7ff9cf3e7ffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9e793cc9fffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fff80fffffcfe7ce7f3ff9cf3e7ffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9e793cccfffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffcfe7ce7f3ff9cf3e7ffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fff9f3399ce7ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffff1fffcfe7ce7f3c79cf3e7ffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffc0387c3ce7ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffff1fffcfe7e4673c799f3f33fffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffff1fff0300f2707c78380787fffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffe7ffff9fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffce7ffff9fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffffe0fffff9fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffefffffffefffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ff9ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffcfffffffe7ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ff9ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ff9cb7399cf3ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ff3ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ff3c03399cf9ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ff3c1c6633879cfffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe7c9339cdfcffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fe79ce0703339efffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fc7c9339c3fc7feffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fe7fce67327992fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe7c9339f7fcffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fcffce7f3e7992fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ff3c9339e1f9ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcfc0e7f3e7992fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ff9c9339d9f3ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f9f9ce7f3e79c9fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffcc93119ce7ffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f9f9ce7f3e79c9fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffec93899cefffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f3f98e7f3f33c9fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3fc4c1e0f87c9fffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7ffffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe7fffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fe7fffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fcffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7fcf07198ce1e73effffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7f9e7381c0cce7beffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7f9ff399cc9e64beffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7f3ff39fcf9e64beffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7f3f039fcf9e64beffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7e7e739fcf9e727effffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7e7e739fcf9e727effffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7cfe639fcfccf27effffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff7cff130783e1f27effffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff79fffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff79fffffffffffffeffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221dffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+00000000000000000000000000000000000000000000077fef81fffff3ffffff
+fceec00c0303e633f060300060060fc77ffe7feffff8f07fffff3ff3fff3f9f0
+7fffff3fdf3fffffffffffffffdfff80e07ff01c1f8ffffffffffffffe000000
+0001
+0ffffffffffffffffffffffffffffffffffffffffffff77fef9ffffff3ffffff
+fcee600c060626330000300030060c677ffe7fcffff4e73ffffffff3ffe3f1e7
+3fffffff9f3fffffffffffffff9fff80e07ff019cf4ffffffffffffffeffffff
+ffe1
+0ffffffffffffffffffffffffffffffffffffffffffff77fef9ff1fff27c3ce7
+fc44603f860c061303e0301e301fcc622ffe4701fff4e73ff9c83f83ffd3e9e7
+3ff9c83e0323ffffe4739ffe0e03fffeefffffd9cf4ffffffffffffffeffffff
+ffe1
+0e001ffffffffffffffffffffffffffffffffffffffff77fef9ff1fff1399ce7
+fc44300c0c0c06d30060303318060c622ffe23cfffece73ff9ef3f33ffb3d9ff
+3ff9ef3f9f11ffffe233dffce79ffffcefffff99cecffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffcffffe7fffff77fef9ff1fff393ce6f
+fc44300c0c0c06d30060306318060c622ffe73cfffece73ff92f3e73fff3f9ff
+3ff92f3f9f39ffffe7325fffe79ffff9e3ffff39cecffffffffffffffeffffff
+ffe1
+0e221fffffffffffffffbffffffffffffcffffe7fffff77fef83fffff393ce1f
+fc00180c0c0c06d3e06030630c060cc00ffe73cfffdce73ff92f3e73fff3f9fe
+7ff92f3f9f39ffffe7325fffe79ffff3f8fffe79cdcffffffffffffffeffffff
+ffe1
+0e889fffffffffffffff3fffffffffffffffffe7fffff77fef9ffffff393cfbf
+fc00180c0c0c03630060307f0c060f800ffe73cfffc0673ff92f3e73fff3f9fc
+fff92f3f9f39ffffe7325ffe079fffe7fe7ffcf9cc07fffffffffffffeffffff
+ffe1
+0e221f19e1f0673c3c1c0707ffc1ffe720f91f070f39f77fef9ffffff393cf0f
+fc000c0c0c0c03630060306006060c000ffe73cfffc0673ffc9f3e73fff3f9f9
+fffc9f3f9f39ffffe7393ffce79fffe7fe7ffcf9cc07fffffffffffffeffffff
+ffe1
+0e889f81cce6673999cf3e73ff9cffe7bcf88e66673df77fef9ff1fff393cecf
+fc000c0c0c0c03630060306006060c000ffe73cffffce73ffc9f3e73fff3f9f3
+fffc9f3f9f39fff1e7393ffce79fffcffe78f9f9cfcffffffffffffffeffffff
+ffe1
+0e221f999cce673399ff3e7ffffcffe4bcf9cce4f325f77fef9ff1fff3399ce7
+fc00060c0c0623630060303303060c000ffe73cffffce73ffc9f3f23fff3f9e0
+3ffc9f3f9f39fff1e7393ffcc79fffcfce78f9f9cfcffffffffffffffeffffff
+ffe1
+0e889f9f9cce673398ff3e3ffffcffe4bcf9cce4f325f77fef9ff1fff07c3ce7
+fc00063f0603e36303fdfe1e031f8c000ffe73e1fff0707ffc980793ffc0e060
+3ffc9807c339fff1e7393ffe27c3ffcfe0f8f9fc1f07fffffffffffffeffffff
+ffe1
+0e221f9f80ce67301c1f3f07ffc0ffe4bcf9cce4f325f77fefffffffffffffff
+fc0003000600000000000000018000000fffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffcfffffffffffffffffffffeffffff
+ffe1
+0e889f9f9fce6733ff8f3fe3ff9cfff27cf9cce4f393f77fefffffffffffffff
+fc0003000300000000000000018000000fffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffcfffffffffffffffffffffeffffff
+ffe1
+0e221f9f9fce6733ffcf3ff3ff9cfff27cf9cce4f393f77fefffffffffffffff
+fc0000000080000000000000000000000fffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffff9fffffffffffffffffffffeffffff
+ffe1
+0e889f9fcce4623999cf3e73ff98fff27cf9ce466793f77fefffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221f07e1f2713c3c1f8707ffc4fff26019cf270f93f77fefffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffe7ffffffffffffffffffffffffffffffff77fefffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffe7ffffffffffffffffffffffffffffffff77fefffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffe7ffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fffe0ce7ffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fffcece7ffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fcf7ff23e1ce7fffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fc97ff11cccf7fffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fc97ff399cc97fffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fc97ff399cc97fffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fe4fff3980c97fffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fe4fff399fe4ffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fc79fe4fff399fe4fffc7fffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fc7cee4fff39cce4fffc7fffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fc7e0e4fff39e1e4fffc7fffffffffffffffffff7222f07f3ffffffff87
+fffffffffffffffffffffffffffff3ffff87c3ffffffffffff0f87ffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fffffffffffffffffffe7fffffffffffffffffff7088fe7f3ffffffff3f
+ffffffffe0f83fffffffefffffe733ffff3f9fffffffe07ffe7f3fffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fffffffffffffffffffe7fffffffffffffffffff7222fe7ffffffffff3f
+ffffffffe6739fffffffcfffffe733ffff3f9fffffffe73ffe7f3fffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fffffffffffffffffffcffffffffffffffffffff7088fe783e47c3ffc06
+33872dffe7339ffc8fff01c3ffe23279cc0603c38cffe733980c078719ffe0ff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffffffffffffffffffffffffffffffffffff7222fe7f3e2399fff3f
+033300ffe73f9ffc47ffcf99ffe23139cf3f9f99c0ffe7339e7f3f3381ffce7f
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fe7f3e7339fff3f
+327924ffe73f9ffce7ffcf3cffe93399cf3f9f39ccffe7739e7f3e7399ffcfff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fffe0ce7ffffffffffffffffffffffffffffffff7222fe7f3e7339fff3f
+3e7924ffe73f3ffce7ffcf3cffe93399cf3f9f39cfffe0f39e7f3e739fffc7ff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fffcece7ffffffffffffffffffffffffffffffff7088fe7f3e7301fff3f
+3e7924ffe73e7ffce7ffcf3cffe93399cf3f9f01cfffe7739e7f3e039fffe0ff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fcf7ff39e1c670f39fffffffffffffffffff7222fe7f3e733ffff3f
+3e7924ffe73cfffce7ffcf3cffe93399cf3f9f3fcfffe7339e7f3e7f9ffffc7f
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fc97ff39cce066739fffffffffffffffffff7088fe7f3e733ffff3f
+3e7924ffe739fe3ce7ffcf3cffef3399cf3f9f3fcff1e7339e7f3e7f9fe3fe7f
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fc97ff9b9ce64f39bfffffffffffffffffff7222fe7f3e7399fff3f
+3f3324ffe6701e3ce7ffcf99ffef33388f3f9f99cff1e7311e7f3f339fe3ce7f
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fc97ff879ce7cf387fffffffffffffffffff7088f0080673c3ffc0e
+0f8724ffe0f01e3ce7ffe1c3ffef307c4c0e07c383f1e078981c0f8707e3e0ff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fff9fe4fffef80e7cf3effffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fff9fe4fffc39fe7cf3c3fffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fc79fe4fffb39fe7cf3b3fffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889fc7cee4fff39cce7e6739fffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221fc7e0e4fff39e1c1f0f39fffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221ffffffffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221f878cfffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889f33c0fffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221e79ccfffffffffffffffffffffffffffffffffff7222fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889e79cffffffffffffffffffffffffffffffffffff7088fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221e79cffffffffffffffffffffffffffffffffffff7000fffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889e79cffffffffffffffffffffffffffffffffffff7ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e221e79cffffffffffffffffffffffffffffffffffff7ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff
+ffe1
+0e889f33cffffffffffffffffffffffffffffffffffff0000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000ffffff
+ffe1
+0e221f8783ffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffe7fffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffe0ce7ffffffffe7fffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffcece7ffffffffe7fffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fff9fcf7ff19e1e0e478393f0ffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fff9fc97ff81ccce6233989e67fffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfff9fc97ff999ccfe73f99cce7fffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0effdfff9fc97ff9f9cc7e73f99cce7fffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fff9fe4fff9f80e0e73819cc07fffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fff9fe4fff9f9ffc673399ccfffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fc79fe4fff9f9ffe673399ccffffc7fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fc7cee4fff9fccce6733199e67ffc7fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fc7e0e4fff07e1e0e738983f0fffc7fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffff9ffffffe7fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffff9ffffffe7fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffff9ffffffcffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcffffffff3fffffffffffffffff3ffffffffffffffffffffffffffffff
+fffffe1fffffffffffcfffffffffffffff07fffffffffffffffffffffffffffc
+fffffffffff9fff9ffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fcff7fffff3fffffffffffffffdf3ffffffffffffffffffffffffffffff
+fffffcfffffffffff7cfffffffffffffffe7fffffffffffffffff7fffffffffc
+fffffffffff9fef9ffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffe7fffff3fffffffffffffff9f3ffffffffffffffffffffffffffffff
+fffffcffffffffffe7cfffffffffffffffe7ffffffffffffffffe7fffffffffc
+fffffffffffffcf9ffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221e0f80fff8723c1c8f83c3c1ffe0323e1ffcb7879ce0f87ffe1ce63383c3
+c67ff018ce1cb7ff80c8f87ff9ce0e7383e7fff07198ce1e73ff80e1fff83ffc
+9f0f39ffce41f0191fffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcfe7fff33119cc4733999cfff9f11ccffc03339cce733ffccce7033999
+e07ffcfc0ccc03ffe7c4733ff9cce67339e7ffe7381c0cce7bffe7ccfff39ffc
+4e6739ffcf79fcf88fffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fcfe7ffe7339fcce673399ffff9f399cffc92799ccfe73ff9cce7333f3c
+e67ffcfcc9e493ffe7ce673ff9ccfe73f9e7ffff399cc9e64bffe79e7fff9ffc
+e4f39bffc979fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcfe7ffe7f39fcce673398ffff9f399cffc92799cc7e73ff9fce73f1f3c
+e7fffcfcf9e493ffe7ce673ff9cc7e73f9e7ffff39fcf9e64bffe79e7fff9ffc
+e4f387ffc979fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fcfe7ffe7f39c0ce67301c1fff9f3980ffc92799ce0e03ff9fce73f833c
+e7fffcfcf9e493ffe7ce603ff9ce0e7381e7fff039fcf9e64bffe79e7ff81ffc
+e4f3efffc979fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcfe7ffe7f399cce6733ff8fff9f399fffc92799cfc67fff9fce73ff13c
+e7fffcfcf9e493ffe7ce67fff9cfc67339e7ffe739fcf9e727ffe79e7ff39ffc
+e4f3c3ffe4f9fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fcfe7ffe7f399cce6733ffcfff9f399fffc92799cfe67fff9fce73ff93c
+e7fffcfcf9e493ffe7ce67fff9cfe67339e7ffe739fcf9e727ffe79e7ff39ffc
+e4f3b3ffe4f9fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fcfe7fff333998ce723999cfff9f39ccffc933388ce733ffccc473f3999
+e7fffcfcfccc93ffe7ce733ff88ce62331e7ffe639fcfccf27ffe7ccfff31ffc
+ce6739ffe4f9fcf9cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221e01f0fff8739c4ce793c3c1fffc339e1ffc9387c4e0f87ffe1e260f83c3
+c1fff0383e1c93fff0ce787ffc4e0f138900fff130783e1f27fff0e1fff89ffc
+1f0f39ffe4c03e19cfffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffff3ffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffff07ffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffff0783fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffe7f3fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffe7f3fffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f83ffe0cb783e7f3fff838cc670f39fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f39ffce40339e7f3fff39c0e06673dfffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ff9ffcfc93f9e7f3ffff9cce64f325fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ff9ffc7c93f9e7f3ffff9cfe7cf325fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f81ffe0c9381e7f3fff81cfe7cf325fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f39fffc49339e7f3fff39cfe7cf393fffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f39fffe49339e7f3fff39cfe7cf393e3fffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f31ffce49331e7f3fff31cfe7e6793e3fffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f89ffe0c938900807ff8983c1f0f93e3fffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000
+000000000000001fffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffff3fe7fffffffffffffffffffffff3fffffffffffffffffffff
+fffffff9ffffffffffffff3fffffffffcffffe79ffffffffffffffffffc00003
+006000000000001ffffffffffffffffff07fffff3fffffffffffffffffffffff
+ffe1
+0e221f03fffffdf3fe7ffffffff7fffdffffffffdf3fffffffffffffffffffff
+fffffff9ffffffffffffff3fff7fffffcffffe79ffffffbfffffffffffc00003
+006010000000001ffffffffff7fffffffe7fffff3fffffffffffffffffffffff
+ffe1
+0e889fcffffff9f3ffffffffffe7fff9ffffffff9f3fffffffffffffffffffff
+fffffff9ffffffffffffff3ffe7ffffffffffe7fffffff3fffffffffffc00003
+000030000000001fffffffffe7fffffffe7fffff3fffffffffffffffffffffff
+ffe1
+0e221fcf91ffe032307e0fff8380e0e03c3ffffe0323e1ffcb7879ce0f87ff2d
+e0e73ff93f0fff39c1f0f83ff80e1ffe0f91f041fc3c1c0787ffc1c8ffc7c733
+63e0fe731f1ccc7ffc6787c380e0e47c1e7f0fff279cffffffffffffffffffff
+ffe1
+0e889fcf88fff9f11e7ce7ff39e7ce79f99fffff9f11ccffc03339cce733ff00
+ce673ff89e67ff399ce6733ffe7ccfffcf88e679f999cf3f33ff9cc47fcc63f3
+b060303f318fcc7ffe073399e7ce62399e7e67ff139cffffffffffffffffffff
+ffe1
+0e221fcf9cfff9f39e7cffff3fe7fe79f39fffff9f399cffc92799ccfe73ff24
+fe67bff9cce7ff399fce673ffe79e7ffcf9cce79f39fcf3e73fffcce7fc06333
+18603033018ccc3ffe667339e7fe67339e7ce7ff399effffffffffffffffffff
+ffe1
+0e889fcf9cfff9f39e7c7fff1fe7fe79f39fffff9f399cffc92799cc7e73ff24
+fe733ff9cce7ff398fce673ffe79e7ffcf9cce79f3ffcf3e73fffcce7fc06303
+18603030018c067ffe7e733fe7fe67339e7ce7ff39ccffffffffffffffffffff
+ffe1
+0e221fcf9cfff9f39e7e0fff83e7e079f01fffff9f3980ffc92799ce0e03ff24
+e0737ff9cc07ff39c1c0673ffe79e7ffcf9cce79f3fc0f3e03ffc0ce7fc7e303
+186030301f8c065ffe7e033fe7e067339e7c07ff39cdffffffffffffffffffff
+ffe1
+0e889fcf9cfff9f39e7fc7fff1e7ce79f3ffffff9f399fffc92799cfc67fff24
+ce737ff9ccffff39f8cfe73ffe79e7ffcf9cce79f3f9cf3e7fff9cce7fcc6303
+18603030318c065ffe7e7f3fe7ce67339e7cffff39cdffffffffffffffffffff
+ffe1
+0e221fcf9cfff9f39e7fe7fff9e7ce79f3fe3fff9f399fffc92799cfe67fff24
+ce78fff9ccffff39fccfe73ffe79e7ffcf9cce79f3f9cf3e7fff9cce7fcc6303
+18603030318c039ffe7e7f3fe7ce67339e7cffff39e3ffffffffffffffffffff
+ffe1
+0e889fcf9cfff9f39e7ce7ff39e7cc79f99e3fff9f39ccffc933388ce733ff24
+cc78fff99e67ff119ce6723ffe7ccfffcf9ce479f9998f3f33ff98ce7fcce303
+30603030338c039ffe7f3399e7cc67391e7e67ff33e3ffffffffffffffffffff
+ffe1
+0e221f039cfffc33900e0fff83f0e27c3c3e3fffc339e1ffc9387c4e0f87ff24
+e27cfff83f0fff89c1f0f93fff0e1ffe019cf2403c3c4f8787ffc4ce7fc767c3
+e3fc1e7c1d9f019ffc1f87c3f0e2673c900f0fff07f3ffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffff3fffffffffffffffffffffffffff
+fffdffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000
+000000000000011fffffffffffffffff9ffffffffff7ffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffff3fffffffffffffffffffffffffff
+fff9ffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000
+000000000000031ffffffffffffffff39fffffffffe7ffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffffffffffffffffe7fffffffffffffffffffffffffff
+fff1ffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000
+000000000000071ffffffffffffffff83fffffffffc7ffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffe7fffffff9fffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffe7ffffff83ffffffff3ffffffff3ffffffffffffff9ff
+fffffffffffffffe7fffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffe7fffffff9ffffdfeffffffff83fffffdffffffffffffff
+fffffffffffffffffffe7fffffff3ffffffff3ffffffff3fdffffffbfffef9ff
+fffffffffffffffe7fbfffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffff9ffff9fcffffffff39fffff9ffffffffffffff
+fffffffffffffffffffe7fffffff3fffffffffffffffffff9ffffff3fffcf9ff
+ffffffffffffffffff3fffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f278cf0f078307c8f83ff93ce60301c3c8fff39ffe0e03ffc3c8f87ffe1
+e1e3323e1c67ff8391f07ff19e1f3f8783c183e47c1ff83e03ffc1c07ff0191f
+0fff8793c9f0f8307c0787ffe1e1e3323e1c67ffffffffffffffffffffffffff
+ffe1
+0e889f13c0e667339e7c4733ff89ce79fcf99c47fff9ffce79fff99c4733ffcc
+ccf0311cce07ff3988e67ff81ccf3f33399cf3e2399fff3f9fff9cf3fffcf88e
+67ff3389c4e6739e7f3f33ffccccf0311cce07ffffffffffffffffffffffffff
+ffe1
+0e221f39ccce67f3fe7ce673ff9cce79fcf3cce7fff9fffe79fff3cce673ff9c
+9e733399ce67fff99cce7ff999cf3e73f99ff3e7339fff3f9ffffcf3fffcf9cc
+e7fe799cce4f33fe7f3e73ff9c9e733399ce67ffffffffffffffffffffffffff
+ffe1
+0e889f39cfce63f1fe7ce673ff9cce79fcf3cce7ffc7fffe79fff3cce673ff9f
+9e73f399ce7ffff99cce7ff9f9cf3e73f98ff3e7339fff3f9ffffcf3fffcf9cc
+e7fe799cce4f31fe7f3e73ff9f9e73f399ce7fffffffffffffffffffffffffff
+ffe1
+0e221f39cfc070783e7ce673ff9cce79fcf3cce7fff9ffe079fff3cce603ff9f
+9e73f3980e7fff819cce7ff9f80f3e0381c1f3e7339fff3f9fffc0f3fffcf9cc
+07fe799cce4f383e7f3e03ff9f9e73f3980e7fffffffffffffffffffffffffff
+ffe1
+0e889f39cfcffe3f1e7ce673ff9cce79fcf3cce7fff9ffce79fff3cce67fff9f
+9e73f399fe7fff399cce7ff9f9ff3e7f39f8f3e7339fff3f9fff9cf3fffcf9cc
+fffe799cce4f3f1e7f3e7fff9f9e73f399fe7fffffffffffffffffffffffffff
+ffe1
+0e221f39cfcfff3f9e7ce673ff9cce79fcf3cce7fff9ffce79fff3cce67fff9f
+9e73f399fe7fff399cce7ff9f9ff3e7f39fcf3e7339fff3f9fff9cf3fffcf9cc
+fffe799cce4f3f9e7f3e7fff9f9e73f399fe7f8fffffffffffffffffffffffff
+ffe1
+0e889f33cfe667339e7ce723ff99c479fcf99ce7ff39ffcc79fff99ce733ffcc
+ccf3f39cce7fff319ce47ff9fccf3f33319cf3e7391fff3f9fff98f3fffcf9ce
+67ff3399cce6739e7f3f33ffccccf3f39cce7f8fffffffffffffffffffffffff
+ffe1
+0e221f0783f0f078300ce793ff83e27c3e1c3ce7ff83ffe27c3ffc3ce787ffe1
+e1e0f39e1c1fff899cf27ff07e18078789c180673c9ff807c3ffc4f87ffe19cf
+0fff8783c1f0f8300f8787ffe1e1e0f39e1c1f8fffffffffffffffffffffffff
+ffe1
+0e889f3ffffffffffffffff3ffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffff9fffffffffffffffffffff
+ffffff9fcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f3ffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffff39fffffffffffffffffffff
+ffffff9fcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3fffffffffffffff07ffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffffffffffffffffff83fffffffffffffffffffff
+ffffff9fcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffffffffffff9fffffffc1fffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f39fffffffffffffffffffffff9ffffffbf9fffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f39ffffffffffffffffffffffffffffff3f9fffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f11c3c6787ffe1e1e4739e1c8c1fc391c079f39ffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f1199e0733ffcccce2339ccc479f9988f3f9f39ffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f493ce6673ff9c9e6733d9cce79f399cf3f9f3dffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f493ce7e73ff9f9e673999cce79f399cf3f9f99ffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f493ce7e03ff9f9e6739b80ce79f019cf3f9f9bffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f493ce7e7fff9f9e6739b9fce79f3f9cf3f9f9bffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f793ce7e7fff9f9e673c79fce79f3f9cf3f9fc7e3ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f7999e7f33ffcccce73c7ccce79f999cf3f9fc7e3ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f79c3c1f87ffe1e1e73c7e1ce403c39cf8403e7e3ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffeff3ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffcff3ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffff8fe7ffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3fffffffffffffffffffffffffffffff3fffff07fffff3fffffffe0fe7
+ffe7ffffcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f3ffffbfdffffffff07ffffffffffffff3fffffe7fffff3ffffffffcfe7
+ffe7ffffcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3ffff3f9fffffffe73ffffffffffffffffffffe7fffff3ffffffffcfff
+ffe7ffffcfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f279cc06038791ffe73ff2de0e73ffc183cb727e7ce7ff27e1fff87cf07
+f0e73c3e0fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f139cf3f9f3388ffff3ff00ce673ff9cf3c0313e7ce7ff13ccfff33cfe7
+e667799ccfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f399cf3f9e799cffff3ff24fe67bff9ff3c9339e7cf7ff399cffe73cfe7
+ce66f399cfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f399cf3f9e799cfff8fff24fe733ff8ff3c9339e7e67ff399cffe7fcfe7
+cfe5f399cfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f399cf3f9e799cffff3ff24e0737ffc1f3c9339e7e6fff3980ffe7fcfe7
+cfe1f019cfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f399cf3f9e799cffff3ff24ce737fff8f3c9339e7e6fff399fffe7fcfe7
+cfe4f3f9cfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f399cf3f9e799cffff3ff24ce78ffffcf3c9339e7f1fff399fffe7fcfe7
+cfe673f9cf1fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3388f3f9f339cffe73ff24cc78fff9cf3c9333e7f1fff33ccfff33cfe7
+e667399c8f1fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f07c4f87c3879cfff07ff24e27cfffc1804930700f9fff07e1fff860100
+f0e73c3e4f1fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffffffffffffdffffffffff3ffffbffffffffffffffff
+ffffffffff9fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffffffffffffffff9ffffffffff3ffff3ffffffffffffffff
+ffffffffff9fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffffffffffffff1ffffffffff3fffe3ffffffffffffffff
+ffffffffff3fffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fff9fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fff9fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fff9fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f3991f0e33c39cc9f0f23ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f3d88e6703999cc4e6711ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f259cce733399cce4f339ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f259cce73f399cce4f339ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f259cc073f019cce4f339ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f939ccff3f3f9cce4f339ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f939ccff3f3f9cce4f339ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889f939ce673f9988cce6739ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221f939cf0e0fc3c4c1f0f39ffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffcffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221ffffffffffffffcffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889ffffffffffffffcffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e221fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e889fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0e001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffe1
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0001
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffff
+showpage
+%ps_include: inclusion end
+grestore
+PS-include-dict-dw begin
+o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice
+ initgraphics 0.1 setlinewidth boxpath stroke grestore} if
+clear o-stack aload pop
+context end restore
+%ps_include: end
+/saveobj save def
+mark
+8 I f
+(Figure 1. A typical)3 623 1 720 3580 t
+8 CW f
+(sam)1370 3580 w
+8 I f
+( The)1 167(screen, with the editing menu presented.)5 1320 2 1541 3580 t
+8 CW f
+(sam)3055 3580 w
+8 I f
+(\(command language\) window is in the middle, with file)8 1814 1 3226 3580 t
+( partially obscured window is a)5 1029( The)1 164( user interface makes it ea…
+( Each)1 206( typing and mouse operations apply, as indicated by its heavy bor…
+( The)1 162(window has its current text highlighted in reverse video.)8 1810 2…
+8 CW f
+(sam)2714 3880 w
+8 I f
+( string on the last visible line, indi-)7 1121(window's current text is the n…
+( also Figure 2.)3 470( See)1 150(cated by a vertical bar.)4 740 3 720 3980 t
+10 R f
+( and leave dot set to the text resulting from the change.)11 2202(most comman…
+(For example, the delete command,)4 1389 1 720 4340 t
+10 CW f
+(d)2135 4340 w
+10 R f
+(, deletes the text in dot, replacing it by the null string and setting dot to…
+( change command,)2 767( The)1 213(the result.)1 402 3 720 4460 t
+10 CW f
+(c)2135 4460 w
+10 R f
+(, replaces dot by text delimited by an arbitrary punctuation character,)10 28…
+( Thus,)1 275(conventionally a slash.)2 913 2 720 4580 t
+9 CW f
+(c/Peter/)1008 4750 w
+10 R f
+(replaces the text in dot by the string)7 1429 1 720 4930 t
+10 CW f
+(Peter)2174 4930 w
+10 R f
+(. Similarly,)1 473 1 2474 4930 t
+9 CW f
+(a/Peter/)1008 5100 w
+10 R f
+(\(append\) adds the string after dot, and)6 1516 1 720 5280 t
+9 CW f
+(i/Peter/)1008 5450 w
+10 R f
+( three leave dot set to the new text,)8 1389( All)1 178(\(insert\) inserts be…
+10 CW f
+(Peter)3343 5630 w
+10 R f
+(.)3643 5630 w
+( lexically terminates a command.)4 1335(Newlines are part of the syntax of co…
+( it is often convenient to insert)6 1282( since)1 242( But)1 207(Within the i…
+(multiple lines of text,)3 856 1 720 6026 t
+10 CW f
+(sam)1601 6026 w
+10 R f
+(has a special syntax for that case:)6 1330 1 1806 6026 t
+9 CW f
+(a)1008 6196 w
+(some lines of text)3 972 1 1008 6306 t
+(to be inserted in the file,)5 1458 1 1008 6416 t
+(terminated by a period)3 1188 1 1008 6526 t
+(on a line by itself)4 1026 1 1008 6636 t
+(.)1008 6746 w
+10 R f
+(In the one-line syntax, a newline character may be specified by a C-like esca…
+9 CW f
+(c/\\n/)1008 7096 w
+10 R f
+(replaces dot by a single newline character.)6 1692 1 720 7276 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 3 3
+%%Page: 4 4
+/saveobj save def
+mark
+4 pagesetup
+10 R f
+(- 4 -)2 166 1 2797 480 t
+10 CW f
+(Sam)970 840 w
+10 R f
+(also has a substitute command,)4 1241 1 1175 840 t
+10 CW f
+(s)2441 840 w
+10 R f
+(:)2501 840 w
+9 CW f
+(s/)1008 1010 w
+9 I f
+(expression)1116 1010 w
+9 CW f
+(/)1501 1010 w
+9 I f
+(replacement)1555 1010 w
+9 CW f
+(/)2000 1010 w
+10 R f
+( if dot is the)4 530( Thus,)1 288( expression.)1 490(substitutes the replacem…
+(string)720 1310 w
+10 CW f
+(Peter)973 1310 w
+10 R f
+(, the command)2 591 1 1273 1310 t
+9 CW f
+(s/t/st/)1008 1480 w
+10 R f
+(changes it to)2 517 1 720 1660 t
+10 CW f
+(Pester)1268 1660 w
+10 R f
+( general,)1 349(. In)1 164 2 1628 1660 t
+10 CW f
+(s)2172 1660 w
+10 R f
+(is unnecessary, but it was inherited from)6 1653 1 2263 1660 t
+10 CW f
+(ed)3947 1660 w
+10 R f
+( has some conve-)3 711(and it)1 231 2 4098 1660 t
+( instance, the replacement text may include the matched text, specified by)11…
+10 CW f
+(&)4525 1780 w
+10 R f
+(:)4585 1780 w
+9 CW f
+(s/Peter/Oh, &, &, &, &!/)4 1296 1 1008 1950 t
+10 R f
+(There are also three commands that apply programs to text:)9 2376 1 970 2166 t
+9 CW f
+(<)1008 2336 w
+9 I f
+(Unix program)1 513 1 1116 2336 t
+10 R f
+( the)1 149( Similarly,)1 450(replaces dot by the output of the Unix program.)…
+10 CW f
+(>)3254 2516 w
+10 R f
+(command runs the program with dot as its)7 1699 1 3341 2516 t
+(standard input, and)2 763 1 720 2636 t
+10 CW f
+(|)1508 2636 w
+10 R f
+( example,)1 388( For)1 189(does both.)1 411 3 1593 2636 t
+9 CW f
+(| sort)1 324 1 1008 2806 t
+10 R f
+( special sig-)2 483( newlines have no)3 724( Again,)1 321(replaces dot by the…
+( these)1 235(nificance for)1 512 2 720 3106 t
+10 CW f
+(sam)1497 3106 w
+10 R f
+( text acted upon and resulting from these commands is not neces-)11 2665(comm…
+(sarily bounded by newlines, although for connection with Unix programs, newli…
+(obey conventions.)1 727 1 720 3346 t
+(One more command:)2 843 1 970 3502 t
+10 CW f
+(p)1838 3502 w
+10 R f
+( I summarizes)2 560( Table)1 277(prints the contents of dot.)4 1019 3 1923 35…
+10 CW f
+(sam)3804 3502 w
+10 R f
+('s commands.)1 555 1 3984 3502 t
+(The value of dot may be changed by specifying an)9 2015 1 970 3658 t
+10 I f
+(address)3010 3658 w
+10 R f
+( simplest address is)3 778( The)1 206(for the command.)2 709 3 3347 3658 t
+(a line number:)2 577 1 720 3778 t
+9 CW f
+(3)1008 3948 w
+10 R f
+(refers to the third line of the file, so)8 1417 1 720 4128 t
+9 CW f
+(3d)1008 4298 w
+10 R f
+( 3.)1 111(deletes the third line of the file, and implicitly renumbers the li…
+(\(This is one of the few places where)7 1485 1 720 4598 t
+10 CW f
+(sam)2236 4598 w
+10 R f
+( Line)1 239(deals with lines directly.\))3 1028 2 2447 4598 t
+10 CW f
+(0)3745 4598 w
+10 R f
+( string at the begin-)4 797(is the null)2 407 2 3836 4598 t
+( a command consists of only an address, a)8 1846( If)1 137(ning of the file.)…
+10 CW f
+(p)3429 4718 w
+10 R f
+(command is assumed, so typing an)5 1504 1 3536 4718 t
+(unadorned)720 4838 w
+10 CW f
+(3)1173 4838 w
+10 R f
+( are a couple of other basic addresses: a period addresses)10 2325( There)1 2…
+(dot itself; and a dollar sign \()6 1127 1 720 4958 t
+10 CW f
+($)1847 4958 w
+10 R f
+(\) addresses the null string at the end of the file.)10 1872 1 1907 4958 t
+( the address)2 477( Thus,)1 278( single substring of the file.)5 1109(An addr…
+10 CW f
+(3)3779 5114 w
+10 R f
+(addresses the characters after)3 1173 1 3867 5114 t
+( A)1 125( file.)1 186(the second newline of the file through the third newlin…
+10 I f
+(compound address)1 755 1 3574 5234 t
+10 R f
+(is constructed by)2 683 1 4357 5234 t
+(the comma operator)2 798 1 720 5354 t
+9 I f
+(address1)1008 5524 w
+9 CW f
+(,)1333 5524 w
+9 I f
+(address2)1387 5524 w
+10 R f
+( the substring of the file from the beginning of)9 1860(and addresses)1 551 2…
+10 I f
+(address1)3157 5704 w
+10 R f
+(to the end of)3 505 1 3544 5704 t
+10 I f
+(address2)4075 5704 w
+10 R f
+( example,)1 389(. For)1 215 2 4436 5704 t
+(the command)1 543 1 720 5824 t
+10 CW f
+(3,5p)1290 5824 w
+10 R f
+(prints the third through fifth lines of the file and)9 1936 1 1557 5824 t
+10 CW f
+(.,$d)3520 5824 w
+10 R f
+( the begin-)2 429(deletes the text from)3 824 2 3787 5824 t
+(ning of dot to the end of the file.)8 1296 1 720 5944 t
+(These addresses are all absolute positions in the file, but)9 2247 1 970 6100…
+10 CW f
+(sam)3242 6100 w
+10 R f
+( indicated by)2 518(also has relative addresses,)3 1075 2 3447 6100 t
+10 CW f
+(+)720 6220 w
+10 R f
+(or)805 6220 w
+10 CW f
+(-)913 6220 w
+10 R f
+( example,)1 388(. For)1 214 2 973 6220 t
+9 CW f
+($-3)1008 6390 w
+10 R f
+(is the third line before the end of the file and)10 1780 1 720 6570 t
+9 CW f
+(.+1)1008 6740 w
+10 R f
+( no address appears to the left of the)8 1457( If)1 118(is the line after dot…
+10 CW f
+(+)3104 6920 w
+10 R f
+(or)3191 6920 w
+10 CW f
+(-)3301 6920 w
+10 R f
+(, dot is assumed; if nothing appears to the)8 1679 1 3361 6920 t
+(right,)720 7040 w
+10 CW f
+(1)959 7040 w
+10 R f
+( Therefore,)1 467(is assumed.)1 461 2 1044 7040 t
+10 CW f
+(.+1)1997 7040 w
+10 R f
+(may be abbreviated to just a plus sign.)7 1532 1 2202 7040 t
+(The)970 7196 w
+10 CW f
+(+)1156 7196 w
+10 R f
+(operator acts relative to the end of its first argument, while the)11 2556 1 …
+10 CW f
+(-)3834 7196 w
+10 R f
+(operator acts relative to the)4 1114 1 3926 7196 t
+(beginning. Thus)1 679 1 720 7316 t
+10 CW f
+(.+1)1428 7316 w
+10 R f
+( first line after dot,)4 758(addresses the)1 533 2 1637 7316 t
+10 CW f
+(.-)2956 7316 w
+10 R f
+(addresses the first line before dot, and)6 1534 1 3104 7316 t
+10 CW f
+(+-)4666 7316 w
+10 R f
+(refers)4814 7316 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 4 4
+%%Page: 5 5
+/saveobj save def
+mark
+5 pagesetup
+10 R f
+(- 5 -)2 166 1 2797 480 t
+(Table I.)1 310 1 2393 900 t
+10 CW f
+(Sam)2728 900 w
+10 R f
+(commands)2933 900 w
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(Text commands)1 641 1 1077 1136 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(a/)1077 1372 w
+10 I f
+(text)1197 1372 w
+10 CW f
+(/)1341 1372 w
+10 R f
+(Append text after dot)3 851 1 2160 1372 t
+10 CW f
+(c/)1077 1492 w
+10 I f
+(text)1197 1492 w
+10 CW f
+(/)1341 1492 w
+10 R f
+(Change text in dot)3 736 1 2160 1492 t
+10 CW f
+(i/)1077 1612 w
+10 I f
+(text)1197 1612 w
+10 CW f
+(/)1341 1612 w
+10 R f
+(Insert text before dot)3 834 1 2160 1612 t
+10 CW f
+(d)1077 1732 w
+10 R f
+(Delete text in dot)3 691 1 2160 1732 t
+10 CW f
+(s/)1077 1852 w
+10 I f
+(regexp)1197 1852 w
+10 CW f
+(/)1468 1852 w
+10 I f
+(text)1528 1852 w
+10 CW f
+(/)1672 1852 w
+10 R f
+(Substitute text for match of regular expression in dot)8 2109 1 2160 1852 t
+10 CW f
+(m)1077 1972 w
+10 I f
+(address)1197 1972 w
+10 R f
+(Move text in dot after address)5 1195 1 2160 1972 t
+10 CW f
+(t)1077 2092 w
+10 I f
+(address)1197 2092 w
+10 R f
+(Copy text in dot after address)5 1179 1 2160 2092 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(Display commands)1 769 1 1077 2328 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(p)1077 2540 w
+10 R f
+(Print contents of dot)3 814 1 2160 2540 t
+10 CW f
+(=)1077 2660 w
+10 R f
+(Print value \(line numbers and character numbers\) of dot)8 2234 1 2160 2660 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(File commands)1 614 1 1077 2896 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(b)1077 3108 w
+10 I f
+(file-list)1197 3108 w
+10 R f
+(Set current file to first file in list that)8 1466 1 2160 3108 t
+10 CW f
+(sam)3651 3108 w
+10 R f
+(has in menu)2 483 1 3856 3108 t
+10 CW f
+(B)1077 3228 w
+10 I f
+(file-list)1197 3228 w
+10 R f
+(Same as)1 330 1 2160 3228 t
+10 CW f
+(b)2515 3228 w
+10 R f
+(, but load new files)4 763 1 2575 3228 t
+10 CW f
+(n)1077 3348 w
+10 R f
+(Print menu lines of all files)5 1086 1 2160 3348 t
+10 CW f
+(D)1077 3468 w
+10 I f
+(file-list)1197 3468 w
+10 R f
+(Delete named files from)3 967 1 2160 3468 t
+10 CW f
+(sam)3152 3468 w
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(I/O commands)1 591 1 1077 3704 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(e)1077 3916 w
+10 I f
+(filename)1197 3916 w
+10 R f
+(Replace file with named disc file)5 1317 1 2160 3916 t
+10 CW f
+(r)1077 4036 w
+10 I f
+(filename)1197 4036 w
+10 R f
+(Replace dot by contents of named disc file)7 1700 1 2160 4036 t
+10 CW f
+(w)1077 4156 w
+10 I f
+(filename)1197 4156 w
+10 R f
+(Write file to named disc file)5 1123 1 2160 4156 t
+10 CW f
+(f)1077 4276 w
+10 I f
+(filename)1197 4276 w
+10 R f
+(Set file name and print new menu line)7 1523 1 2160 4276 t
+10 CW f
+(<)1077 4396 w
+10 I f
+(Unix-command)1197 4396 w
+10 R f
+(Replace dot by standard output of command)6 1770 1 2160 4396 t
+10 CW f
+(>)1077 4516 w
+10 I f
+(Unix-command)1197 4516 w
+10 R f
+(Send dot to standard input of command)6 1577 1 2160 4516 t
+10 CW f
+(|)1077 4636 w
+10 I f
+(Unix-command)1197 4636 w
+10 R f
+(Replace dot by result of command applied to dot)8 1948 1 2160 4636 t
+10 CW f
+(!)1077 4756 w
+10 I f
+(Unix-command)1197 4756 w
+10 R f
+(Run the command)2 733 1 2160 4756 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(Loops and conditionals)2 933 1 1077 4992 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(x/)1077 5204 w
+10 I f
+(regexp)1197 5204 w
+10 CW f
+(/)1468 5204 w
+10 I f
+(command)1588 5204 w
+10 R f
+(For each match of regexp, set dot and run command)9 2079 1 2160 5204 t
+10 CW f
+(y/)1077 5324 w
+10 I f
+(regexp)1197 5324 w
+10 CW f
+(/)1468 5324 w
+10 I f
+(command)1588 5324 w
+10 R f
+(Between adjacent matches of regexp, set dot and run command)9 2522 1 2160 532…
+10 CW f
+(X/)1077 5444 w
+10 I f
+(regexp)1197 5444 w
+10 CW f
+(/)1468 5444 w
+10 I f
+(command)1588 5444 w
+10 R f
+(Run command in each file whose menu line matches regexp)9 2404 1 2160 5444 t
+10 CW f
+(Y/)1077 5564 w
+10 I f
+(regexp)1197 5564 w
+10 CW f
+(/)1468 5564 w
+10 I f
+(command)1588 5564 w
+10 R f
+(Run command in each file whose menu line does not match)10 2386 1 2160 5564 t
+10 CW f
+(g/)1077 5684 w
+10 I f
+(regexp)1197 5684 w
+10 CW f
+(/)1468 5684 w
+10 I f
+(command)1588 5684 w
+10 R f
+(If dot contains a match of regexp, run command)8 1921 1 2160 5684 t
+10 CW f
+(v/)1077 5804 w
+10 I f
+(regexp)1197 5804 w
+10 CW f
+(/)1468 5804 w
+10 I f
+(command)1588 5804 w
+10 R f
+(If dot does not contain a match of regexp, run command)10 2243 1 2160 5804 t
+10 S f
+(_ ________________________________________________________________________)1 …
+10 R f
+(Miscellany)1077 6040 w
+10 S f
+(_ ________________________________________________________________________)1 …
+10 CW f
+(k)1077 6252 w
+10 R f
+(Set address mark to value of dot)6 1287 1 2160 6252 t
+10 CW f
+(q)1077 6372 w
+10 R f
+(Quit)2160 6372 w
+10 CW f
+(u)1077 6492 w
+10 I f
+(n)1197 6492 w
+10 R f
+(Undo last)1 386 1 2160 6492 t
+10 I f
+(n)2571 6492 w
+10 R f
+(\(default 1\) changes)2 764 1 2646 6492 t
+10 CW f
+({ })1 180 1 1077 6612 t
+10 R f
+(Braces group commands)2 987 1 2160 6612 t
+10 S f
+(_ ________________________________________________________________________)1 …
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 5 5
+%%Page: 6 6
+/saveobj save def
+mark
+6 pagesetup
+10 R f
+(- 6 -)2 166 1 2797 480 t
+( multiple lines, and)3 788( may span)2 417( \(Dot)1 239(to the line containin…
+10 CW f
+(+)3687 840 w
+10 R f
+(selects the line after the end of)6 1261 1 3779 840 t
+(dot, then)1 350 1 720 960 t
+10 CW f
+(-)1095 960 w
+10 R f
+(backs up one line.\))3 754 1 1180 960 t
+( addresses the text matched by the expression.)7 1855(The final type of addre…
+(The expression is enclosed in slashes, as in)7 1720 1 720 1236 t
+9 CW f
+(/)1008 1406 w
+9 I f
+(expression)1062 1406 w
+9 CW f
+(/)1447 1406 w
+10 R f
+( the same as those in the Unix program)8 1647(The expressions are)2 812 2 720…
+10 CW f
+(egrep)3215 1586 w
+10 R f
+(,)3515 1586 w
+6 R f
+(6,7)3540 1536 w
+10 R f
+(and include closures, alternations,)3 1389 1 3651 1586 t
+( find the)2 335( They)1 256(and so on.)2 410 3 720 1706 t
+10 I f
+(leftmost longest)1 632 1 1747 1706 t
+10 R f
+( match after the)3 623(string that matches the expression, that is, the first…
+( the search is started, and if more than one match begins at the same spot, t…
+( assume familiarity with the syntax for regular expressions in Unix programs.…
+6 R f
+(9)4201 1896 w
+10 R f
+(\) For example,)2 585 1 4231 1946 t
+9 CW f
+(/x/)1008 2116 w
+10 R f
+(matches the next)2 671 1 720 2296 t
+10 CW f
+(x)1416 2296 w
+10 R f
+(character in the file,)3 797 1 1501 2296 t
+9 CW f
+(/xx*/)1008 2466 w
+10 R f
+(matches the next run of one or more)7 1444 1 720 2646 t
+10 CW f
+(x)2189 2646 w
+10 R f
+('s, and)1 266 1 2249 2646 t
+9 CW f
+(/x|Peter/)1008 2816 w
+10 R f
+(matches the next)2 677 1 720 2996 t
+10 CW f
+(x)1425 2996 w
+10 R f
+(or)1513 2996 w
+10 CW f
+(Peter)1624 2996 w
+10 R f
+( character' operator, a)3 885( compatibility with other Unix programs, the `a…
+(period, does not match a newline, so)6 1459 1 720 3116 t
+9 CW f
+(/.*/)1008 3286 w
+10 R f
+( from dot to the end of the line, but excludes the newline and so will not ma…
+(line boundary.)1 577 1 720 3586 t
+( is forwards by default, so)5 1222( direction)1 416( The)1 241(Regular expres…
+10 CW f
+(/Peter/)720 3862 w
+10 R f
+(is really an abbreviation for)4 1103 1 1165 3862 t
+10 CW f
+(+/Peter/)2293 3862 w
+10 R f
+( search can be reversed with a minus sign, so)9 1796(. The)1 230 2 2773 3862 t
+9 CW f
+(-/Peter/)1008 4032 w
+10 R f
+(finds the first)2 595 1 720 4212 t
+10 CW f
+(Peter)1371 4212 w
+10 R f
+( with other address forms, so)5 1314( expressions may be used)4 1139( Regular…
+10 CW f
+(0+/Peter/)720 4332 w
+10 R f
+(finds the first)2 539 1 1288 4332 t
+10 CW f
+(Peter)1855 4332 w
+10 R f
+(in the file and)3 561 1 2183 4332 t
+10 CW f
+($-/Peter/)2772 4332 w
+10 R f
+( II summarizes)2 599( Table)1 280(finds the last.)2 542 3 3340 4332 t
+10 CW f
+(sam)4788 4332 w
+10 R f
+('s)4968 4332 w
+(addresses.)720 4452 w
+( who use Unix text editors such as)7 1389(The language discussed so far will …
+10 CW f
+(ed)4809 4608 w
+10 R f
+(or)4957 4608 w
+10 CW f
+(vi)720 4728 w
+10 R f
+(.)840 4728 w
+6 R f
+(9)865 4678 w
+10 R f
+( operations these commands allow, with the exception of regular expres-)10 28…
+( Indeed,)1 351( a mouse-based interface.)3 1028(sions and line numbers, are c…
+10 CW f
+(sam)4788 4848 w
+10 R f
+('s)4968 4848 w
+( For)1 194( usually made.)2 590(mouse language \(discussed at length below\) …
+(large or repetitive changes, however, a textual language outperforms a manual…
+(Imagine that, instead of deleting just one occurrence of the string)10 2708 1…
+10 CW f
+(Peter)3714 5244 w
+10 R f
+( wanted to eliminate)3 849(, we)1 177 2 4014 5244 t
+(every)720 5364 w
+10 CW f
+(Peter)970 5364 w
+10 R f
+( some text.)2 442( needed is an iterator that runs a command for each occurre…
+10 CW f
+(Sam)4788 5364 w
+10 R f
+('s)4968 5364 w
+(iterator is called)2 643 1 720 5484 t
+10 CW f
+(x)1388 5484 w
+10 R f
+(, for extract:)2 490 1 1448 5484 t
+9 CW f
+(x/)1008 5654 w
+9 I f
+(expression)1116 5654 w
+9 CW f
+(/)1501 5654 w
+9 I f
+(command)1609 5654 w
+10 R f
+( text matched)2 554(finds all matches in dot of the specified expression, and…
+( to delete all the)4 638( So)1 156(and runs the command.)3 932 3 720 5954 t
+10 CW f
+(Peters:)2471 5954 w
+9 CW f
+(0,$ x/Peter/ d)2 756 1 1008 6124 t
+10 R f
+( are to improve readability;)4 1163(\(Blanks in these examples)3 1100 2 720 6…
+10 CW f
+(sam)3027 6304 w
+10 R f
+( This)1 247(neither requires nor interprets them.\))4 1542 2 3251 6304 t
+(searches the entire file \()4 964 1 720 6424 t
+10 CW f
+(0,$)1684 6424 w
+10 R f
+( of the string)3 514(\) for occurrences)2 680 2 1864 6424 t
+10 CW f
+(Peter)3085 6424 w
+10 R f
+(, and runs the)3 544 1 3385 6424 t
+10 CW f
+(d)3956 6424 w
+10 R f
+(command with dot set to)4 997 1 4043 6424 t
+( contrast, the comparable)3 1003( \(By)1 200(each such occurrence.)2 876 3 72…
+10 CW f
+(ed)2824 6544 w
+10 R f
+(command would delete all)3 1057 1 2969 6544 t
+10 I f
+(lines)4051 6544 w
+10 R f
+(containing)4265 6544 w
+10 CW f
+(Peter)4712 6544 w
+10 R f
+(;)5012 6544 w
+10 CW f
+(sam)720 6664 w
+10 R f
+(deletes only the)2 653 1 938 6664 t
+10 CW f
+(Peters)1629 6664 w
+10 R f
+( address)1 337(.\) The)1 276 2 1989 6664 t
+10 CW f
+(0,$)2640 6664 w
+10 R f
+( be abbreviated to just a)5 1011(is commonly used, and may)4 1171 2 2858 6664…
+( another example,)2 712(comma. As)1 480 2 720 6784 t
+9 CW f
+(, x/Peter/ p)2 648 1 1008 6954 t
+10 R f
+(prints a list of)3 556 1 720 7134 t
+10 CW f
+(Peters,)1303 7134 w
+10 R f
+(one for each appearance in the file, with no intervening text \(not even newl…
+(separate the instances\).)2 922 1 720 7254 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 6 6
+%%Page: 7 7
+/saveobj save def
+mark
+7 pagesetup
+10 R f
+(- 7 -)2 166 1 2797 480 t
+(Table II.)1 343 1 2402 900 t
+10 CW f
+(Sam)2770 900 w
+10 R f
+(addresses)2975 900 w
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 R f
+(Simple addresses)1 691 1 1248 1136 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 CW f
+(#)1248 1348 w
+10 I f
+(n)1308 1348 w
+10 R f
+(The empty string after character)4 1279 1 2250 1348 t
+10 I f
+(n)3554 1348 w
+(n)1248 1468 w
+10 R f
+(Line)2250 1468 w
+10 I f
+(n)2458 1468 w
+10 R f
+(.)2508 1468 w
+10 CW f
+(/)1248 1588 w
+10 I f
+(regexp)1308 1588 w
+10 CW f
+(/)1579 1588 w
+10 R f
+(The first following match of the regular expression)7 2038 1 2250 1588 t
+10 CW f
+(-/)1248 1708 w
+10 I f
+(regexp)1368 1708 w
+10 CW f
+(/)1639 1708 w
+10 R f
+(The first previous match of the regular expression)7 1993 1 2250 1708 t
+10 CW f
+($)1248 1828 w
+10 R f
+(The null string at the end of the file)8 1415 1 2250 1828 t
+10 CW f
+(.)1248 1948 w
+10 R f
+(Dot)2250 1948 w
+10 CW f
+(')1248 2068 w
+10 R f
+(The address mark, set by)4 995 1 2250 2068 t
+10 CW f
+(k)3270 2068 w
+10 R f
+(command)3355 2068 w
+10 CW f
+(")1248 2188 w
+10 I f
+(regexp)1308 2188 w
+10 CW f
+(")1579 2188 w
+10 R f
+(Dot in the file whose menu line matches regexp)8 1908 1 2250 2188 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 R f
+(Compound addresses)1 852 1 1248 2424 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 I f
+(a1)1248 2636 w
+10 CW f
+(+)1348 2636 w
+10 I f
+(a2)1408 2636 w
+10 R f
+(The address)1 479 1 2250 2636 t
+10 I f
+(a2)2754 2636 w
+10 R f
+(evaluated starting at right of)4 1126 1 2879 2636 t
+10 I f
+(a1)4030 2636 w
+(a1)1248 2756 w
+10 CW f
+(-)1348 2756 w
+10 I f
+(a2 a2)1 942 1 1408 2756 t
+10 R f
+(evaluated in the reverse direction starting at left of)8 2012 1 2375 2756 t
+10 I f
+(a1)4412 2756 w
+(a1)1248 2876 w
+10 CW f
+(,)1348 2876 w
+10 I f
+(a2)1408 2876 w
+10 R f
+(From the left of)3 630 1 2250 2876 t
+10 I f
+(a1)2905 2876 w
+10 R f
+(to the right of)3 547 1 3030 2876 t
+10 I f
+(a2)3602 2876 w
+10 R f
+(\(default)3727 2876 w
+10 CW f
+(0,$)4062 2876 w
+10 R f
+(\))4242 2876 w
+10 I f
+(a1)1248 2996 w
+10 CW f
+(;)1348 2996 w
+10 I f
+(a2)1408 2996 w
+10 R f
+(Like)2250 2996 w
+10 CW f
+(,)2458 2996 w
+10 R f
+(but sets dot after evaluating)4 1104 1 2543 2996 t
+10 I f
+(a1)3672 2996 w
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 R f
+(The operators)1 569 1 1440 3232 t
+10 CW f
+(+)2052 3232 w
+10 R f
+(and)2155 3232 w
+10 CW f
+(-)2342 3232 w
+10 R f
+(are high precedence, while)3 1122 1 2445 3232 t
+10 CW f
+(,)3610 3232 w
+10 R f
+(and)3713 3232 w
+10 CW f
+(;)3901 3232 w
+10 R f
+(are low)1 315 1 4005 3232 t
+( both)1 208(precedence. In)1 610 2 1440 3352 t
+10 CW f
+(+)2288 3352 w
+10 R f
+(and)2378 3352 w
+10 CW f
+(-)2552 3352 w
+10 R f
+(forms,)2642 3352 w
+10 I f
+(a2)2930 3352 w
+10 R f
+(defaults to 1 and)3 678 1 3060 3352 t
+10 I f
+(a1)3768 3352 w
+10 R f
+(defaults to)1 423 1 3897 3352 t
+( both)1 203(dot. If)1 269 2 1440 3472 t
+10 I f
+(a1)1937 3472 w
+10 R f
+(and)2062 3472 w
+10 I f
+(a2)2231 3472 w
+10 R f
+(are present,)1 459 1 2356 3472 t
+10 CW f
+(+)2840 3472 w
+10 R f
+(may be elided.)2 585 1 2925 3472 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 …
+10 R f
+(Of course, the text extracted by)5 1267 1 970 3852 t
+10 CW f
+(x)2265 3852 w
+10 R f
+(may be selected by a regular expression, which complicates decid-)9 2687 1 23…
+( is resolved by generating the matches)6 1597( This)1 240( matches is chosen …
+( starting)1 338(starting from the beginning of dot using the leftmost-longest…
+( adja-)1 228( expressions may also match null strings, but a null match)10 23…
+( example,)1 388( For)1 189(cent to a non-null match is never selected; at lea…
+9 CW f
+(, c/AAA/)1 432 1 1008 4502 t
+(x/B*/ c/-/)1 540 1 1008 4612 t
+(, p)1 162 1 1008 4722 t
+10 R f
+(produces as output)2 749 1 720 4902 t
+9 CW f
+(-A-A-A-)1008 5072 w
+10 R f
+(because the pattern)2 764 1 720 5252 t
+10 CW f
+(B*)1509 5252 w
+10 R f
+(matches the null strings separating the)5 1529 1 1654 5252 t
+10 CW f
+(A)3208 5252 w
+10 R f
+('s.)3268 5252 w
+(The)970 5408 w
+10 CW f
+(x)1150 5408 w
+10 R f
+(command has a complement,)3 1165 1 1235 5408 t
+10 CW f
+(y)2425 5408 w
+10 R f
+( syntax, that executes the command with dot set to)9 2024(, with similar)2 53…
+(the text)1 297 1 720 5528 t
+10 I f
+(between)1042 5528 w
+10 R f
+( example,)1 388( For)1 189(the matches of the expression.)4 1206 3 1394 5528 t
+9 CW f
+(, c/AAA/)1 432 1 1008 5698 t
+(y/A/ c/-/)1 486 1 1008 5808 t
+(, p)1 162 1 1008 5918 t
+10 R f
+(produces the same result as the example above.)7 1890 1 720 6098 t
+(The)970 6254 w
+10 CW f
+(x)1158 6254 w
+10 R f
+(and)1251 6254 w
+10 CW f
+(y)1428 6254 w
+10 R f
+(commands are looping constructs, and)4 1566 1 1521 6254 t
+10 CW f
+(sam)3120 6254 w
+10 R f
+(has a pair of conditional commands to go)7 1707 1 3333 6254 t
+( have similar syntax:)3 830( They)1 255(with them.)1 428 3 720 6374 t
+9 CW f
+(g/)1008 6544 w
+9 I f
+(expression)1116 6544 w
+9 CW f
+(/)1501 6544 w
+9 I f
+(command)1609 6544 w
+10 R f
+( is different from)3 688( This)1 231(\(guard\) runs the command exactly once …
+10 CW f
+(x)4955 6724 w
+10 R f
+(,)5015 6724 w
+(which runs the command for)4 1148 1 720 6844 t
+10 I f
+(each)1893 6844 w
+10 R f
+(match:)2106 6844 w
+10 CW f
+(x)2403 6844 w
+10 R f
+(loops;)2488 6844 w
+10 CW f
+(g)2758 6844 w
+10 R f
+( Thus,)1 275(merely tests, without changing the value of dot.)7 1901 2 2843 6…
+9 CW f
+(, x/Peter/ d)2 648 1 1008 7014 t
+10 R f
+(deletes all occurrences of)3 1010 1 720 7194 t
+10 CW f
+(Peter)1755 7194 w
+10 R f
+(, but)1 178 1 2055 7194 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 7 7
+%%Page: 8 8
+/saveobj save def
+mark
+8 pagesetup
+10 R f
+(- 8 -)2 166 1 2797 480 t
+9 CW f
+(, g/Peter/ d)2 648 1 1008 830 t
+10 R f
+( string\) if)2 384(deletes the whole file \(reduces it to a null)8 1687 2 720…
+10 CW f
+(Peter)2822 1010 w
+10 R f
+( complemen-)1 530( The)1 211(occurs anywhere in the text.)4 1146 3 3153 1010 t
+(tary conditional is)2 722 1 720 1130 t
+10 CW f
+(v)1467 1130 w
+10 R f
+(, which runs the command if there is)7 1459 1 1527 1130 t
+10 I f
+(no)3011 1130 w
+10 R f
+(match of the expression.)3 976 1 3136 1130 t
+( For)1 190(These control-structure-like commands may be composed to construct…
+(example, to print those lines of text that contain the string)10 2307 1 720 1…
+10 CW f
+(Peter)3052 1406 w
+10 R f
+(:)3352 1406 w
+9 CW f
+(, x/.*\\n/ g/Peter/ p)3 1080 1 1008 1576 t
+10 R f
+(The)720 1756 w
+10 CW f
+(x)908 1756 w
+10 R f
+( file into lines, the)4 761(breaks the)1 415 2 1001 1756 t
+10 CW f
+(g)2211 1756 w
+10 R f
+(selects those lines containing)3 1190 1 2305 1756 t
+10 CW f
+(Peter)3529 1756 w
+10 R f
+(, and the)2 359 1 3829 1756 t
+10 CW f
+(p)4222 1756 w
+10 R f
+( This)1 237(prints them.)1 487 2 4316 1756 t
+(command gives an address for the)5 1416 1 720 1876 t
+10 CW f
+(x)2172 1876 w
+10 R f
+(command \(the whole file\), but because)5 1607 1 2268 1876 t
+10 CW f
+(g)3911 1876 w
+10 R f
+(does not have an explicit)4 1033 1 4007 1876 t
+( the value of dot produced by the)7 1359(address, it applies to)3 831 2 720 1…
+10 CW f
+(x)2941 1996 w
+10 R f
+( commands in)2 573( All)1 184(command, that is, to each line.)5 1251 3 3032 1…
+10 CW f
+(sam)720 2116 w
+10 R f
+(except for the command to write a file to disc use dot for the default addres…
+(Composition may be continued indefinitely.)4 1764 1 970 2272 t
+9 CW f
+(, x/.*\\n/ g/Peter/ v/SaltPeter/ p)4 1782 1 1008 2442 t
+10 R f
+(prints those lines containing)3 1125 1 720 2622 t
+10 CW f
+(Peter)1870 2622 w
+10 R f
+(but)2195 2622 w
+10 I f
+(not)2348 2622 w
+10 R f
+(those containing)1 658 1 2501 2622 t
+10 CW f
+(SaltPeter)3184 2622 w
+10 R f
+(.)3724 2622 w
+10 B f
+(Structural Regular Expressions)2 1350 1 720 2862 t
+10 R f
+( non-interactive ones such as)4 1177(Unlike other Unix text editors, includin…
+10 CW f
+(sed)3732 3018 w
+10 R f
+(and)3943 3018 w
+10 CW f
+(awk)4118 3018 w
+10 R f
+(,)4298 3018 w
+6 R f
+(7)4323 2968 w
+10 CW f
+(sam)4384 3018 w
+10 R f
+(is good for)2 445 1 4595 3018 t
+( on-line phone book composed of records,)6 1737( example is an)3 604( An)1 18…
+(separated by blank lines, of the form)6 1461 1 720 3258 t
+9 CW f
+(Herbert Tic)1 594 1 1008 3428 t
+(44 Turnip Ave., Endive, NJ)4 1404 1 1008 3538 t
+(201-5555642)1008 3648 w
+(Norbert Twinge)1 756 1 1008 3868 t
+(16 Potato St., Cabbagetown, NJ)4 1620 1 1008 3978 t
+(201-5553145)1008 4088 w
+(...)1008 4308 w
+10 R f
+(The format may be encoded as a regular expression:)8 2083 1 720 4488 t
+9 CW f
+(\(.+\\n\)+)1008 4658 w
+10 R f
+( command to print Mr. Tic's entire record is then)9 1958( The)1 205(that is, …
+9 CW f
+(, x/\(.+\\n\)+/ g/\303Herbert Tic$/ p)4 1674 1 1008 5008 t
+10 R f
+(and that to extract just the phone number is)8 1726 1 720 5188 t
+9 CW f
+(, x/\(.+\\n\)+/ g/\303Herbert Tic$/ x/\303[0-9]*-[0-9]*\\n/ p)5 2754 1 1008 5…
+10 R f
+( Tic's record, extracts the phone number from)7 1862(The latter command break…
+(the record, and finally prints the number.)6 1636 1 720 5658 t
+(A more involved problem is that of renaming a particular variable, say)11 292…
+10 CW f
+(n)3932 5814 w
+10 R f
+(, to)1 138 1 3992 5814 t
+10 CW f
+(num)4165 5814 w
+10 R f
+(in a C program.)3 660 1 4380 5814 t
+(The obvious first attempt,)3 1033 1 720 5934 t
+9 CW f
+(, x/n/ c/num/)2 702 1 1008 6104 t
+10 R f
+( flawed: it changes not only the variable)7 1628(is badly)1 317 2 720 6284 t
+10 CW f
+(n)2694 6284 w
+10 R f
+(but any letter)2 535 1 2783 6284 t
+10 CW f
+(n)3347 6284 w
+10 R f
+( need to extract all the)5 904( We)1 192(that appears.)1 508 3 3436 6284 t
+(variables, and select those that match)5 1486 1 720 6404 t
+10 CW f
+(n)2231 6404 w
+10 R f
+(and only)1 347 1 2316 6404 t
+10 CW f
+(n)2688 6404 w
+10 R f
+(:)2748 6404 w
+9 CW f
+(, x/[A-Za-z_][A-Za-z_0-9]*/ g/n/ v/../ c/num/)4 2430 1 1008 6574 t
+10 R f
+(The pattern)1 458 1 720 6754 t
+10 CW f
+([A-Za-z_][A-Za-z_0-9]*)1204 6754 w
+10 R f
+( Next)1 246(matches C identifiers.)2 876 2 2550 6754 t
+10 CW f
+(g/n/)3699 6754 w
+10 R f
+(selects those containing an)3 1074 1 3966 6754 t
+10 CW f
+(n)720 6874 w
+10 R f
+(. Then)1 303 1 780 6874 t
+10 CW f
+(v/../)1131 6874 w
+10 R f
+(rejects those containing two \(or more\) characters, and finally)8 2614 1 147…
+10 CW f
+(c/num/)4141 6874 w
+10 R f
+(changes the)1 491 1 4549 6874 t
+(remainder \(identifiers)1 871 1 720 6994 t
+10 CW f
+(n)1620 6994 w
+10 R f
+(\) to)1 140 1 1680 6994 t
+10 CW f
+(num)1849 6994 w
+10 R f
+( version clearly works much better, but there may still be problems.)11 2754(…
+(For example, in C character and string constants, the sequence)9 2510 1 720 7…
+10 CW f
+(\\n)3257 7114 w
+10 R f
+( and)1 170(is interpreted as a newline character,)5 1466 2 3404 7114 t
+(we don't want to change it to)6 1165 1 720 7234 t
+10 CW f
+(\\num.)1910 7234 w
+10 R f
+(This problem can be forestalled with a)6 1536 1 2235 7234 t
+10 CW f
+(y)3796 7234 w
+10 R f
+(command:)3881 7234 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 8 8
+%%Page: 9 9
+/saveobj save def
+mark
+9 pagesetup
+10 R f
+(- 9 -)2 166 1 2797 480 t
+9 CW f
+(, y/\\\\n/ x/[A-Za-z_][A-Za-z_0-9]*/ g/n/ v/../ c/num/)5 2808 1 1008 830 t
+10 R f
+(\(the second)1 464 1 720 1010 t
+10 CW f
+(\\)1216 1010 w
+10 R f
+( reject)1 254(is necessary because of lexical conventions in regular expressi…
+(character constants and strings outright:)4 1592 1 720 1130 t
+9 CW f
+(, y/'[\303']*'/ y/"[\303"]*"/ x/[A-Za-z_][A-Za-z_0-9]*/ g/n/ v/../ c/num/)6 3…
+10 R f
+(The)720 1480 w
+10 CW f
+(y)907 1480 w
+10 R f
+( only)1 211( The)1 213( strings.)1 325(commands in this version exclude from …
+(remaining problem is to deal with the possible occurrence of)9 2439 1 720 160…
+10 CW f
+(\\')3186 1600 w
+10 R f
+(or)3332 1600 w
+10 CW f
+(\\")3441 1600 w
+10 R f
+(within these sequences, but it's easy)5 1453 1 3587 1600 t
+(to see how to resolve this difficulty.)6 1435 1 720 1720 t
+( of the command)3 683( simple version)2 615( A)1 124(The point of these compo…
+( can be undone;)3 644( \(Mistakes)1 449( it can be honed by adding a clause o…
+( result-)1 283( The)1 208( the mouse language makes it unnecessary to retype …
+(ing chains of commands are somewhat reminiscent of shell pipelines.)9 2783 1 …
+6 R f
+(7)3503 2186 w
+10 R f
+(Unlike pipelines, though, which pass)4 1481 1 3559 2236 t
+(along modified)1 612 1 720 2356 t
+10 I f
+(data)1361 2356 w
+10 R f
+(,)1539 2356 w
+10 CW f
+(sam)1593 2356 w
+10 R f
+(commands pass a)2 707 1 1802 2356 t
+10 I f
+(view)2538 2356 w
+10 R f
+( text at each step of the command is the)9 1623( The)1 210(of the data.)2 456…
+( by step until the correct piece is available to the final)11 2237(same, but …
+(step of the command line, which ultimately makes the change.)9 2494 1 720 259…
+(In other Unix programs, regular expressions are used only for selection, as i…
+10 CW f
+(sam g)1 272 1 4317 2752 t
+10 R f
+(command,)4621 2752 w
+(never for extraction as in the)5 1159 1 720 2872 t
+10 CW f
+(x)1907 2872 w
+10 R f
+(or)1995 2872 w
+10 CW f
+(y)2106 2872 w
+10 R f
+( patterns in)2 448( example,)1 391(command. For)1 611 3 2194 2872 t
+10 CW f
+(awk)3671 2872 w
+6 R f
+(7)3851 2822 w
+10 R f
+(are used to select lines to be)6 1132 1 3908 2872 t
+( The)1 207( but cannot be used to describe the format of the input text, or t…
+( the structure of a piece of text rather than its contents, as in the)14 2671…
+10 CW f
+(x)4980 3112 w
+10 R f
+(command, has been given a name:)5 1455 1 720 3232 t
+10 I f
+(structural regular expressions.)2 1268 1 2216 3232 t
+10 R f
+(When they are composed, as in the)6 1490 1 3550 3232 t
+( use is discussed at greater length elsewhere.)7 1779( Their)1 266(above exam…
+6 R f
+(10)4635 3302 w
+10 B f
+(Multiple files)1 564 1 720 3628 t
+10 CW f
+(Sam)720 3784 w
+10 R f
+(has a few other commands, mostly relating to input and output.)10 2526 1 925 …
+9 CW f
+(e discfilename)1 756 1 1008 3954 t
+10 R f
+(replaces the contents and name of the current file with those of the named di…
+9 CW f
+(w discfilename)1 756 1 1008 4304 t
+10 R f
+(writes the contents to the named disc file; and)8 1831 1 720 4484 t
+9 CW f
+(r discfilename)1 756 1 1008 4654 t
+10 R f
+( file's name if)3 590( these commands use the current)5 1350( All)1 188(repla…
+( Finally,)1 359(none is specified.)2 696 2 720 4954 t
+9 CW f
+(f discfilename)1 756 1 1008 5124 t
+10 R f
+(changes the name associated with the file and displays the result:)10 2596 1 …
+9 CW f
+('-. discfilename)1 864 1 1008 5474 t
+10 R f
+(This output is called the file's)5 1226 1 720 5654 t
+10 I f
+(menu line,)1 423 1 1978 5654 t
+10 R f
+( contents of the file's line in the button 3 menu)10 1951(because it is the)3…
+( The)1 205( first three characters are a concise notation for the state of th…
+( sign indicates the number of windows open on the)9 2052( minus)1 271( The)1 …
+(file \(see the next section\):)4 1071 1 720 6014 t
+10 CW f
+(-)1826 6014 w
+10 R f
+(means none,)1 509 1 1921 6014 t
+10 CW f
+(+)2465 6014 w
+10 R f
+(means one, and)2 636 1 2560 6014 t
+10 CW f
+(*)3230 6014 w
+10 R f
+( the period)2 445( Finally,)1 368(means more than one.)3 903 3 3324 6014 t
+( are useful for controlling the)5 1192( characters)1 432( These)1 292(indicat…
+10 CW f
+(X)4119 6134 w
+10 R f
+(command, described)1 831 1 4209 6134 t
+(shortly.)720 6254 w
+10 CW f
+(Sam)970 6410 w
+10 R f
+( \(such as all the source for a program\) by invoking it with a)13 2384(may b…
+(list of file names as arguments, and more may be added or deleted on demand.)…
+9 CW f
+(B discfile1 discfile2 ...)3 1350 1 1008 6700 t
+10 R f
+(adds the named files to)4 921 1 720 6880 t
+10 CW f
+(sam)1666 6880 w
+10 R f
+('s list, and)2 414 1 1846 6880 t
+9 CW f
+(D discfile1 discfile2 ...)3 1350 1 1008 7050 t
+10 R f
+(removes them from)2 790 1 720 7230 t
+10 CW f
+(sam)1539 7230 w
+10 R f
+( these commands have a)4 990( Both)1 250('s memory \(without effect on associ…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 9 9
+%%Page: 10 10
+/saveobj save def
+mark
+10 pagesetup
+10 R f
+(- 10 -)2 216 1 2772 480 t
+(syntax for using the shell)4 1005 1 720 840 t
+6 R f
+(7)1725 790 w
+10 R f
+(\(the Unix command interpreter\) to generate the lists:)7 2099 1 1780 840 t
+9 CW f
+(B <echo *.c)2 594 1 1008 1010 t
+10 R f
+(will add all C source files, and)6 1218 1 720 1190 t
+9 CW f
+(B <grep -l variable *.c)4 1242 1 1008 1360 t
+10 R f
+( files referencing a particular variable \(the Unix command)8 2377(will add a…
+10 CW f
+(grep -l)1 420 1 3980 1540 t
+10 R f
+(lists all files in)3 608 1 4432 1540 t
+( Finally,)1 379( the specified regular expression\).)4 1429(its arguments tha…
+10 CW f
+(D)4168 1660 w
+10 R f
+(without arguments)1 767 1 4273 1660 t
+(deletes the current file.)3 914 1 720 1780 t
+(There are two ways to change which file is current:)9 2047 1 970 1936 t
+9 CW f
+(b filename)1 540 1 1008 2106 t
+10 R f
+( The)1 213(makes the named file current.)4 1215 2 720 2286 t
+10 CW f
+(B)2181 2286 w
+10 R f
+( but also adds any new files to)7 1270(command does the same,)3 1028 2 2274 2…
+10 CW f
+(sam)4606 2286 w
+10 R f
+('s list.)1 254 1 4786 2286 t
+( The)1 208( mouse actions, not by textual commands.\))6 1728(\(In practice, o…
+(other way is to use a form of address that refers to files:)12 2223 1 720 252…
+9 CW f
+(")1008 2696 w
+9 I f
+(expression)1062 2696 w
+9 CW f
+(")1447 2696 w
+9 I f
+(address)1555 2696 w
+10 R f
+( matches the expression \(there must be exactly)7 1923(refers to the address …
+( example,)1 388( For)1 189(one match\).)1 471 3 720 2996 t
+9 CW f
+("peter.c" 3)1 594 1 1008 3166 t
+10 R f
+( whose name matches)3 885(refers to the third line of the file)7 1299 2 720 3…
+10 CW f
+(peter.c)2933 3346 w
+10 R f
+( is most useful in the move \()7 1164(. This)1 257 2 3353 3346 t
+10 CW f
+(m)4774 3346 w
+10 R f
+(\) and)1 206 1 4834 3346 t
+(copy \()1 252 1 720 3466 t
+10 CW f
+(t)972 3466 w
+10 R f
+(\) commands:)1 519 1 1032 3466 t
+9 CW f
+(0,$ t "peter.c" 0)3 918 1 1008 3636 t
+10 R f
+(makes a copy of the current file at the beginning of)10 2040 1 720 3816 t
+10 CW f
+(peter.c)2785 3816 w
+10 R f
+(.)3205 3816 w
+(The)970 3972 w
+10 CW f
+(X)1150 3972 w
+10 R f
+(command is a looping construct, like)5 1477 1 1235 3972 t
+10 CW f
+(x)2737 3972 w
+10 R f
+(, that refers to files instead of strings:)7 1487 1 2797 3972 t
+9 CW f
+(X/)1008 4142 w
+9 I f
+(expression)1116 4142 w
+9 CW f
+(/)1501 4142 w
+9 I f
+(command)1609 4142 w
+10 R f
+( best example is)3 641( The)1 205(runs the command in all files whose menu li…
+9 CW f
+(X/'/ w)1 324 1 1008 4492 t
+10 R f
+(which writes to disc all modified files.)6 1571 1 720 4672 t
+10 CW f
+(Y)2347 4672 w
+10 R f
+(is the complement of)3 859 1 2438 4672 t
+10 CW f
+(X)3328 4672 w
+10 R f
+( command on all files whose)5 1181(: it runs the)3 471 2 3388 4672 t
+(menu lines don't match the expression:)5 1568 1 720 4792 t
+9 CW f
+(Y/\\.c/ D)1 432 1 1008 4962 t
+10 R f
+(deletes all files that don't have)5 1223 1 720 5142 t
+10 CW f
+(.c)1968 5142 w
+10 R f
+(in their names, that is, it keeps all C source files and deletes the rest.)14…
+(Braces allow commands to be grouped, so)6 1689 1 970 5298 t
+9 CW f
+({)1008 5468 w
+9 I f
+(command1)1440 5578 w
+(command2)1440 5688 w
+9 CW f
+(})1008 5798 w
+10 R f
+( Thus,)1 275(is syntactically a single command that runs two commands.)8 2379…
+9 CW f
+(X/\\.c/ ,g/variable/ {)2 1134 1 1008 6148 t
+(f)1440 6258 w
+(, x/.*\\n/ g/variable/ p)3 1242 1 1440 6368 t
+(})1008 6478 w
+10 R f
+(finds all occurrences of)3 936 1 720 6658 t
+10 CW f
+(variable)1682 6658 w
+10 R f
+( out the file names and lines of each match.)9 1748(in C source files, and pr…
+(The precise semantics of compound operations is discussed in the implementati…
+(Finally, the undo command,)3 1152 1 970 6934 t
+10 CW f
+(u)2156 6934 w
+10 R f
+( files were affected.)3 815(, undoes the last command, no matter how many)8 2…
+(Multiple undo operations move further back in time, so)8 2212 1 720 7054 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 10 10
+%%Page: 11 11
+/saveobj save def
+mark
+11 pagesetup
+10 R f
+(- 11 -)2 216 1 2772 480 t
+9 CW f
+(u)1008 830 w
+(u)1008 940 w
+10 R f
+(\(which may be abbreviated)3 1086 1 720 1120 t
+10 CW f
+(u2)1832 1120 w
+10 R f
+( be undone, however, nor)4 1022( undo may not)3 578( An)1 173(\) undoes the l…
+( though, including for example)4 1243( else is undoable,)3 697( Everything)1 …
+10 CW f
+(e)4980 1240 w
+10 R f
+(commands:)720 1360 w
+9 CW f
+(e filename)1 540 1 1008 1530 t
+(u)1008 1640 w
+10 R f
+( of the undo,)3 538( Because)1 393( file completely, including its name, dot,…
+( Only)1 259(potentially dangerous commands are not guarded by confirmations.)…
+10 CW f
+(D)3772 1940 w
+10 R f
+( informa-)1 382(, which destroys the)3 826 2 3832 1940 t
+( a modified file, but a second)6 1210( will not delete)3 618( It)1 118(tion n…
+10 CW f
+(D)4471 2060 w
+10 R f
+(of the same)2 476 1 4564 2060 t
+( The)1 205(file will succeed regardless.)3 1108 2 720 2180 t
+10 CW f
+(q)2058 2180 w
+10 R f
+(command, which exits)2 902 1 2143 2180 t
+10 CW f
+(sam)3070 2180 w
+10 R f
+(, is similarly guarded.)3 869 1 3250 2180 t
+10 B f
+(Mouse Interface)1 695 1 720 2420 t
+10 CW f
+(Sam)720 2576 w
+10 R f
+( dif-)1 173( only)1 206( The)1 208(is most commonly run connected to a bitmap…
+(ference in the command language between regular, mouse-driven)7 2637 1 720 26…
+10 CW f
+(sam)3386 2696 w
+10 R f
+(and)3595 2696 w
+10 CW f
+(sam -d)1 360 1 3768 2696 t
+10 R f
+(is that if an address is)5 883 1 4157 2696 t
+(provided without a command,)3 1217 1 720 2816 t
+10 CW f
+(sam -d)1 360 1 1968 2816 t
+10 R f
+(will print the text referenced by the address, but regular)9 2280 1 2360 2816…
+10 CW f
+(sam)4672 2816 w
+10 R f
+(will)4884 2816 w
+(highlight it on the screen \320 in fact, dot is always highlighted \(see Figu…
+cleartomark
+saveobj restore
+%ps_include: begin
+save
+/ed {exch def} def
+{} /showpage ed
+{} /copypage ed
+{} /erasepage ed
+{} /letter ed
+currentdict /findfont known systemdict /findfont known and {
+ /findfont systemdict /findfont get def
+} if
+36 dict dup /PS-include-dict-dw ed begin
+/context ed
+count array astore /o-stack ed
+%ps_include: variables begin
+/llx 80 def
+/lly 322 def
+/urx 531.44 def
+/ury 468.88 def
+/w 0 def
+/o 0 def
+/s 0 def
+/cx 2880 def
+/cy -3910 def
+/sx 4320 def
+/sy 1468 def
+/ax 0.5 def
+/ay 0.5 def
+/rot 0 def
+%ps_include: variables end
+{llx lly urx ury} /bbox ed
+{newpath 2 index exch 2 index exch dup 6 index exch
+ moveto 3 {lineto} repeat closepath} /boxpath ed
+{dup mul exch dup mul add sqrt} /len ed
+{2 copy gt {exch} if pop} /min ed
+{2 copy lt {exch} if pop} /max ed
+{transform round exch round exch A itransform} /nice ed
+{6 array} /n ed
+n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed
+urx llx sub 0 A dtransform len /Sx ed
+0 ury lly sub A dtransform len /Sy ed
+llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed
+rot dup sin abs /S ed cos abs /C ed
+Sx S mul Sy C mul add /H ed
+Sx C mul Sy S mul add /W ed
+sy H div /Scaley ed
+sx W div /Scalex ed
+s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if
+sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed
+sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed
+urx llx sub 0 A dtransform exch atan rot exch sub /rot ed
+n currentmatrix initgraphics setmatrix
+cx cy translate
+Scalex Scaley scale
+rot rotate
+Cx neg Cy neg translate
+A concat
+bbox boxpath clip newpath
+w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if
+end
+gsave
+%ps_include: inclusion begin
+/picstr 79 string def
+80 322 translate
+451.44 146.88 scale
+
+627 204 1 [627 0 0 -204 0 204]
+{currentfile picstr readhexstring pop} image
+
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff3fffffff3cffffffffffffffffffffdfe7f3bff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff3fffefff3cffffffff07ffffffffff3fe7f3cff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff3fffe7ff3fffffffff33fffffffffe7fe7f3e7f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff27fff3f820fc1f0fff39c3c9f8723e7f0783e7e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff13fff9f33cf9ce66033999c4f3311cfe6733f3e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff39fffce73cf9fce603393cce67339cfce673f3e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff3980fc673cf8fcffff393cce67339cfce673f3f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffff3980fce73cfc1cffff393cce60339cfce673f3f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffff39fff9e73cff8cfe03393cce67f39cfce673f3f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffff39fff3e73cffccfe03393cce67f39cfce673f3e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffff33ffe7f23cf9ce67ff3399ccf3339cfe4723f3e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffff07ffeff9201c1f0fff07c3c1f8739e7f2793e7e
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffffffffffffffffffffffffffcffffffe7fffffe7f
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffffffffffffffffffffffffffcfffffff3fffffcff
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdfffffffffffffffffffffffffffffffffffffffffcfffffffdfffffbfe
+7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2ffdffffffffffffffffffffffffff9fffe7ffff7ff9ffffffffffffe7fffd
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffff7fff9fffe7fbfcf0f9ffff7fffffffe7fffe
+7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffffffe7ffffffffff3f9e679ffff3fffffffe7ffff
+3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff8380c641f2307c079e6793fff9fc3c1f0e47c3f
+3f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff39e7e079f11e7f3f3e6f89fffcf999ce662399f
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff3fe7e679f39e7f3f3f1f9cfffe739fcce67339f
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff1fe7e7f9f39e7f3f3f499cc07e33ffccfe7339f
+9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff83e7e7f9f39e7f3f3e4b9cc07e73fc0cfe7301f
+9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffff1e7e7f9f39e7f3f3e679cfffcf3f9ccfe733ff
+9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffff9e7e7f9f39e7f3f3e679cfff9f3f9ccfe733ff
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff39e7e7f9f39e7f3f3e2399fff3f9998e667399f
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff83f0c1c033900f879f1183fff7fc3c4f0e73c3f
+3f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffffffffffffffffffff9ffffffffffffffffffffff
+3f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffffffffffffffffffffcfffffffffffffffffffffe
+7f9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffd
+ff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffbfffffffffe7ffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffff3fffffffffe7ffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff19e1c06731991ffe4fc7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff81ccf3e738188ffe27c7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff999cf3e73999cffe73c7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9f9cf3e739f9cffe73fffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff9f80f3e739f9cffe73fffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9f9ff3e739f9cffe73fffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff9f9ff3e739f9cffe73c7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9fccf3e239f9cffe67c7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff07e1f8713079cffe0fc7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffffffffffffffffffffffe7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffffffffffffffffffffffe7fffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffffffffffffffffffffffcffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffff83ffffffffee7fbffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f03fff3ffffffff9e7fcffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f39fff3ffffffff3e7fe7fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f39e1f3f0f83e1f3e4fe7fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f39ccf3e6739cce7e27f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f3b9cf3cf33f9ce7e73f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f079ff3cf31f9ce7e73f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f3b9ff3cf38380e7e73f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f399ff3cf3f19fe7e73f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f399ff3cf3f99fe7e73f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f39ccf3e6739cce7e67f3fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f03e18070f83e1f3e0fe7fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffff3fffe7fffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffff9fffcffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffefffbffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffff9fffffffffffffffffc3e1fffffffff9
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffff9ffff7fffffff81fff9fcffffffffff9
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffffffffe7fffffff9cfff9fcffffffffff9
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff19e1f041f8380f0e33ff9cce60301e1c67ffef9
+3f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff81cce679f39e7e6703ff9cce79fcfcce07ffab8
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff999cce79f3fe7ce733ff9dce79fcf9ce67ffc79
+cf1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9f9cce79f1fe7ce73fff83ce79fcf9ce7fff119
+cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff9f80ce79f83e7c073fff9dce79fcf80e7fffc79
+cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9f9fce79ff1e7cff3fff9cce79fcf9fe7fffab9
+cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff9f9fce79ff9e7cff3fff9cce79fcf9fe7fffef9
+cf1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffff9fcce479f39e7e673fff9cc479fcfcce7fffff9
+9f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffff07e1f240383f0f0e0fff81e260703e1c1fffff8
+3f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffff
+ff9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fffffffffffffffffffffffce7ffffffffffffffffffffffffffffffff
+ff9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fffffffffffffffffffffffe0fffffffffffffffffffffffffffffffff
+ff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffc000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffc00007c000000001180000001860000010000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcf8000c00000000618000200186000000c000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffccc000c00000000c180003001800000006000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcc61e0c0f07c1e0c1b000180fbe07c1e06070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffcc6330c198c633181d8000c19860c63303070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcc6630c30cc0631818c000631860c06303070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffcc6600c30ce0631818cfe0731860e06003000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcc6600c30c7c7f1818cfe06318607c6003000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffcc6600c30c0e601818c000c318600e6003000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcc6600c30c06601818c001831860066003070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffccc330c198c6331819800301b860c63303070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffcf81e7f8f07c1e0c1f000200dbfc7c1e06070000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffc00000000000000c000000000000000006030000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221ffffffffffffffffffc00000000000000600000000000000000c030000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889ffffffffffffffffffc000000000000001000000000000000010060000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000000000001f00000000040030000000000000c00
+0100000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c000000000000000000000800000300000000187830000400000000c00
+00c0000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c00000000000000000000180000030000000030cc30000600000000c00
+0060000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c0000000000000000007c7f39878303c1f07830cc36000301e1f078dc1
+e060700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000c6181f8cc3066318cc60c83b0001833318ccee3
+3030700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c000000000000000000c0181998c30c33018c6070318000c630198cc66
+3030700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000e0181818030c33818c605b319fc0e6001980c66
+3030000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c0000000000000000007c181818030c31f1fc60da319fc0c601f980c67
+f030000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c0000000000000000000e181818030c30398060cc31800186031980c66
+0030000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c00000000000000000006181818030c30198060cc31800306031980c66
+0030700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000c618180cc3066318cc60ee330006033338ccc63
+3030700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c0000000000000000007c0f3e079fe3c1f07830773e000401e1d878c61
+e060700000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000000000000000000000300000000000000000000
+0060300000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c000000000000000000000000000000000000180000000000000000000
+00c0300000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2889c000000000000000000000000000000000000040000000000000000000
+0100600000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe2221c00000000000000000000000000000000000000000000000000000001f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c0000000000000000001e0000000040200000c0000000000203008001f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c000000000000000000300000000180c00000c0000000000183006001f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c000000000000000000300000000301800000c00000000000c3003001f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c000000000000000000fe730f078301831878dc3e39800100c3603039f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c000000000000000000303f198cc6030318ccee631f80054063b01839f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c00000000000000000030333198c60303198cc6031980038063181839f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c00000000000000000030303198c603031980c60318000ee063181801f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c00000000000000000030303f9fc603031980c63f1800038063181801f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c000000000000000000303030180603031980c6631800054063181801f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c000000000000000000303030180603031980c6631800010063181839f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c0000000000000000003030198cc60303b8ccc6671800000063301839f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c000000000000000000fc7c0f07830181d878c63b3e000000c3e03039f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c000000000000000000000000000301800000000000000000c0003019f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221c000000000000000000000000000180c0000000000000000180006019f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889c00000000000000000000000000004020000000000000000200008031f
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fcffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2889ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2221ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe2001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffffffffffe3fff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+fe00000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000003fff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffff
+showpage
+%ps_include: inclusion end
+grestore
+PS-include-dict-dw begin
+o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice
+ initgraphics 0.1 setlinewidth boxpath stroke grestore} if
+clear o-stack aload pop
+context end restore
+%ps_include: end
+/saveobj save def
+mark
+8 I f
+(Figure 2. A)2 370 1 720 4744 t
+8 CW f
+(sam)1112 4744 w
+8 I f
+( bar down the left represents the file, with the bubble showing the fraction …
+( current text, which is highlighted, need not fit on a)10 1703( The)1 167( be…
+( it consists of one partial line, one complete line, and final partial line.)…
+10 R f
+( in all)2 236( any time, only one window)5 1131( At)1 153(Each file may have …
+(of)720 5340 w
+10 CW f
+(sam)830 5340 w
+10 R f
+(is the)1 216 1 1037 5340 t
+10 I f
+(current window,)1 658 1 1280 5340 t
+10 R f
+( may be the)3 466(that is, the window to which typing and mouse actions refer…
+10 CW f
+(sam)720 5460 w
+10 R f
+( a file has multiple)4 752( When)1 290( file windows.)2 573(window \(that in …
+( current file is the last file)6 1074( The)1 213(windows, the image of the fi…
+(affected by a command, so if the)6 1401 1 720 5700 t
+10 CW f
+(sam)2162 5700 w
+10 R f
+( the)1 164(window is current, the current window is not a window on)10 2493 2…
+( window on a file has its own value of dot, and when switching between)14 314…
+( flipping between)2 711( Thus,)1 281( file, the file's value of dot is change…
+(windows behaves in the obvious, convenient way.)6 2003 1 720 6060 t
+( 3 has a list of commands to)7 1154( Button)1 328( numbered left to right.)4 …
+( as printed by the)4 708(manipulate windows, followed by a list of `menu line…
+10 CW f
+(f)4037 6336 w
+10 R f
+(command, one per file)3 913 1 4127 6336 t
+( the list is long, the Blit menu software)8 1577( If)1 119( file name.)2 430(…
+( the)1 153( Using)1 295( manageable by generating a scrolling menu instead of…
+( makes that file the current file, and the most recently current window in)13…
+( if that file is already current, selecting it in the menu cycles through the…
+( is no)2 239( there)1 234( If)1 126(windows on the file; this simple trick av…
+(window open on the file,)4 996 1 720 7056 t
+10 CW f
+(sam)1741 7056 w
+10 R f
+(changes the mouse cursor to prompt the user to create one.)10 2342 1 1946 705…
+( commands)1 465(The commands on the button 3 menu are straightforward \(see F…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 11 11
+%%Page: 12 12
+/saveobj save def
+mark
+12 pagesetup
+10 R f
+(- 12 -)2 216 1 2772 480 t
+(to manipulate windows in)3 1048 1 720 840 t
+10 CW f
+(mux)1797 840 w
+10 R f
+(,)1977 840 w
+6 R f
+(8)2002 790 w
+10 R f
+(the Blit's window system.)3 1057 1 2061 840 t
+10 CW f
+(New)3172 840 w
+10 R f
+( empty)1 278(makes a new file, and gives it one)7 1381 2 3381 840 t
+(window, whose size is determined by a rectangle swept by the mouse.)11 2820 1…
+10 CW f
+(Xerox)3592 960 w
+10 R f
+( a window to be)4 650(prompts for)1 471 2 3919 960 t
+( multiple windows are created on one file.)7 1848(selected, and makes a clone…
+10 CW f
+(Reshape)720 1200 w
+10 R f
+( and)1 172(changes the size of the indicated window,)6 1678 2 1167 1200 t
+10 CW f
+(close)3045 1200 w
+10 R f
+( that is the last window open)6 1162( If)1 119(deletes it.)1 386 3 3373 1200 t
+(on the file,)2 434 1 720 1320 t
+10 CW f
+(close)1180 1320 w
+10 R f
+(first does a)2 440 1 1506 1320 t
+10 CW f
+(D)1972 1320 w
+10 R f
+(command on the file.)3 852 1 2058 1320 t
+10 CW f
+(Write)2961 1320 w
+10 R f
+(is identical to a)3 611 1 3287 1320 t
+10 CW f
+(w)3924 1320 w
+10 R f
+(command on the file; it is)5 1030 1 4010 1320 t
+( Finally,)1 373(in the menu purely for convenience.)5 1511 2 720 1440 t
+10 CW f
+(\304\304sam\304\304)2643 1440 w
+10 R f
+( the com-)2 407(is a menu item that appears between)6 1531 2 3102 1440 t
+( it makes the)3 544( Selecting)1 434(mands and the file names.)4 1088 3 720 1…
+10 CW f
+(sam)2822 1560 w
+10 R f
+(window the current window, causing subsequent)5 2002 1 3038 1560 t
+(typing to be interpreted as commands.)5 1526 1 720 1680 t
+cleartomark
+saveobj restore
+%ps_include: begin
+save
+/ed {exch def} def
+{} /showpage ed
+{} /copypage ed
+{} /erasepage ed
+{} /letter ed
+currentdict /findfont known systemdict /findfont known and {
+ /findfont systemdict /findfont get def
+} if
+36 dict dup /PS-include-dict-dw ed begin
+/context ed
+count array astore /o-stack ed
+%ps_include: variables begin
+/llx 242 def
+/lly 297 def
+/urx 369.44 def
+/ury 494.28 def
+/w 0 def
+/o 0 def
+/s 0 def
+/cx 2880 def
+/cy -2906 def
+/sx 4320 def
+/sy 1972 def
+/ax 0.5 def
+/ay 0.5 def
+/rot 0 def
+%ps_include: variables end
+{llx lly urx ury} /bbox ed
+{newpath 2 index exch 2 index exch dup 6 index exch
+ moveto 3 {lineto} repeat closepath} /boxpath ed
+{dup mul exch dup mul add sqrt} /len ed
+{2 copy gt {exch} if pop} /min ed
+{2 copy lt {exch} if pop} /max ed
+{transform round exch round exch A itransform} /nice ed
+{6 array} /n ed
+n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed
+urx llx sub 0 A dtransform len /Sx ed
+0 ury lly sub A dtransform len /Sy ed
+llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed
+rot dup sin abs /S ed cos abs /C ed
+Sx S mul Sy C mul add /H ed
+Sx C mul Sy S mul add /W ed
+sy H div /Scaley ed
+sx W div /Scalex ed
+s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if
+sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed
+sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed
+urx llx sub 0 A dtransform exch atan rot exch sub /rot ed
+n currentmatrix initgraphics setmatrix
+cx cy translate
+Scalex Scaley scale
+rot rotate
+Cx neg Cy neg translate
+A concat
+bbox boxpath clip newpath
+w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if
+end
+gsave
+%ps_include: inclusion begin
+/picstr 23 string def
+242 297 translate
+127.44 197.28 scale
+
+177 274 1 [177 0 0 -274 0 274]
+{currentfile picstr readhexstring pop} image
+
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffc00000000000000000000000000000000000000fffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffe47c39cfffffffffffffefffff
+ffc0000fffffffffffffe23999efffffffffffffefffff
+ffc0000fffffffffffffe733992fffffffffffffefffff
+ffc0000fffffffffffffe733992fffffffffffffefffff
+ffc0000fffffffffffffe730192fffffffffffffefffff
+ffc0000fffffffffffffe733fc9fffffffffffffefffff
+ffc0000fffffffffffffe733fc9fffffffffffffefffff
+ffc0000fffffffffffffe7399c9fffffffffffffefffff
+ffc0000fffffffffffffe73c3c9fffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffce78719c3ce7ffffffffffefffff
+ffc0000fffffffffffce7338199ce7ffffffffffefffff
+ffc0000fffffffffffe6e73993ce6fffffffffffefffff
+ffc0000fffffffffffe1e739f3ce1fffffffffffefffff
+ffc0000ffffffffffffbe039f3cfbfffffffffffefffff
+ffc0000ffffffffffff0e7f9f3cf0fffffffffffefffff
+ffc0000fffffffffffece7f9f3cecfffffffffffefffff
+ffc0000fffffffffffce7339f99ce7ffffffffffefffff
+ffc0000fffffffffffce78707c3ce7ffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000ffffffffffffffff3ffffffffffffffffefffff
+ffc0000ffffffffffffffff3ffffffffffffffffefffff
+ffc0000ffffffffffffffff3ffffffffffffffffefffff
+ffc0000fffffffff8cf0f0723c1c9f87ffffffffefffff
+ffc0000fffffffffc0e6673119cc4f33ffffffffefffff
+ffc0000fffffffffccce67f39fcce673ffffffffefffff
+ffc0000fffffffffcfce63f39fcce673ffffffffefffff
+ffc0000fffffffffcfc070739c0ce603ffffffffefffff
+ffc0000fffffffffcfcffe3399cce67fffffffffefffff
+ffc0000fffffffffcfcfff3399cce67fffffffffefffff
+ffc0000fffffffffcfe66733998ccf33ffffffffefffff
+ffc0000fffffffff83f0f0739c4c1f87ffffffffefffff
+ffc0000ffffffffffffffffffffcffffffffffffefffff
+ffc0000ffffffffffffffffffffcffffffffffffefffff
+ffc0000ffffffffffffffffffffcffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffc1ffffffffffffffffffefffff
+ffc0000ffffffffffffff9ffffffffffffffffffefffff
+ffc0000ffffffffffffff9ffffffffffffffffffefffff
+ffc0000ffffffffffff0f9f87c1f0fffffffffffefffff
+ffc0000fffffffffffe679f339ce67ffffffffffefffff
+ffc0000fffffffffffce79e799fce7ffffffffffefffff
+ffc0000fffffffffffcff9e798fce7ffffffffffefffff
+ffc0000fffffffffffcff9e79c1c07ffffffffffefffff
+ffc0000fffffffffffcff9e79f8cffffffffffffefffff
+ffc0000fffffffffffcff9e79fccffffffffffffefffff
+ffc0000fffffffffffe679f339ce67ffffffffffefffff
+ffc0000ffffffffffff0c0387c1f0fffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc00008000000000000000000000000000000000fffff
+ffc00008000000000000000000000000000000000fffff
+ffc00008000000000000000300000000000000000fffff
+ffc00008000000000000000300800000000000000fffff
+ffc00008000000000000000001800000000000000fffff
+ffc000080000000000319cdf07f0f000000000000fffff
+ffc000080000000000308fc301819800000000000fffff
+ffc000080000000000368cc30183180000ff80000fffff
+ffc000080000000000368c030183180000fe00000fffff
+ffc000080000000000368c030183f80000f800000fffff
+ffc0000800000000001b0c030183000000fc00000fffff
+ffc0000800000000001b0c030183000000fe00000fffff
+ffc0000800000000001b0c030181980000df00000fffff
+ffc0000800000000001b1f1fe0f0f00000cf80000fffff
+ffc000080000000000000000000000000087c0000fffff
+ffc000080000000000000000000000000083e0000fffff
+ffc0000ffffffffffffffffffffffffffffe0fffefffff
+ffc0000fffffffffffffffffffffffffffff07ffefffff
+ffc0000fffffffffffffffffffffffffffff83ffefffff
+ffc0000fffffffffffffffffffffffffffffc1ffefffff
+ffc0000fffffffffffffffffffffffffffffe0ffefffff
+ffc0000ffffffffffffff07832dffffffffff1ffefffff
+ffc0000fffffffffeef76733900f77bbfffffbffefffff
+ffc0000fffffffffc6e367ff924e371bffffffffefffff
+ffc0000fffffffff80c063ff924c0603ffffffffefffff
+ffc0000fffffffffb1d8f078124d8ec7ffffffffefffff
+ffc0000fffffffffbbddfe33924ddeefffffffffefffff
+ffc0000fffffffffffffff33924fffffffffffffefffff
+ffc0000fffffffffffffe733124fffffffffffffefffff
+ffc0000ffffffffffffff078924fffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000ffffffffffffff3f9ffffffffffffffffefffff
+ffc0000ffffffffffffff3f9ffffffffffffffffefffff
+ffc0000ffffffffffffff3f9ffffffffffffffffefffff
+ffc0000ffff9fffffe0f83c18cf0f0783fff0fffefffff
+ffc0000ffff9fffffce73399c0e667339ffe67ffefffff
+ffc0000ffff9ffffffe67339ccce67f3fffce7ffefffff
+ffc0000fffc03fffffe67339cfce63f1fffcffffefffff
+ffc0000fffc03ffffe067339cfc070783ffcffffefffff
+ffc0000ffff9fffffce67339cfcffe3f1ffcffffefffff
+ffc0000ffff9fc7ffce67339cfcfff3f9e3cffffefffff
+ffc0000ffff9fc7ffcc72391cfe667339e3e67ffefffff
+ffc0000ffffffc7ffe2793c983f0f0783e3f0fffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000fffffffffffffffffffffffffffffffffefffff
+ffc0000ffffffffffffc1e0fffffffffffffffffefffff
+ffc0000fffffffffffff9fcfffffffffffffffffefffff
+ffc0000fffffffffffff9fcfffffffffffffffffefffff
+ffc0000ffffffffffe0f9fcfc3f0fffc3fffffffefffff
+ffc0000ffffffffffce79fcf99e67ff99fffffffefffff
+ffc0000fffffffffffe79fcf3cce7ff39fffffffefffff
+ffc0000fffe03fffffe79fcf3ccffff3ffffffffefffff
+ffc0000fffe03ffffe079fcf3ccffff3ffffffffefffff
+ffc0000ffffffffffce79fcf3ccffff3ffffffffefffff
+ffc0000ffffffffffce79fcf3ccff8f3ffffffffefffff
+ffdffffffffffffffcc79fcf99e678f99fffffffefffff
+ffdffffffffffffffe240201c3f0f8fc3fffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdffffffffffffffcffffe1f0ffffffffffffffefffff
+ffdffffffffffffffcffffcfe7ffffffffffffffefffff
+ffdffffffffffffffcffffcfe7ffffffffffffffefffff
+ffdffffffffdfffffc9e730180f0e33ffe1fffffefffff
+ffdffffffff57ffffc4e73cfe7e6703ffccfffffefffff
+ffdffffffff8fffffce673cfe7ce733ff9cfffffefffff
+ffdfffffffe23ffffce673cfe7ce73fff9ffffffefffff
+ffdffffffff8fffffce673cfe7c073fff9ffffffefffff
+ffdffffffff57ffffce673cfe7cff3fff9ffffffefffff
+ffdffffffffdfffffce673cfe7cff3fc79ffffffefffff
+ffdffffffffffffffcce23cfe7e673fc7ccfffffefffff
+ffdffffffffffffffc1f130381f0e0fc7e1fffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdffffffffffffffffffff9ffffffffffffffffefffff
+ffdffffffffffffffffffff9ffffffffffffffffefffff
+ffdffffffffffffffffffff9ffffffffffffffffefffff
+ffdfffffffffffffff0cb7c1fff0ffffffffffffefffff
+ffdffffffffffffffe640399ffe67fffffffffffefffff
+ffdffffffffffffffce49339ffce7fffffffffffefffff
+ffdfffffffe03ffffcfc9339ffcfffffffffffffefffff
+ffdfffffffe03ffffcfc9339ffcfffffffffffffefffff
+ffdffffffffffffffcfc9339ffcfffffffffffffefffff
+ffdffffffffffffffcfc9339e3cfffffffffffffefffff
+ffdffffffffffffffe649391e3e67fffffffffffefffff
+ffdfffffffffffffff0c93c9e3f0ffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdffffff1ffffffffe79fffffffffffffffffffefffff
+ffdffffff1ffffffffe79fffffffffffffffffffefffff
+ffdffffff1ffffffffe7ffffffffffffffffffffefffff
+ffdffffff9f9ffffff041f83e1fff87fffffffffefffff
+ffdffffff9f9fffffe679f39ccfff33fffffffffefffff
+ffdffffff3f9fffffce79f3f9cffe73fffffffffefffff
+ffdfffffffc03ffffce79f1f9fffe7ffffffffffefffff
+ffdfffffffc03ffffce79f839fffe7ffffffffffefffff
+ffdffffffff9fffffce79ff19fffe7ffffffffffefffff
+ffdffffffff9fffffce79ff99ff1e7ffffffffffefffff
+ffdffffffff9fffffe479f39ccf1f33fffffffffefffff
+ffdfffffffffffffff240383e1f1f87fffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffff0e3319c3c67ffc3fffffffefffff
+ffdffffffffffffffe67038199e07ff99fffffffefffff
+ffdffffffffffffffce733993ce67ff39fffffffefffff
+ffdfffffffe03ffffce73f9f3ce7fff3ffffffffefffff
+ffdfffffffe03ffffc073f9f3ce7fff3ffffffffefffff
+ffdffffffffffffffcff3f9f3ce7fff3ffffffffefffff
+ffdffffffffffffffcff3f9f3ce7f8f3ffffffffefffff
+ffdffffffffffffffe673f9f99e7f8f99fffffffefffff
+ffdfffffffffffffff0e0f07c3c1f8fc3fffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdffffffffffffffffffffffffffffff9ffffffefffff
+ffdffffffffffffffffffffffffffffff9ffffffefffff
+ffdffffffffffffffffffffffffffffff9ffffffefffff
+ffdfffffffffffffff0e3319c3c6707ff91fffffefffff
+ffdffffffffffffffe67038199e0673ff88fffffefffff
+ffdffffffffffffffce733993ce667fff9cfffffefffff
+ffdfffffffe03ffffce73f9f3ce7e3fff9cfffffefffff
+ffdfffffffe03ffffc073f9f3ce7f07ff9cfffffefffff
+ffdffffffffffffffcff3f9f3ce7fe3ff9cfffffefffff
+ffdffffffffffffffcff3f9f3ce7ff3c79cfffffefffff
+ffdffffffffffffffe673f9f99e7e73c79cfffffefffff
+ffdfffffffffffffff0e0f07c3c1f07c79cfffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffff879e0fffffffffffffffffefffff
+ffdfffffffffffffff3f9fcfffffffffffffffffefffff
+ffdfffffffffffffff3fffcfffffffffffffffffefffff
+ffdffffffffffffffc041fcfe1fff87fffffffffefffff
+ffdfffffffffffffff3f9fcfccfff33fffffffffefffff
+ffdfffffffffffffff3f9fcf9cffe73fffffffffefffff
+ffdfffffffe03fffff3f9fcf9cffe7ffffffffffefffff
+ffdfffffffe03fffff3f9fcf80ffe7ffffffffffefffff
+ffdfffffffffffffff3f9fcf9fffe7ffffffffffefffff
+ffdfffffffffffffff3f9fcf9ff1e7ffffffffffefffff
+ffdfffffffffffffff3f9fcfccf1f33fffffffffefffff
+ffdffffffffffffffc0c0201e1f1f87fffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffff0783ffffffffffffefffff
+ffdfffffffffffffffffffffe7f3ffffffffffffefffff
+ffdfffffffffffffffffffffe7f3ffffffffffffefffff
+ffdfffffffffffffff078783e7f3f0fc3fff0fffefffff
+ffdffffffffffffffe673339e7f3e6799ffe67ffefffff
+ffdffffffffffffffce673f9e7f3cf339ffce7ffefffff
+ffdfffffffe03ffffce67ff9e7f3cf33fffcffffefffff
+ffdfffffffe03ffffce67f81e7f3cf33fffcffffefffff
+ffdffffffffffffffce67f39e7f3cf33fffcffffefffff
+ffdffffffffffffffce67f39e7f3cf33fe3cffffefffff
+ffdffffffffffffffe473331e7f3e6799e3e67ffefffff
+ffdfffffffffffffff278789008070fc3e3f0fffefffff
+ffdfffffffffffffffe7ffffffffffffffffffffefffff
+ffdffffffffffffffce7ffffffffffffffffffffefffff
+ffdffffffffffffffe0fffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffff3fffffffffffffffffffffefffff
+ffdfffffffffffffff3fffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdffffffffffffff83f0fffe1ffffffffffffffefffff
+ffdfffffffffffffff3e67ffccffffffffffffffefffff
+ffdfffffffffffffff3cf3ff9cffffffffffffffefffff
+ffdfffffffe03fffff3cf3ff9fffffffffffffffefffff
+ffdfffffffe03fffff3cf3ff9fffffffffffffffefffff
+ffdfffffffffffffff3cf3ff9fffffffffffffffefffff
+ffdfffffffffffffff3cf3c79fffffffffffffffefffff
+ffdfffffffffffffff3e67c7ccffffffffffffffefffff
+ffdffffffffffffff8070fc7e1ffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffdfffffffffffffffffffffffffffffffffffffefffff
+ffc00000000000000000000000000000000000000fffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffff
+showpage
+%ps_include: inclusion end
+grestore
+PS-include-dict-dw begin
+o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice
+ initgraphics 0.1 setlinewidth boxpath stroke grestore} if
+clear o-stack aload pop
+context end restore
+%ps_include: end
+/saveobj save def
+mark
+8 I f
+( prevent its)2 361( black rectangle on the left is a scroll bar; the menu is …
+( the)1 121( Above)1 243(becoming unwieldy.)1 642 3 720 4092 t
+8 CW f
+(\304\304sam\304\304)1750 4092 w
+8 I f
+( a list of files, presented exactly as with the)9 1397(line is a list of comm…
+8 CW f
+(f)4810 4092 w
+8 I f
+(com-)4881 4092 w
+(mand.)720 4192 w
+10 R f
+(When)970 4468 w
+10 CW f
+(sam)1241 4468 w
+10 R f
+(requests that a window be swept, in response to)8 1972 1 1454 4468 t
+10 CW f
+(new)3460 4468 w
+10 R f
+(,)3640 4468 w
+10 CW f
+(xerox)3699 4468 w
+10 R f
+(or)4033 4468 w
+10 CW f
+(reshape)4150 4468 w
+10 R f
+(, it changes)2 470 1 4570 4468 t
+( may be used to)4 631( this state, the mouse)4 844( In)1 135(the mouse cursor…
+( one corner and releasing it at the opposite corner.)9 2103(indicate an arbit…
+( button 3 may simply be clicked, whereupon)7 1860(More conveniently,)1 795 2 …
+10 CW f
+(sam)3412 4828 w
+10 R f
+(creates the maximal rectangle that)4 1411 1 3629 4828 t
+( the)1 155(contains the cursor and abuts)4 1187 2 720 4948 t
+10 CW f
+(sam)2095 4948 w
+10 R f
+( placing the)2 482(window. By)1 522 2 2308 4948 t
+10 CW f
+(sam)3345 4948 w
+10 R f
+(window in the middle of the screen,)6 1482 1 3558 4948 t
+( stacked fully-overlapping windows can be)5 1721(the user can define two regi…
+( simple user interface trick makes window creation notice-)8 2403( This)1 236…
+(ably easier.)1 454 1 720 5308 t
+(The cut-and-paste editor is essentially the same as that in Smalltalk-80.)10 …
+6 R f
+(11)3914 5414 w
+10 R f
+( always)1 313(The text in dot is)4 718 2 4009 5464 t
+( after the)2 358( a character is typed it replaces dot, and sets dot to the n…
+( button, moving)2 657( 1 is used for selection: pressing the)7 1492( Button)1…
+( to\) the text between the points where the button was)10 2248(the mouse, and…
+( a null string; this is called clicking.)7 1477( and releasing at the same po…
+(Clicking twice quickly, or)3 1077 1 720 6064 t
+10 I f
+(double clicking,)1 648 1 1833 6064 t
+10 R f
+(selects larger objects; for example, double clicking in a word)9 2524 1 2516 …
+( clicking just inside an opening bracket selects the text contained in the br…
+( double-clicking)1 656( The)1 209( similarly for parentheses, quotes, and so …
+( If)1 127(rules reflect a bias toward programmers.)5 1667 2 720 6424 t
+10 CW f
+(sam)2550 6424 w
+10 R f
+( more for word processing, double-clicks)5 1701(were intended)1 573 2 2766 64…
+(would probably select linguistic structures such as sentences.)7 2441 1 720 6…
+( is the)2 245( This)1 231( outside the current window, it makes the indicated…
+(easiest way to switch between windows and files.)7 1980 1 720 6820 t
+( mostly apply to the)4 835( These)1 298( of editing functions \(see Figure 4\…
+(selected text:)1 537 1 720 7096 t
+10 CW f
+(cut)1295 7096 w
+10 R f
+( remembers it in a hidden buffer called the)8 1792(deletes the selected text,…
+10 I f
+(snarf buffer,)1 507 1 4533 7096 t
+10 CW f
+(paste)720 7216 w
+10 R f
+(replaces the selected text by the contents of the snarf buffer,)10 2426 1 104…
+10 CW f
+(snarf)3501 7216 w
+10 R f
+(just copies the selected text to)5 1211 1 3829 7216 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 12 12
+%%Page: 13 13
+/saveobj save def
+mark
+13 pagesetup
+10 R f
+(- 13 -)2 216 1 2772 480 t
+(the snarf buffer,)2 667 1 720 840 t
+10 CW f
+(look)1426 840 w
+10 R f
+( next literal occurrence of the selected text, and)8 1990(searches forward fo…
+10 CW f
+(<mux>)4740 840 w
+10 R f
+(exchanges snarf buffers with the window system in which)8 2510 1 720 960 t
+10 CW f
+(sam)3280 960 w
+10 R f
+( the last regular)3 693( Finally,)1 384(is running.)1 453 3 3510 960 t
+( occurrence of a match for the)6 1291(expression used appears as a menu entry…
+(expression.)720 1200 w
+cleartomark
+saveobj restore
+%ps_include: begin
+save
+/ed {exch def} def
+{} /showpage ed
+{} /copypage ed
+{} /erasepage ed
+{} /letter ed
+currentdict /findfont known systemdict /findfont known and {
+ /findfont systemdict /findfont get def
+} if
+36 dict dup /PS-include-dict-dw ed begin
+/context ed
+count array astore /o-stack ed
+%ps_include: variables begin
+/llx 268 def
+/lly 352 def
+/urx 342.16 def
+/ury 438.4 def
+/w 0 def
+/o 0 def
+/s 0 def
+/cx 2880 def
+/cy -1872 def
+/sx 4320 def
+/sy 864 def
+/ax 0.5 def
+/ay 0.5 def
+/rot 0 def
+%ps_include: variables end
+{llx lly urx ury} /bbox ed
+{newpath 2 index exch 2 index exch dup 6 index exch
+ moveto 3 {lineto} repeat closepath} /boxpath ed
+{dup mul exch dup mul add sqrt} /len ed
+{2 copy gt {exch} if pop} /min ed
+{2 copy lt {exch} if pop} /max ed
+{transform round exch round exch A itransform} /nice ed
+{6 array} /n ed
+n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed
+urx llx sub 0 A dtransform len /Sx ed
+0 ury lly sub A dtransform len /Sy ed
+llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed
+rot dup sin abs /S ed cos abs /C ed
+Sx S mul Sy C mul add /H ed
+Sx C mul Sy S mul add /W ed
+sy H div /Scaley ed
+sx W div /Scalex ed
+s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if
+sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed
+sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed
+urx llx sub 0 A dtransform exch atan rot exch sub /rot ed
+n currentmatrix initgraphics setmatrix
+cx cy translate
+Scalex Scaley scale
+rot rotate
+Cx neg Cy neg translate
+A concat
+bbox boxpath clip newpath
+w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if
+end
+gsave
+%ps_include: inclusion begin
+/picstr 13 string def
+268 352 translate
+ 74.16 86.40 scale
+
+103 120 1 [103 0 0 -120 0 120]
+{currentfile picstr readhexstring pop} image
+
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+f800000000000000000001ffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffdfffffffdffff
+fbfffffffffff9fffffffdffff
+fbffffffe1ce603ffffffdffff
+fbffffffccce79fffffffdffff
+fbffffff9cce79fffffffdffff
+fbffffff9fce79fffffffdffff
+fbffffff9fce79fffffffdffff
+fbffffff9fce79fffffffdffff
+fbffffff9fce79fffffffdffff
+fbffffffccc479fffffffdffff
+fbffffffe1e27c3ffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffdfffffffdffff
+fbfffffffffff9fffffffdffff
+fbffff27c1e0e03c3ffffdffff
+fbffff139cce79f99ffffdffff
+fbffff39fccff9f39ffffdffff
+fbffff39fcc7f9f39ffffdffff
+fbffff39c0e0f9f01ffffdffff
+fbffff399cfc79f3fffffdffff
+fbffff399cfe79f3fffffdffff
+fbffff3398ce79f99ffffdffff
+fbffff07c4e0fc3c3ffffdffff
+fbffff3ffffffffffffffdffff
+fbffff3ffffffffffffffdffff
+fbffff3ffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffe1ffffdffff
+fbfffffffffffffcfffffdffff
+fbfffffffffffffcfffffdffff
+fbffff8391e0e3301ffffdffff
+fbffff3988ce703cfffffdffff
+fbffff3f9cfe733cfffffdffff
+fbffff1f9cfe73fcfffffdffff
+fbffff839ce073fcfffffdffff
+fbfffff19cce73fcfffffdffff
+fbfffff99cce73fcfffffdffff
+fbffff399ccc73fcfffffdffff
+fbffff839ce260f03ffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffff07fffff3ffffffdffff
+fbfffffe7fffff3ffffffdffff
+fbfffffe7fffff3ffffffdffff
+fbfffffe7e1f0f39fffffdffff
+fbfffffe7cce673bfffffdffff
+fbfffffe79e4f337fffffdffff
+fbfffffe79e4f32ffffffdffff
+fbfffffe79e4f30ffffffdffff
+fbfffffe79e4f327fffffdffff
+fbfffffe79e4f333fffffdffff
+fbfffffe7cce6739fffffdffff
+fbfffff00e1f0f39fffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+fbfffffbfffffffbfffffdffff
+fbfffff3fffffff9fffffdffff
+fbffffe72dce673cfffffdffff
+fbffffcf00ce673e7ffffdffff
+fbffff9f24ce737f3ffffdffff
+fbffff1f24ce70ff1ffffdffff
+fbffff9f24ce7dff3ffffdffff
+fbffffcf24ce787e7ffffdffff
+fbffffe724ce767cfffffdffff
+fbfffff324c46739fffffdffff
+fbfffffb24e2673bfffffdffff
+fbfffffffffffffffffffdffff
+fbfffffffffffffffffffdffff
+f800000000000000000001ffff
+f800000000000000000001ffff
+f800c04000030000200001ffff
+f800c18000030000180001ffff
+f8018300000180000c0001ffff
+f801830000c180dc0c0c01ffff
+f803060000c0c0ee060c01ffff
+f803060000c0c0c6060c01ffff
+f806060007f860c6067f81ffff
+f806060007f860c6067f81ffff
+f80c060000c030c6060c01ffff
+f80c0601c0c030c6060c01ffff
+f8180601c0c018c6060c01ffff
+f8180301c00018c60c001e0fff
+f830030000000c000c001e3fff
+f830018000000c0018001effff
+f80000400000000020001e7fff
+ffffffffffffffffffffe03fff
+ffffffffffffffffffffe41fff
+ffffffffffffffffffffe60fff
+ffffffffffffffffffffef07ff
+ffffffffffffffffffffef83ff
+ffffffffffffffffffffffc1ff
+ffffffffffffffffffffffe0ff
+fffffffffffffffffffffff07f
+fffffffffffffffffffffff83f
+fffffffffffffffffffffffc1f
+fffffffffffffffffffffffe3f
+ffffffffffffffffffffffff7f
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+ffffffffffffffffffffffffff
+showpage
+%ps_include: inclusion end
+grestore
+PS-include-dict-dw begin
+o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice
+ initgraphics 0.1 setlinewidth boxpath stroke grestore} if
+clear o-stack aload pop
+context end restore
+%ps_include: end
+/saveobj save def
+mark
+8 I f
+( bottom entry tracks the most recently used regular expression, which may be …
+10 R f
+( mouse language is entirely due to the equal-)8 1794(The relationship between…
+( example, to make a set of changes)7 1419( For)1 193( button 1 on the mouse.)…
+( be set by double clicking on the left brace that begins the subroutine, whic…
+( address-free command then typed in the)6 1660( An)1 178(dot for the command …
+10 CW f
+(sam)3858 3040 w
+10 R f
+(window will apply only)3 971 1 4069 3040 t
+( idea is to select what you want, and)8 1453( The)1 206( text between the ope…
+( of)1 108( And)1 222( want to do with it, whether invoked by a menu selection…
+( relationship)1 516( This)1 247( command completes.)2 912(course, the value o…
+( to explain, but comfortable, even natural, in)7 1859(between mouse interface…
+(practice.)720 3640 w
+10 B f
+(The Implementation)1 875 1 720 3880 t
+10 R f
+(The next few sections describe how)5 1447 1 720 4036 t
+10 CW f
+(sam)2196 4036 w
+10 R f
+( together, first the host part, then the inter-component com-)9 2411(is put)1…
+( dis-)1 181( explaining how the command language is implemented, the)8 2462( …
+( presen-)1 320( The)1 207(cussion follows \(roughly\) the path of a character…
+( because that is how the program was designed and because the algo-)12 2844(t…
+(rithms are easy to provide, given the right data structures.)9 2299 1 720 451…
+10 B f
+(Parsing and execution)2 945 1 720 4756 t
+10 R f
+( recursive descent)2 754(The command language is interpreted by parsing each …
+( editors instead)2 617( Most)1 262( top-down executor.)2 813(parser, and when…
+( and unambiguous to)3 875( of a parser makes it easy)6 1080( Use)1 217(employ…
+( conventions such as back-)4 1102( escape)1 299( First,)1 268(detect when a c…
+( the command isn't finished, the parser keeps)7 1850( if)1 117( multiple-line…
+( example, a multiple-line append driven by an)7 1836(reading. For)1 513 2 720…
+10 CW f
+(x)3094 5512 w
+10 R f
+(command is straightforward:)2 1154 1 3179 5512 t
+9 CW f
+(x/.*\\n/ g/Peter/ a)2 972 1 1008 5682 t
+(one line about Peter)3 1080 1 1008 5792 t
+(another line about Peter)3 1296 1 1008 5902 t
+(.)1008 6012 w
+10 R f
+(Other Unix editors would require a backslash after all but the last line.)12 …
+( advantage is specific to the two-process structure of)8 2167(The other)1 393…
+10 CW f
+(sam)3564 6348 w
+10 R f
+( host process must decide)4 1057(. The)1 239 2 3744 6348 t
+( easily resolved)2 631( problem is)2 462( This)1 234(when a command is comple…
+(by having the lexical analyzer read the single stream of events from the term…
+(ing and mouse commands, but passing to the parser characters typed to the)12 …
+10 CW f
+(sam)3823 6708 w
+10 R f
+( This)1 234(command window.)1 772 2 4034 6708 t
+( complicated by the availability of cut-and-paste editing in the)9 2510(schem…
+10 CW f
+(sam)3979 6828 w
+10 R f
+(window, but that dif-)3 853 1 4187 6828 t
+(ficulty is resolved by applying the rules used in)8 1925 1 720 6948 t
+10 CW f
+(mux)2674 6948 w
+10 R f
+(: when a newline is typed to the)7 1296 1 2854 6948 t
+10 CW f
+(sam)4179 6948 w
+10 R f
+(window, all text)2 653 1 4387 6948 t
+( permits arbi-)2 554( This)1 236( newline is made available to the parser.)7 …
+(trary editing to be done to a command before typing newline and thereby reque…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 13 13
+%%Page: 14 14
+/saveobj save def
+mark
+14 pagesetup
+10 R f
+(- 14 -)2 216 1 2772 480 t
+( and commands is regular enough to be)7 1574(The parser is driven by a table …
+( replacement text in a substitution, so the syn-)8 1859( are few special case…
+( include whether the command allows)5 1542( These)1 293( can be encoded with …
+(an address \(for example,)3 989 1 720 1200 t
+10 CW f
+(e)1737 1200 w
+10 R f
+( in)1 105(does not\), whether it takes a regular expression \(as)8 2044 2 182…
+10 CW f
+(x)4001 1200 w
+10 R f
+(and)4088 1200 w
+10 CW f
+(s)4259 1200 w
+10 R f
+(\), whether it takes)3 721 1 4319 1200 t
+(replacement text \(as in)3 915 1 720 1320 t
+10 CW f
+(c)1663 1320 w
+10 R f
+(or)1751 1320 w
+10 CW f
+(i)1862 1320 w
+10 R f
+( syntax of regular expres-)4 1035( internal)1 333( The)1 208(\), which may be…
+( Regular)1 377( parser; a regular expression is a leaf of the command parse t…
+(expressions are discussed fully in the next section.)7 2014 1 720 1560 t
+( a com-)2 315(The parser table also has information about defaults, so the in…
+( example, the parser fills in the implicit)7 1676( For)1 204(plete tree.)1 40…
+10 CW f
+(0)3048 1836 w
+10 R f
+(and)3148 1836 w
+10 CW f
+($)3332 1836 w
+10 R f
+(in the abbreviated address)3 1084 1 3432 1836 t
+10 CW f
+(,)4556 1836 w
+10 R f
+(\(comma\),)4655 1836 w
+(inserts a)1 345 1 720 1956 t
+10 CW f
+(+)1105 1956 w
+10 R f
+( default)1 318(to the left of an unadorned regular expression in an address, …
+(address)720 2076 w
+10 CW f
+(.)1044 2076 w
+10 R f
+(\(dot\) for commands that expect an address but are not given one.)11 2589 1 …
+( address is evaluated left-to-right)4 1358( The)1 216( complete command is pa…
+( the)1 156( like many of)3 560( Addresses,)1 495(starting from the value of d…
+(data structures in)2 682 1 720 2472 t
+10 CW f
+(sam)1427 2472 w
+10 R f
+(, are held in a C structure and passed around by value:)11 2162 1 1607 2472 t
+9 CW f
+( Position in a file */)5 1188( /*)1 270( Posn;)1 918(typedef long)1 648 4 100…
+(typedef struct Range{)2 1134 1 1008 2752 t
+( p2;)1 216(Posn p1,)1 1026 2 1440 2862 t
+(}Range;)1008 2972 w
+(typedef struct Address{)2 1242 1 1008 3082 t
+(Range r;)1 540 1 1440 3192 t
+(File *f;)1 1026 1 1440 3302 t
+(}Address;)1008 3412 w
+10 R f
+( encoded as a substring \(character positions)6 1795(An address is)2 556 2 72…
+10 CW f
+(p1)3106 3592 w
+10 R f
+(to)3261 3592 w
+10 CW f
+(p2)3374 3592 w
+10 R f
+(\) in a file)3 393 1 3494 3592 t
+10 CW f
+(f)3922 3592 w
+10 R f
+( data type)2 408(. \(The)1 273 2 3982 3592 t
+10 CW f
+(File)4698 3592 w
+10 R f
+(is)4973 3592 w
+(described in detail below.\))3 1059 1 720 3712 t
+( interpreter is an)3 690(The address)1 491 2 970 3868 t
+10 CW f
+(Address)2189 3868 w
+10 R f
+(-valued function that traverses the parse tree describing an)8 2431 1 2609 38…
+(address \(the parse tree for the address has type)8 1855 1 720 3988 t
+10 CW f
+(Addrtree)2600 3988 w
+10 R f
+(\):)3080 3988 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 14 14
+%%Page: 15 15
+/saveobj save def
+mark
+15 pagesetup
+10 R f
+(- 15 -)2 216 1 2772 480 t
+9 CW f
+(Address)1008 830 w
+(address\(ap, a, sign\))2 1080 1 1008 940 t
+(Addrtree *ap;)1 702 1 1440 1050 t
+(Address a;)1 540 1 1440 1160 t
+(int sign;)1 486 1 1440 1270 t
+({)1008 1380 w
+(Address a2;)1 594 1 1440 1490 t
+(do)1440 1600 w
+(switch\(ap->type\){)1872 1710 w
+(case '.':)1 486 1 1872 1820 t
+(a=a.f->dot;)2304 1930 w
+(break;)2304 2040 w
+(case '$':)1 486 1 1872 2150 t
+(a.r.p1=a.r.p2=a.f->nbytes;)2304 2260 w
+(break;)2304 2370 w
+(case '"':)1 486 1 1872 2480 t
+(a=matchfile\(a, ap->aregexp\)->dot;)1 1782 1 2304 2590 t
+(break;)2304 2700 w
+(case ',':)1 486 1 1872 2810 t
+(a2=address\(ap->right, a, 0\);)2 1512 1 2304 2920 t
+(a=address\(ap->left, a, 0\);)2 1404 1 2304 3030 t
+(if\(a.f!=a2.f || a2.r.p2<a.r.p1\))2 1674 1 2304 3140 t
+(error\(Eorder\);)2736 3250 w
+(a.r.p2=a2.r.p2;)2304 3360 w
+(return a;)1 486 1 2304 3470 t
+(/* and so on */)4 810 1 1872 3580 t
+(})1872 3690 w
+(while\(\(ap=ap->right\)!=0\);)1440 3800 w
+(return a;)1 486 1 1440 3910 t
+(})1008 4020 w
+10 R f
+( non-local)1 408(Throughout, errors are handled by a)5 1460 2 970 4236 t
+10 CW f
+(goto)2869 4236 w
+10 R f
+(\(a)3140 4236 w
+10 CW f
+(setjmp/longjmp)3248 4236 w
+10 R f
+(in C terminology\) hid-)3 921 1 4119 4236 t
+( routine called)2 587(den in a)2 334 2 720 4356 t
+10 CW f
+(error)1674 4356 w
+10 R f
+(that immediately aborts the execution, retracts any partially made changes)9 …
+( argument to)2 513( The)1 209( level of the parser.)4 783(\(see the section b…
+10 CW f
+(error)4644 4476 w
+10 R f
+(is)4973 4476 w
+( possibly helpful message such as `?addresses out of)8 2179(an enumeration ty…
+( common messages are kept short; for example the message for a failed regular…
+(search is `?search.')2 760 1 720 4836 t
+(Character addresses such as)3 1125 1 970 4992 t
+10 CW f
+(#3)2126 4992 w
+10 R f
+(are trivial to implement, as the)5 1251 1 2277 4992 t
+10 CW f
+(File)3559 4992 w
+10 R f
+(data structure is accessible by)4 1210 1 3830 4992 t
+( However,)1 447(character number.)1 726 2 720 5112 t
+10 CW f
+(sam)1925 5112 w
+10 R f
+( of newlines \320 it is too expen-)7 1277(keeps no information about the posi…
+( Except)1 330( are computed by reading the file, counting newlines.)8 2148(si…
+( access is fast enough to make the technique practical, and)10 2325(in very l…
+(lines are not central to the structure of the command language.)10 2492 1 720…
+(The command interpreter, called)3 1308 1 970 5628 t
+10 CW f
+(cmdexec)2305 5628 w
+10 R f
+( parse table includes a func-)5 1131( The)1 208( also straightforward.)2 857(…
+( as arguments the calculated address)5 1484( function receives)2 725( That)1 …
+( command and the command tree \(of type)7 1729(for the)1 271 2 720 5868 t
+10 CW f
+(Cmdtree)2754 5868 w
+10 R f
+(\), which may contain information such as the)7 1866 1 3174 5868 t
+( for example, is the function for the)7 1414( Here,)1 268(subtree for compoun…
+10 CW f
+(g)3786 5988 w
+10 R f
+(and)3871 5988 w
+10 CW f
+(v)4040 5988 w
+10 R f
+(commands:)4125 5988 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 15 15
+%%Page: 16 16
+/saveobj save def
+mark
+16 pagesetup
+10 R f
+(- 16 -)2 216 1 2772 480 t
+9 CW f
+(int)1008 830 w
+(g_cmd\(a, cp\))1 648 1 1008 940 t
+(Address a;)1 540 1 1440 1050 t
+(Cmdtree *cp;)1 648 1 1440 1160 t
+({)1008 1270 w
+(compile\(cp->regexp\);)1440 1380 w
+(if\(execute\(a.f, a.r.p1, a.r.p2\) != \(cp->cmdchar=='v'\)\){)4 2970 1 1440 1…
+(a.f->dot=a;)1872 1600 w
+(return cmdexec\(a, cp->subcmd\);)2 1620 1 1872 1710 t
+(})1440 1820 w
+( indicate that execution is to continue */)7 2268( /*)1 324(return TRUE;)1 64…
+(})1008 2040 w
+10 R f
+(\()720 2220 w
+10 CW f
+(Compile)753 2220 w
+10 R f
+(and)1202 2220 w
+10 CW f
+(execute)1375 2220 w
+10 R f
+( Because)1 387(are part of the regular expression code, described in the next…
+(the parser and the)3 706 1 720 2340 t
+10 CW f
+(File)1451 2340 w
+10 R f
+(data structure do most of the work, most commands are similarly brief.)11 283…
+10 B f
+(Regular expressions)1 858 1 720 2580 t
+10 R f
+(The regular expression code in)4 1266 1 720 2736 t
+10 CW f
+(sam)2020 2736 w
+10 R f
+( than compiled on-the-fly, implementation of)5 1854(is an interpreted, rather…
+(Thompson's non-deterministic finite automaton algorithm.)4 2392 1 720 2856 t
+6 R f
+(12)3112 2806 w
+10 R f
+( the expressions)2 656(The syntax and semantics of)4 1177 2 3207 2856 t
+(are as in the Unix program)5 1092 1 720 2976 t
+10 CW f
+(egrep)1842 2976 w
+10 R f
+( only)1 209( The)1 211( alternation, closures, character classes, and so on.)…
+(changes in the notation are two additions:)6 1755 1 720 3096 t
+10 CW f
+(\\n)2515 3096 w
+10 R f
+( and)1 183(is translated to, and matches, a newline character,)7 2083 2 2675 …
+10 CW f
+(@)4980 3096 w
+10 R f
+( In)1 140(matches any character.)2 924 2 720 3216 t
+10 CW f
+(egrep)1816 3216 w
+10 R f
+(, the character)2 575 1 2116 3216 t
+10 CW f
+(.)2723 3216 w
+10 R f
+(matches any character except newline, and in)6 1856 1 2816 3216 t
+10 CW f
+(sam)4705 3216 w
+10 R f
+(the)4918 3216 w
+(same rule seemed safest, to prevent idioms like)7 2033 1 720 3336 t
+10 CW f
+(.*)2799 3336 w
+10 R f
+(from spanning newlines.)2 1027 1 2965 3336 t
+10 CW f
+(Egrep)4063 3336 w
+10 R f
+(expressions are)1 632 1 4408 3336 t
+( certainly it would make sense if all the special charac-)10 2209(arguably to…
+( wouldn't have peculiar mean-)4 1223(ters were two-character sequences, so th…
+( regular expressions are necessary, and)5 1660(ings \320 but for an interesti…
+10 CW f
+(egrep)4740 3696 w
+10 R f
+( it seemed superfluous to define a new)7 1568( Also,)1 269( Unix programs.)2 …
+(syntax, since various Unix programs \()5 1520 1 720 3936 t
+10 CW f
+(ed)2240 3936 w
+10 R f
+(,)2360 3936 w
+10 CW f
+(egrep)2410 3936 w
+10 R f
+(and)2735 3936 w
+10 CW f
+(vi)2904 3936 w
+10 R f
+(\) define too many already.)4 1050 1 3024 3936 t
+(The expressions are compiled by a routine,)6 1812 1 970 4092 t
+10 CW f
+(compile)2823 4092 w
+10 R f
+( the description of the non-)5 1164(, that generates)2 633 2 3243 4092 t
+( second routine,)2 669( A)1 139(deterministic finite state machine.)3 1405 3 …
+10 CW f
+(execute)2975 4212 w
+10 R f
+(, interprets the machine to generate the)6 1645 1 3395 4212 t
+( else-)1 238( algorithm is described)3 985( The)1 229(leftmost-longest match …
+(where.)720 4452 w
+6 R f
+(12,13)988 4402 w
+10 CW f
+(Execute)1152 4452 w
+10 R f
+( sets a global variable, of type)6 1213(reports whether a match was found, an…
+10 CW f
+(Range)4459 4452 w
+10 R f
+(, to the)2 281 1 4759 4452 t
+(substring matched.)1 755 1 720 4572 t
+( such as when searching backwards for an)7 1752(A trick is required to evalua…
+( example,)1 388(expression. For)1 641 2 720 4848 t
+9 CW f
+(-/P.*r/)1008 5018 w
+10 R f
+( expression, however, is defined for a)6 1532( The)1 211( file for a match of…
+( solution is to construct a machine identical to the machine for a forward se…
+( the concatenation operators \(the other operators are symmetric under direct…
+(to exchange the meaning of the operators)6 1652 1 720 5558 t
+10 CW f
+(\303)2398 5558 w
+10 R f
+(and)2484 5558 w
+10 CW f
+($)2654 5558 w
+10 R f
+(, and then to read the file backwards, looking for the usual)11 2326 1 2714 5…
+(earliest longest match.)2 896 1 720 5678 t
+10 CW f
+(Execute)970 5834 w
+10 R f
+( as)1 118( interpret looping constructs such)4 1368( To)1 170(generates only …
+(the)720 5954 w
+10 CW f
+(x)874 5954 w
+10 R f
+(command,)966 5954 w
+10 CW f
+(sam)1417 5954 w
+10 R f
+(must therefore synchronize between calls of)5 1794 1 1629 5954 t
+10 CW f
+(execute)3455 5954 w
+10 R f
+( null)1 187(to avoid problems with)3 946 2 3907 5954 t
+( example, even given the leftmost-longest rule, the expression)8 2522(matches…
+10 CW f
+(a*)3820 6074 w
+10 R f
+( in the)2 264(matches three times)2 805 2 3971 6074 t
+(string)720 6194 w
+10 CW f
+(ab)978 6194 w
+10 R f
+(\(the character)1 549 1 1128 6194 t
+10 CW f
+(a)1706 6194 w
+10 R f
+(, the null string between the)5 1130 1 1766 6194 t
+10 CW f
+(a)2925 6194 w
+10 R f
+(and)3014 6194 w
+10 CW f
+(b)3187 6194 w
+10 R f
+( returning a)2 468( After)1 264(, and the final null string\).)5 1061 3 3247 …
+(match for the)2 532 1 720 6314 t
+10 CW f
+(a)1277 6314 w
+10 R f
+(,)1337 6314 w
+10 CW f
+(sam)1387 6314 w
+10 R f
+(must not match the null string before the)7 1624 1 1592 6314 t
+10 CW f
+(b)3241 6314 w
+10 R f
+( algorithm starts)2 650(. The)1 230 2 3301 6314 t
+10 CW f
+(execute)4206 6314 w
+10 R f
+( end)1 170(at the)1 219 2 4651 6314 t
+( match, and if the match it returns is null and abuts the previous match, rej…
+(advances the initial position one character.)5 1702 1 720 6554 t
+10 B f
+(Memory allocation)1 807 1 720 6794 t
+10 R f
+(The C language has no memory allocation primitives, although a standard libra…
+10 CW f
+(malloc)4454 6950 w
+10 R f
+(, pro-)1 226 1 4814 6950 t
+( however, it can be better to write a custom)9 1769( specific uses,)2 569( Fo…
+( rather, pair of allocators\) described here work in both the terminal and ho…
+(parts of)1 325 1 720 7310 t
+10 CW f
+(sam)1093 7310 w
+10 R f
+( are designed for efficient manipulation of strings, which are allocated and …
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 16 16
+%%Page: 17 17
+/saveobj save def
+mark
+17 pagesetup
+10 R f
+(- 17 -)2 216 1 2772 480 t
+( \(very large strings are written to disc\).)7 1643(frequently and vary in le…
+(More important, strings may be large and change size often, so to minimize me…
+(reclaim and to coalesce the unused portions of strings when they are truncate…
+(Objects to be allocated in)4 1023 1 970 1236 t
+10 CW f
+(sam)2020 1236 w
+10 R f
+( the first is C)4 529(are of two flavors:)3 740 2 2227 1236 t
+10 CW f
+(structs)3524 1236 w
+10 R f
+(, which are small and often)5 1096 1 3944 1236 t
+( integers whose base)3 868(addressed by pointer variables; the second is vari…
+( memory allocator in)3 841( The)1 207( used to access them.)4 848(pointer is …
+10 CW f
+(sam)3322 1476 w
+10 R f
+(is therefore in two parts: first, a tradi-)7 1511 1 3529 1476 t
+(tional first-fit allocator that provides fixed storage for)7 2145 1 720 1596 t
+10 CW f
+(structs)2891 1596 w
+10 R f
+( second, a garbage-compacting alloca-)4 1531(; and)1 198 2 3311 1596 t
+( two)1 190( The)1 220( reduces storage overhead for variable-sized objects, a…
+( allocator controlling the)3 997(types of objects are allocated from adjoinin…
+( prevents fragmentation)2 958( into two arenas simplifies compaction and)6 17…
+( garbage-compactable objects \(discussed in the next para-)7 2358( access rul…
+( so when the first-fit arena needs space, it moves the garbage-compacted)11 2…
+( is therefore created only at successively higher addresses,)8 2340( Storage)…
+(either when more garbage-compacted space is needed or when the first-fit aren…
+( the sole reposi-)3 647(Objects that may be compacted declare to the allocato…
+( then update the)3 668( compactor can)2 633( The)1 217(tory of the address of…
+( type)1 199( example, the implementation of)4 1300( For)1 190(address when th…
+10 CW f
+(List)3811 2832 w
+10 R f
+(\(really a variable-length)2 962 1 4078 2832 t
+(array\) is:)1 357 1 720 2952 t
+9 CW f
+(typedef struct List{)2 1080 1 1008 3122 t
+(int nused;)1 1188 1 1440 3232 t
+(long *ptr;)1 1134 1 1440 3342 t
+(}List;)1008 3452 w
+10 R f
+(The)720 3632 w
+10 CW f
+(ptr)910 3632 w
+10 R f
+( a)1 79( When)1 298(cell must always be used directly, and never copied.)8 21…
+10 CW f
+(List)3696 3632 w
+10 R f
+(is to be created the)4 792 1 3972 3632 t
+10 CW f
+(List)4800 3632 w
+10 R f
+(structure is allocated in the ordinary first-fit arena and its)9 2303 1 720 3…
+10 CW f
+(ptr)3051 3752 w
+10 R f
+(is allocated in the garbage-compacted arena.)5 1782 1 3258 3752 t
+(A similar data type for strings, called)6 1520 1 720 3872 t
+10 CW f
+(String)2271 3872 w
+10 R f
+( ele-)1 181(, stores variable-length character arrays of up to 32767)8 2228 2…
+(ments.)720 3992 w
+( matter of programming style:)4 1227(A related)1 374 2 970 4148 t
+10 CW f
+(sam)2603 4148 w
+10 R f
+(frequently passes structures by value, which simplifies)6 2225 1 2815 4148 t
+( programs have passed structures by reference, but implicit allocation on the…
+( passing is a relatively new feature of C \(it is not in the standard referen…
+(manual for C)2 537 1 720 4508 t
+6 R f
+(14)1257 4458 w
+10 R f
+( convenient and expressive,)3 1115( It's)1 187( commercial C compilers.)3 104…
+(though, and simplifies memory management by avoiding the allocator altogether…
+(aliases.)720 4748 w
+10 B f
+(Data structures for manipulating files)4 1610 1 720 4988 t
+10 R f
+(Experience with)1 651 1 720 5144 t
+10 CW f
+(jim)1396 5144 w
+10 R f
+( files)1 198( First,)1 260( requirements of the file data structure were few,…
+( the implementation must)3 1026( Second,)1 371( read and written quickly; add…
+( should be practical to edit many files, and)8 1719( \(It)1 147( sizes of fil…
+( implies that files be stored on disc, not)8 1593( This)1 231(files up to meg…
+( of virtual memory may argue otherwise, but the implementation of virtual)11 …
+( changes to files need)4 879( Third,)1 302( system is not something to depend…
+( are inverses of each other, which simplifies)7 1785( These)1 292( only two p…
+( the file, either)3 609( it must be easy and efficient to access)8 1610( Fina…
+(forwards or backwards, a byte at a time.)7 1598 1 720 6104 t
+(The)970 6260 w
+10 CW f
+(File)1157 6260 w
+10 R f
+( characters.)1 461(data type is constructed from three simpler data structure…
+( has an insertion and deletion operator, and the insertion and deletion opera…
+10 CW f
+(File)720 6500 w
+10 R f
+(type itself are constructed from them.)5 1497 1 985 6500 t
+(The simplest type is the)4 950 1 970 6656 t
+10 CW f
+(String)1945 6656 w
+10 R f
+( code that man-)3 621( The)1 206( hold strings in main memory.)5 1211(, which…
+(ages)720 6776 w
+10 CW f
+(Strings)926 6776 w
+10 R f
+( some moderate size, and in practice they are)8 1816(guarantees that they wil…
+(rarely larger than 8 Kbytes.)4 1098 1 720 6896 t
+10 CW f
+(Strings)1869 6896 w
+10 R f
+( little)1 211(have two purposes: they hold short strings like file names with…
+( are therefore used as)4 862( They)1 259( efficient to modify.)3 811(overhead…
+(the data structure for in-memory caches.)5 1612 1 720 7136 t
+(The disc copy of the file is managed by a data structure called a)13 2749 1 9…
+10 CW f
+(Disc)3760 7292 w
+10 R f
+( corresponds to a)3 730(, which)1 310 2 4000 7292 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 17 17
+%%Page: 18 18
+/saveobj save def
+mark
+18 pagesetup
+10 R f
+(- 18 -)2 216 1 2772 480 t
+( A)1 133(temporary file.)1 604 2 720 840 t
+10 CW f
+(Disc)1493 840 w
+10 R f
+( storage in main memory other than bookkeeping information; the actual)10 300…
+( reduce the number of open files needed,)7 1665( To)1 167(data being held is …
+10 CW f
+(sam)3935 960 w
+10 R f
+(opens a dozen tempo-)3 894 1 4146 960 t
+(rary Unix files and multiplexes the)5 1395 1 720 1080 t
+10 CW f
+(Discs)2141 1080 w
+10 R f
+( many files to be edited; the entire)7 1362( permits)1 326( This)1 229(upon t…
+10 CW f
+(sam)4860 1080 w
+10 R f
+(source \(48 files\) may be edited comfortably with a single instance of)11 28…
+10 CW f
+(sam)3571 1200 w
+10 R f
+( temporary file)2 609( one)1 176(. Allocating)1 504 3 3751 1200 t
+(per)720 1320 w
+10 CW f
+(Disc)874 1320 w
+10 R f
+( spreading the traffic)3 831( Also,)1 265( on the number of open files.)6 115…
+(among temporary files keeps the files shorter, and shorter files are more eff…
+(Unix I/O subsystem.)2 825 1 720 1560 t
+(A)970 1716 w
+10 CW f
+(Disc)1070 1716 w
+10 R f
+( between 1 and 4096 characters of)6 1386(is an array of fixed-length blocks, …
+( block addresses within the tempo-)5 1400( The)1 207( file system is 4096 byt…
+( stored in a)3 450(rary file and the length of each block are)8 1633 2 720 19…
+10 CW f
+(List)2831 1956 w
+10 R f
+( changes are made the live part of blocks)8 1653(. When)1 316 2 3071 1956 t
+( to keep the sizes between 2048)6 1300( are created and coalesced when necess…
+( actively changing part of the)5 1172( An)1 173(and 4096 bytes.)2 632 3 720 2…
+10 CW f
+(Disc)2723 2196 w
+10 R f
+( has about a kilobyte of slop that)7 1316(therefore typically)1 735 2 2989 21…
+( an)1 130( When)1 299( inserted or deleted without changing more than one blo…
+( one is allocated to receive the overflow, and the)9 1970(insertion would ove…
+(memory-resident list of blocks is rearranged to reflect the insertion of the …
+( data)1 200( The)1 214( modification to the file is prohibitively expensive.)…
+(type)720 2832 w
+10 CW f
+(Buffer)921 2832 w
+10 R f
+(consists of a)2 502 1 1310 2832 t
+10 CW f
+(Disc)1841 2832 w
+10 R f
+(to hold the data and a)5 872 1 2109 2832 t
+10 CW f
+(String)3009 2832 w
+10 R f
+( is the first of a)5 617( This)1 231(that acts as a cache.)4 795 3 3397 2832 t
+( throughout the data structures in)5 1378(series of caches)2 647 2 720 2952 t
+10 CW f
+(sam.)2782 2952 w
+10 R f
+(The caches not only improve performance, they)6 1981 1 3059 2952 t
+( the flow of data, particularly in the communication between the host and ter…
+( idea is developed below, in the section on communications.)9 2406(nal. This)…
+( traffic, changes to a)4 855(To reduce disc)2 607 2 970 3348 t
+10 CW f
+(Buffer)2468 3348 w
+10 R f
+(are mediated by a variable-length string, in memory,)7 2176 1 2864 3348 t
+( an insertion or deletion is made to a)8 1502( When)1 294(that acts as a cach…
+10 CW f
+(Buffer)3354 3468 w
+10 R f
+( can be accommo-)3 743(, if the change)3 583 2 3714 3468 t
+( cache becomes bigger than a block because of an insertion, some)11 2643( the…
+( written to the)3 558(of it is)2 258 2 720 3708 t
+10 CW f
+(Disc)1561 3708 w
+10 R f
+( the change does not intersect the cache, the cache)9 1999( If)1 116(and dele…
+( the new position if the change is smaller than a block; otherwise, it)13 274…
+( the)1 158(is sent directly to)3 722 2 720 3948 t
+10 CW f
+(Disc)1636 3948 w
+10 R f
+( is because large changes are typically sequential, whereupon the next)10 290…
+(change is unlikely to overlap the current one.)7 1802 1 720 4068 t
+(A)970 4224 w
+10 CW f
+(File)1067 4224 w
+10 R f
+(comprises a)1 474 1 1332 4224 t
+10 CW f
+(String)1831 4224 w
+10 R f
+( such as dot and the mod-)6 1027(to hold the file name and some ancillary dat…
+( most important components, though, are a pair of)8 2006( The)1 206(ified bit…
+10 CW f
+(Buffers)3298 4344 w
+10 R f
+(, one called the transcript and the)6 1322 1 3718 4344 t
+( use is described in the next section.)7 1437( Their)1 266(other the contents…
+( times)1 243( it may seem that the data is touched many)9 1701( Although)1 42…
+(on its way from the)4 825 1 720 4740 t
+10 CW f
+(Disc)1582 4740 w
+10 R f
+( \(by one Unix system call\) directly into the cache of the associated)12 278…
+10 CW f
+(Buffer)720 4860 w
+10 R f
+( the cache, the text is written directly from the)9 1922( when flushing)2 610…
+( principle applied throughout)3 1172( A)1 125( operations act directly on the…
+10 CW f
+(sam)4765 4980 w
+10 R f
+(is)4973 4980 w
+(that the fewer times the data is copied, the faster the program will run \(se…
+6 R f
+(15)4650 5050 w
+10 R f
+(\).)4710 5100 w
+cleartomark
+saveobj restore
+%%BeginGlobal
+%
+% Version 3.3.2 drawing procedures for dpost. Automatically pulled in when
+% needed.
+%
+
+/inpath false def
+/savematrix matrix def
+
+/Dl {
+ inpath
+ {pop pop neg lineto}
+ {newpath neg moveto neg lineto stroke}
+ ifelse
+} bind def
+
+/De {
+ /y1 exch 2 div def
+ /x1 exch 2 div def
+ /savematrix savematrix currentmatrix def
+ neg exch x1 add exch translate
+ x1 y1 scale
+ 0 0 1 0 360
+ inpath
+ {1 0 moveto arc savematrix setmatrix}
+ {newpath arc savematrix setmatrix stroke}
+ ifelse
+} bind def
+
+/Da {
+ /dy2 exch def
+ /dx2 exch def
+ /dy1 exch def
+ /dx1 exch def
+ dy1 add neg exch dx1 add exch
+ dx1 dx1 mul dy1 dy1 mul add sqrt
+ dy1 dx1 neg atan
+ dy2 neg dx2 atan
+ inpath
+ {arc}
+ {newpath arc stroke}
+ ifelse
+} bind def
+
+/DA {
+ /dy2 exch def
+ /dx2 exch def
+ /dy1 exch def
+ /dx1 exch def
+ dy1 add neg exch dx1 add exch
+ dx1 dx1 mul dy1 dy1 mul add sqrt
+ dy1 dx1 neg atan
+ dy2 neg dx2 atan
+ inpath
+ {arcn}
+ {newpath arcn stroke}
+ ifelse
+} bind def
+
+/Ds {
+ /y2 exch def
+ /x2 exch def
+ /y1 exch def
+ /x1 exch def
+ /y0 exch def
+ /x0 exch def
+ x0 5 x1 mul add 6 div
+ y0 5 y1 mul add -6 div
+ x2 5 x1 mul add 6 div
+ y2 5 y1 mul add -6 div
+ x1 x2 add 2 div
+ y1 y2 add -2 div
+ inpath
+ {curveto}
+ {newpath x0 x1 add 2 div y0 y1 add -2 div moveto curveto strok…
+ ifelse
+} bind def
+%%EndGlobal
+/saveobj save def
+mark
+10 R f
+2966 6183 2966 6471 Dl
+3599 6183 2966 6183 Dl
+3600 6471 3600 6183 Dl
+2967 6471 3600 6471 Dl
+10 CW f
+(Disc)3163 6347 w
+2966 6586 2966 6874 Dl
+3599 6586 2966 6586 Dl
+3600 6874 3600 6586 Dl
+2967 6874 3600 6874 Dl
+10 R f
+(temp. file)1 383 1 3092 6750 t
+3283 6471 3283 6586 Dl
+1642 6183 1642 6471 Dl
+2275 6183 1642 6183 Dl
+2275 6471 2275 6183 Dl
+1642 6471 2275 6471 Dl
+10 CW f
+(Disc)1838 6347 w
+1642 6586 1642 6874 Dl
+2275 6586 1642 6586 Dl
+2275 6874 2275 6586 Dl
+1642 6874 2275 6874 Dl
+10 R f
+(temp. file)1 383 1 1767 6750 t
+1958 6471 1958 6586 Dl
+1642 5722 1642 6010 Dl
+2275 5722 1642 5722 Dl
+2275 6010 2275 5722 Dl
+1642 6010 2275 6010 Dl
+10 CW f
+(Buffer)1778 5826 w
+10 R f
+(\(transcript\))1737 5946 w
+2390 5722 2390 6010 Dl
+2793 5722 2390 5722 Dl
+2794 6010 2794 5722 Dl
+2391 6010 2794 6010 Dl
+10 CW f
+(String)2412 5826 w
+10 R f
+(\(cache\))2446 5946 w
+2275 5866 2390 5866 Dl
+2966 5722 2966 6010 Dl
+3599 5722 2966 5722 Dl
+3600 6010 3600 5722 Dl
+2967 6010 3600 6010 Dl
+10 CW f
+(Buffer)3103 5826 w
+10 R f
+(\(contents\))3084 5946 w
+3715 5722 3715 6010 Dl
+4118 5722 3715 5722 Dl
+4118 6010 4118 5722 Dl
+3715 6010 4118 6010 Dl
+10 CW f
+(String)3737 5826 w
+10 R f
+(\(cache\))3771 5946 w
+3600 5866 3715 5866 Dl
+2182 5262 2182 5550 Dl
+2988 5262 2182 5262 Dl
+2988 5550 2988 5262 Dl
+2182 5550 2988 5550 Dl
+10 CW f
+(File)2465 5426 w
+1958 6011 1958 6183 Dl
+3283 6011 3283 6183 Dl
+1958 5636 1958 5722 Dl
+2318 5636 1958 5636 Dl
+2318 5550 2318 5636 Dl
+3283 5636 3283 5722 Dl
+2851 5636 3283 5636 Dl
+2851 5550 2851 5636 Dl
+8 I f
+( temporary files are stored in the standard repository for such files on the …
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 18 18
+%%Page: 19 19
+/saveobj save def
+mark
+19 pagesetup
+10 R f
+(- 19 -)2 216 1 2772 480 t
+( a)1 73(The contents of)2 627 2 970 840 t
+10 CW f
+(File)1699 840 w
+10 R f
+(are accessed by a routine that copies to a buffer a substring of a file start…
+( read a byte at a time, a per-)8 1166( To)1 168(at a specified offset.)3 824 …
+10 CW f
+(File)2878 960 w
+10 R f
+( a specified initial)3 731(array is loaded starting from)4 1159 2 3150 960 t
+( implementation is done by a macro similar to the)9 2012( The)1 208(position,…
+(C standard I/O)2 616 1 720 1200 t
+10 CW f
+(getc)1375 1200 w
+10 R f
+(macro.)1654 1200 w
+6 R f
+(14)1928 1150 w
+10 R f
+( reading may be done at any address, a minor change to the)12 2520(Because th…
+( array is read-only; there is no)6 1197( This)1 228(macro allows the file to …
+10 CW f
+(putc)3904 1320 w
+10 R f
+(.)4144 1320 w
+10 B f
+(Doing and undoing)2 820 1 720 1560 t
+10 CW f
+(Sam)720 1716 w
+10 R f
+( command language makes it easy to spec-)7 1733( The)1 209( to files.)2 333(h…
+( variable-length changes to a file millions of bytes long, and such changes m…
+( usual techniques for inserting and deleting strings are inadequate)9 2633( T…
+( The)1 207(under these conditions.)2 930 2 720 2076 t
+10 CW f
+(Buffer)1884 2076 w
+10 R f
+(and)2271 2076 w
+10 CW f
+(Disc)2442 2076 w
+10 R f
+(data structures are designed for efficient random access to)8 2331 1 2709 207…
+( be taken to avoid super-linear behavior when making many changes simultane-)…
+(ously.)720 2316 w
+10 CW f
+(Sam)970 2472 w
+10 R f
+(uses a two-pass algorithm for making changes, and treats each file as a datab…
+( when a command is)4 865( Instead,)1 374( the contents.)2 552( are not made d…
+(started, a `mark' containing a sequence number is placed in the transcript)11…
+10 CW f
+(Buffer)3714 2712 w
+10 R f
+( made)1 246(, and each change)3 720 2 4074 2712 t
+( name, is appended to the end of the tran-)9 1715(to the file, either an inse…
+( the command is complete, the transcript is rewound to the mark and applied t…
+( is to simplify tracking the)5 1198(One reason for separating evaluation from…
+( two-pass algorithm also allows all)5 1481( The)1 224(addresses of changes ma…
+(changes to apply to the)4 965 1 720 3348 t
+10 I f
+(original)1721 3348 w
+10 R f
+( the same command.)3 857(data: no change can affect another change made in)8 …
+( evaluating an)2 606(This is particularly important when)4 1506 2 720 3468 t
+10 CW f
+(x)2880 3468 w
+10 R f
+(command because it prevents regular expression)5 2052 1 2988 3468 t
+( is)1 114( the two-pass algorithm)3 1004( Also,)1 285(matches from stumbling …
+( to affect each other; for example,)6 1366(cleaner than the way other Unix ed…
+10 CW f
+(ed)4313 3708 w
+10 R f
+('s idioms to do)3 607 1 4433 3708 t
+( Instead,)1 364( on the implementation.)3 953(things like delete every other …
+10 CW f
+(sam)4090 3828 w
+10 R f
+('s simple model, in)3 770 1 4270 3828 t
+(which all changes in a command occur effectively simultaneously, is easy to e…
+( substring from locations 123 to 456'' and)7 1825(The records in the transcri…
+( changes are not at monotonically)5 1370( is an error if the)5 692( \(It)1 15…
+( the update is occurring, these numbers must be offset by earlier)11 2633( Wh…
+( to the update routine; moreover, all the numbers have been)10 2466(changes, …
+(computed before the first is examined.)5 1536 1 720 4584 t
+( it takes is to)4 566( All)1 193(Treating the file as a transaction system ha…
+( deletions and vice versa, and)5 1200(invert the transcript after it has been…
+(saving them in a holding)4 989 1 720 4980 t
+10 CW f
+(Buffer)1734 4980 w
+10 R f
+( can then be deleted from the transcript)7 1567( `do' transcript)2 593(. The)…
+10 CW f
+(Buffer)4510 4980 w
+10 R f
+(and)4896 4980 w
+( an undo is requested, the transcript is rewound and the undo transcript)12 2…
+( the transcript)2 561(executed. Because)1 767 2 720 5220 t
+10 CW f
+(Buffer)2079 5220 w
+10 R f
+( it accumulates successive)3 1066(is not truncated after each command,)5 1504…
+( can therefore back up the file arbitrarily, which is more helpful)11 2626( s…
+( \()1 87( form of undo.)3 589(than the more commonly implemented self-inverse…
+10 CW f
+(Sam)3450 5460 w
+10 R f
+(provides no way to undo an undo,)6 1381 1 3659 5460 t
+( mark in the)3 495( Each)1 254( re-interpreting the `do' transcript.\))4 1419…
+( offset into the transcript of the previous mark, to aid in)11 2390(transcrip…
+( also contain the value of dot and the modified bit so these can be restored)…
+( merely demands undoing all files whose latest change has the)10 2625( multip…
+(same sequence number as the current file.)6 1670 1 720 6060 t
+( encountered in the middle of a complicated com-)8 2017(Another benefit of ha…
+( rewinding the transcript to the mark beginning)7 1933( By)1 174( files in an…
+(the command, the partial command can be trivially undone.)8 2380 1 720 6456 t
+( implemented, it was unacceptably slow, so a cache was added to)11 2614(When …
+( reduced the number)3 820( This)1 229( small changes by a single larger one.)…
+(of insertions into the transaction)4 1308 1 720 6852 t
+10 CW f
+(Buffer)2058 6852 w
+10 R f
+( in performance, but made it)5 1161(, and made a dramatic improvement)5 1461 …
+( the caching method only works if changes)7 1718(impossible to handle changes…
+( cache was added, the transaction could in principle be sorted if the changes…
+( therefore acceptable performance with a)5 1664( current status is)3 676( The…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 19 19
+%%Page: 20 20
+/saveobj save def
+mark
+20 pagesetup
+10 R f
+(- 20 -)2 216 1 2772 480 t
+(minor restriction on global changes, which is sometimes, but rarely, an annoy…
+( simpler algorithms, but it is not pro-)7 1627(The update algorithm obviously…
+( principle of avoiding copying the data is still honored here,)10 2519( \(The…
+( through)1 343(although not as piously: the data is moved from contents' cach…
+( dead start a hundred)4 839( read from a)3 493( To)1 164( figures confirm the…
+( a VAX-11/750 takes 1.4 seconds of user time, 2.5 seconds of system time, and…
+( the same file in)4 670( Reading)1 391(real time.)1 385 3 720 1596 t
+10 CW f
+(ed)2199 1596 w
+10 R f
+( of system time, and 8)5 918(takes 6.0 seconds of user time, 1.7 seconds)7 17…
+(seconds of real time.)3 862 1 720 1716 t
+10 CW f
+(Sam)1644 1716 w
+10 R f
+( stated)1 271( more interesting example is the one)6 1520( A)1 134(uses about…
+( The)1 205(above: inserting a character between every pair of characters in t…
+10 CW f
+(sam)3801 1836 w
+10 R f
+(command is)1 486 1 4006 1836 t
+9 CW f
+(,y/@/ a/x/)1 540 1 1008 2006 t
+10 R f
+( 3 CPU seconds per kilobyte of input file, of which about a third is spent in…
+( translates to about 500 changes per second.)7 1759(code. This)1 442 2 720 23…
+10 CW f
+(Ed)2972 2306 w
+10 R f
+( make a similar)3 613(takes 1.5 seconds per kilobyte to)5 1309 2 3118 2306 t
+( same example in)3 702( The)1 207(change \(ignoring newlines\), but cannot un…
+10 CW f
+(ex)3554 2426 w
+10 R f
+(,)3674 2426 w
+6 R f
+(9)3699 2376 w
+10 R f
+(a variant of)2 458 1 3756 2426 t
+10 CW f
+(ed)4241 2426 w
+10 R f
+(done at the Uni-)3 652 1 4388 2426 t
+( summary,)1 428( In)1 139( takes 3 seconds.)3 689(versity of California at Be…
+10 CW f
+(sam)720 2666 w
+10 R f
+('s performance is comparable to that of other Unix editors, although it solve…
+10 B f
+(Communications)720 2906 w
+10 R f
+(The discussion so far has described the implementation of the host part of)12…
+10 CW f
+(sam)3899 3062 w
+10 R f
+(; the next few sections)4 961 1 4079 3062 t
+( machine with mouse and bitmap display can be engaged to improve interaction.…
+10 CW f
+(Sam)4607 3182 w
+10 R f
+(is not)1 224 1 4816 3182 t
+(the first editor to be written as two processes,)8 1811 1 720 3302 t
+6 R f
+(16)2531 3252 w
+10 R f
+(but its implementation has some unusual aspects.)6 1969 1 2616 3302 t
+(There are several ways)3 924 1 970 3458 t
+10 CW f
+(sam)1922 3458 w
+10 R f
+( first and simplest is to)5 929( The)1 208('s host and terminal parts may be …
+( This)1 229( command language to edit text on an ordinary terminal.)9 2256(fo…
+(mode is invoked by starting)4 1111 1 720 3698 t
+10 CW f
+(sam)1856 3698 w
+10 R f
+(with the)1 325 1 2061 3698 t
+10 CW f
+(-d)2412 3698 w
+10 R f
+( no options,)2 472(option. With)1 532 2 2558 3698 t
+10 CW f
+(sam)3588 3698 w
+10 R f
+(runs separate host and terminal)4 1246 1 3794 3698 t
+( Typically,)1 458( over the physical connection that joins them.)7 1815(progr…
+( display for)2 469(the connection is an RS-232 link between a Blit \(the prot…
+10 CW f
+(sam)3879 3938 w
+10 R f
+(\) and a host running the)5 981 1 4059 3938 t
+(Ninth Edition of the Unix operating system.)6 1782 1 720 4058 t
+6 R f
+(8)2502 4008 w
+10 R f
+(\(This is the version of the system used in the Computing Sci-)11 2480 1 2560…
+( are discussed in the)4 828( relevant aspects)2 669( Its)1 155(ences Research…
+(Blit paper.)1 432 1 720 4298 t
+6 R f
+(1)1152 4248 w
+10 R f
+( implementation of)2 779(\) The)1 223 2 1182 4298 t
+10 CW f
+(sam)2218 4298 w
+10 R f
+(for the Sun computer runs both processes on the same machine)10 2608 1 2432 4…
+(and connects them by a pipe.)5 1159 1 720 4418 t
+( division)1 351( The)1 208( of an RS-232 link necessitated the split between …
+( a self-contained one,)3 859(is a mixed blessing: a program in two parts is m…
+( terminal may be physically separated from)6 1736( The)1 206( configurations …
+( to be taken home while leaving the files)8 1628(the host, allowing the conve…
+( is also possible to run the host part on a remote machine:)12 2298( It)1 111…
+9 CW f
+(sam -r host)2 594 1 1008 5224 t
+10 R f
+( the network to establish the host part)7 1503(connects to the terminal in th…
+(of)720 5524 w
+10 CW f
+(sam)831 5524 w
+10 R f
+( allows)1 288( This)1 230( parts.)1 246( it cross-connects the I/O to join th…
+10 CW f
+(sam)4755 5524 w
+10 R f
+(to)4962 5524 w
+(be run on machines that do not support bitmap displays; for example,)11 2869 …
+10 CW f
+(sam)3623 5644 w
+10 R f
+( our)1 168(is the editor of choice on)5 1035 2 3837 5644 t
+(Cray X-MP/24.)1 632 1 720 5764 t
+10 CW f
+(Sam -r)1 334 1 1412 5764 t
+10 R f
+(involves)1780 5764 w
+10 I f
+(three)2153 5764 w
+10 R f
+( The)1 214(machines: the remote host, the terminal, and the local host.)9 243…
+(local host's job is simple but vital: it passes the data between the remote h…
+( exchange messages asynchronously \(rather than, say, as remote procedure)9 3…
+( correction because, whatever the configuration, the connection is)8 2709(cal…
+( mundane interaction tasks such as popping up menus and interpret-)10 2763( t…
+( example, the host knows nothing about)6 1657( For)1 200( not actions.)2 508(…
+( says)1 211(what is displayed on the screen, and when the user types a charac…
+( position in the)3 601(``insert a one-byte string at location 123 in file 7,'…
+( other words, the messages look very much like the transaction records in the…
+( host or terminal part of)5 961(Either the)1 393 2 970 6916 t
+10 CW f
+(sam)2352 6916 w
+10 R f
+( command language oper-)3 1048( The)1 208(may initiate a change to a file.)6 …
+( mouse operations are executed directly in the terminal to optimize)10 2708(a…
+( \(A)1 165( the terminal, and vice versa.)5 1200( initiated by the host progr…
+( which means that characters typed while a time-)8 1999(token is exchanged to…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 20 20
+%%Page: 21 21
+/saveobj save def
+mark
+21 pagesetup
+10 R f
+(- 21 -)2 216 1 2772 480 t
+( main-)1 264( To)1 167( buffered and do not appear until the command is compl…
+( track changes through a per-file data structure that records)9 2351(tain con…
+( data structure, called a)4 954( The)1 213(what portions of the file the term…
+10 CW f
+(Rasp)3994 1080 w
+10 R f
+(\(a weak pun: it's a)4 773 1 4267 1080 t
+( A)1 128(file with holes\) is held and updated by both the host and terminal.…
+10 CW f
+(Rasp)3574 1200 w
+10 R f
+( of)1 113(is a list)2 296 2 3845 1200 t
+10 CW f
+(Strings)4284 1200 w
+10 R f
+(holding)4734 1200 w
+( number of bytes in the interstices.)6 1415(those parts of the file known to …
+( needs the lengths of the various pieces\),)7 1619(Of course, the host doesn'…
+(but the structure is the same on both ends.)8 1679 1 720 1560 t
+(The)970 1716 w
+10 CW f
+(Rasp)1158 1716 w
+10 R f
+( the terminal keeps the text for portions of the)9 1909( Since)1 280(in the t…
+(file it has displayed, it need not request data from the host when revisiting…
+(obscured windows, which speeds things up considerably over low-speed links.)9…
+(It's trivial for the terminal to maintain its)7 1683 1 970 2112 t
+10 CW f
+(Rasp)2684 2112 w
+10 R f
+( on the terminal apply to)5 1015(, because all changes made)4 1101 2 2924 211…
+( made by the host are compared against the)8 1740( Changes)1 396( already loa…
+10 CW f
+(Rasp)4363 2232 w
+10 R f
+(during the)1 410 1 4630 2232 t
+( changes to pieces of the file loaded in the terminal are sent in)13 2509( Sm…
+( in the holes, are transmitted as messages with-)8 1894( changes, and changes…
+( a command is)3 610( When)1 298( the lengths of the deleted and inserted stri…
+( in their)2 319(completed, the terminal examines its visible windows to see i…
+10 CW f
+(Rasps)4015 2712 w
+10 R f
+(intersect the visi-)2 696 1 4344 2712 t
+( 512 bytes of sur-)4 731( then requests the missing data from the host, along…
+( technique)1 414( This)1 229( the file.)2 332(rounding data, to minimize the …
+( first level sends a minimum of informa-)7 1646( The)1 209(provides a kind of…
+( the file not being edited interactively; the second level waits until a chan…
+( is also helped by having the terminal respond)8 1933( course, performance)2 …
+( for small changes to active pieces of the file,)9 1906( Except)1 338( and si…
+(which are transmitted to the terminal without negotiation, the terminal is wh…
+(what is displayed; the host uses the)6 1405 1 720 3672 t
+10 CW f
+(Rasp)2150 3672 w
+10 R f
+(only to tell the terminal what might be relevant.)8 1907 1 2415 3672 t
+( host, the messages to the terminal describing the change are gener-)11 2727(…
+( the transcript of the changes to the contents of the)10 2023(ated by the rou…
+10 CW f
+(File)4008 3948 w
+10 R f
+( changes are)2 494(. Since)1 298 2 4248 3948 t
+( no extra code in the communications; the usual mes-)9 2173(undone by the sam…
+(sages describing changes to the file are sufficient to back up the screen ima…
+(The)970 4344 w
+10 CW f
+(Rasp)1164 4344 w
+10 R f
+(is a particularly good example of the way caches are used in)11 2556 1 1443 4…
+10 CW f
+(sam)4038 4344 w
+10 R f
+( it facilitates)2 524(. First,)1 298 2 4218 4344 t
+( so doing, it provides)4 870( In)1 140( portion of the text by placing the bu…
+( be)1 138( the form of data is to)6 968( Since)1 290(efficient access to a la…
+( characters will frequently be scanned sequentially,)6 2090(imposed by the us…
+( help keep performance good and linear when working with such)10 2724( Caches…
+(data.)720 4944 w
+(Second, the)1 468 1 970 5100 t
+10 CW f
+(Rasp)1465 5100 w
+10 R f
+(and several of the other caches have some)7 1696 1 1732 5100 t
+10 I f
+(read-ahead;)3456 5100 w
+10 R f
+(that is, the cache is loaded)5 1063 1 3977 5100 t
+( manipulating linear struc-)3 1090( When)1 299( than is needed for the job im…
+( time to access)3 588(tures, the accesses are usually sequential, and read-ah…
+( mode for people as well as programs; con-)8 1764( access is a common)4 839( …
+(sider scrolling through a document while looking for something.)8 2579 1 720 …
+( at least the implementation.)4 1158(Finally, like any good data structure, t…
+(The)720 5856 w
+10 CW f
+(Rasp)906 5856 w
+10 R f
+( between the host and terminal parts, but I)8 1718(was actually invented to c…
+( caches were more explicitly intended to serve a)8 1957( Other)1 282( was als…
+(double purpose: for example, the caches in)6 1728 1 720 6096 t
+10 CW f
+(Files)2476 6096 w
+10 R f
+(that coalesce updates not only reduce traffic to the tran-)9 2237 1 2803 6096…
+(script and contents)2 749 1 720 6216 t
+10 CW f
+(Buffers)1494 6216 w
+10 R f
+( clump screen updates so that complicated changes to the screen are)11 2718(,…
+( not need to write)4 715( saved me considerable work: I did)6 1427( This)1 23…
+( they)1 200( Also,)1 267( pay off in surprising ways.)5 1108( Caches)1 341( o…
+(tend to be independent, so their performance improvements are multiplicative.…
+10 B f
+(Data structures in the terminal)4 1320 1 720 6816 t
+10 R f
+(The terminal's job is to display and to maintain a consistent image of pieces…
+( text is always in memory, the data structures are considerably simpler than …
+(part.)720 7212 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 21 21
+%%Page: 22 22
+/saveobj save def
+mark
+22 pagesetup
+10 R f
+(- 22 -)2 216 1 2772 480 t
+10 CW f
+(Sam)970 840 w
+10 R f
+( does)1 211(typically has far more windows than)5 1466 2 1177 840 t
+10 CW f
+(mux)2882 840 w
+10 R f
+(, the window system within which its Blit imple-)8 1978 1 3062 840 t
+(mentation runs.)1 633 1 720 960 t
+10 CW f
+(Mux)1414 960 w
+10 R f
+(has a fairly small number of asynchronously updated windows;)8 2618 1 1630 96…
+10 CW f
+(sam)4284 960 w
+10 R f
+(needs a large)2 540 1 4500 960 t
+( different)1 376( The)1 213( static and often fully obscured.)5 1299(number o…
+(tradeoffs guided)1 670 1 720 1200 t
+10 CW f
+(sam)1434 1200 w
+10 R f
+( windows, called)2 710(away from the memory-intensive implementation of)5 218…
+10 CW f
+(Layers)4595 1200 w
+10 R f
+(,)4955 1200 w
+6 R f
+(17)4980 1150 w
+10 R f
+(used in)1 297 1 720 1320 t
+10 CW f
+(mux.)1053 1320 w
+10 R f
+( window,)1 384(Rather than depending on a complete bitmap image of the displa…
+10 CW f
+(sam)4860 1320 w
+10 R f
+( image from its in-memory text \(stored in the)8 1876(regenerates the)1 610 2…
+10 CW f
+(Rasp)3240 1440 w
+10 R f
+(\) when necessary, although it will use)6 1560 1 3480 1440 t
+( Like)1 243(such an image if it is available.)6 1300 2 720 1560 t
+10 CW f
+(Layers)2299 1560 w
+10 R f
+(, though,)1 364 1 2659 1560 t
+10 CW f
+(sam)3059 1560 w
+10 R f
+(uses the screen bitmap as active storage in)7 1765 1 3275 1560 t
+(which to update the image using)5 1306 1 720 1680 t
+10 CW f
+(bitblt)2053 1680 w
+10 R f
+(.)2413 1680 w
+6 R f
+(18,19)2438 1630 w
+10 R f
+(The resulting organization, pictured in Figure 6, has a global)9 2440 1 2600 …
+(array of windows, called)3 989 1 720 1800 t
+10 CW f
+(Flayers)1735 1800 w
+10 R f
+( structure)1 376(, each of which holds an image of a piece of text held in a …
+(called a)1 319 1 720 1920 t
+10 CW f
+(Frame)1076 1920 w
+10 R f
+( rectangular window full of text displayed in some)8 2102(, which in turn rep…
+10 CW f
+(Bitmap)4655 1920 w
+10 R f
+(.)5015 1920 w
+(Each)720 2040 w
+10 CW f
+(Flayer)947 2040 w
+10 R f
+( display, and simultaneously)3 1151(appears in a global list that orders them…
+( in the termi-)3 519( complement)1 520( The)1 206(as an element of a per-file…
+(nal of the)2 377 1 720 2280 t
+10 CW f
+(File)1122 2280 w
+10 R f
+(on the host is called a)5 863 1 1387 2280 t
+10 CW f
+(Text)2275 2280 w
+10 R f
+(; each connects its)3 729 1 2515 2280 t
+10 CW f
+(Flayers)3269 2280 w
+10 R f
+(to the associated)2 660 1 3714 2280 t
+10 CW f
+(Rasp)4399 2280 w
+10 R f
+(.)4639 2280 w
+1800 2478 1800 2766 Dl
+2606 2478 1800 2478 Dl
+2606 2766 2606 2478 Dl
+1800 2766 2606 2766 Dl
+10 CW f
+(Text)2083 2642 w
+2722 2478 2722 2766 Dl
+3370 2478 2722 2478 Dl
+3370 2766 3370 2478 Dl
+2722 2766 3370 2766 Dl
+(Rasp)2926 2642 w
+2721 2622 2606 2622 Dl
+3542 2622 3370 2622 Dl
+3542 2622 3470 2640 Dl
+3542 2622 3470 2604 Dl
+10 R f
+(to host)1 270 1 3677 2642 t
+2203 2881 2203 2766 Dl
+1987 2881 2203 2881 Dl
+1987 3097 1987 2881 Dl
+2102 3097 1987 3097 Dl
+2102 2953 2102 3241 Dl
+2642 2953 2102 2953 Dl
+2642 3241 2642 2953 Dl
+2102 3241 2642 3241 Dl
+10 CW f
+(Flayer)2192 3117 w
+2642 2953 2642 3241 Dl
+3182 2953 2642 2953 Dl
+3182 3241 3182 2953 Dl
+2642 3241 3182 3241 Dl
+3182 2953 3182 3241 Dl
+3722 2953 3182 2953 Dl
+3722 3241 3722 2953 Dl
+3182 3241 3722 3241 Dl
+3722 2953 3722 3241 Dl
+4262 2953 3722 2953 Dl
+4262 3241 4262 2953 Dl
+3722 3241 4262 3241 Dl
+(...)3902 3117 w
+(...)2822 3578 w
+2102 3414 2102 3702 Dl
+2642 3414 2102 3414 Dl
+2642 3702 2642 3414 Dl
+2102 3702 2642 3702 Dl
+(Frame)2222 3578 w
+2372 3413 2372 3241 Dl
+2912 3413 2912 3241 Dl
+3452 3413 3452 3241 Dl
+3992 3413 3992 3241 Dl
+1498 3414 1498 3702 Dl
+1930 3414 1498 3414 Dl
+1930 3702 1930 3414 Dl
+1498 3702 1930 3702 Dl
+(Bitmap)1534 3518 w
+10 R f
+(\(cache\))1568 3638 w
+2102 3558 1930 3558 Dl
+2372 3817 2372 3702 Dl
+2012 3817 2372 3817 Dl
+2012 4033 2012 3817 Dl
+2127 4033 2012 4033 Dl
+2128 3889 2128 4177 Dl
+2452 3889 2128 3889 Dl
+2452 4177 2452 3889 Dl
+2128 4177 2452 4177 Dl
+10 CW f
+(Box)2200 4053 w
+2452 3889 2452 4177 Dl
+2776 3889 2452 3889 Dl
+2776 4177 2776 3889 Dl
+2452 4177 2776 4177 Dl
+2776 3889 2776 4177 Dl
+3100 3889 2776 3889 Dl
+3100 4177 3100 3889 Dl
+2776 4177 3100 4177 Dl
+3100 3889 3100 4177 Dl
+3424 3889 3100 3889 Dl
+3424 4177 3424 3889 Dl
+3100 4177 3424 4177 Dl
+(...)3172 4053 w
+8 I f
+(Figure 6. Data structures in the terminal.)6 1360 1 720 4355 t
+8 CW f
+(Flayers)2126 4355 w
+8 I f
+(are also linked together into a front-to-back list.)7 1587 1 2488 4355 t
+8 CW f
+(Boxes)4122 4355 w
+8 I f
+(are discussed in the)3 651 1 4389 4355 t
+(next section.)1 397 1 720 4455 t
+10 R f
+(The)970 4731 w
+10 CW f
+(Bitmap)1155 4731 w
+10 R f
+(for a)1 190 1 1545 4731 t
+10 CW f
+(Frame)1765 4731 w
+10 R f
+( a fully visible window, the)5 1124( For)1 195(contains the image of the text…
+10 CW f
+(Bitmap)4680 4731 w
+10 R f
+(will be the screen \(or at least the)7 1315 1 720 4851 t
+10 CW f
+(Layer)2063 4851 w
+10 R f
+(in which)1 350 1 2391 4851 t
+10 CW f
+(sam)2769 4851 w
+10 R f
+( while for partially obscured windows)5 1527(is being run\),)2 536 2 2977 485…
+(the)720 4971 w
+10 CW f
+(Bitmap)867 4971 w
+10 R f
+( the window is fully obscured, the)6 1357( If)1 116(will be off-screen.)2 728…
+10 CW f
+(Bitmap)3478 4971 w
+10 R f
+(will be null.)2 481 1 3863 4971 t
+(The)970 5127 w
+10 CW f
+(Bitmap)1159 5127 w
+10 R f
+( image)1 279( making changes to the display, most of the original)9 2152( Whe…
+( The)1 207( this.)1 197(will look the same in the final image, and the update…
+10 CW f
+(Frame)4038 5247 w
+10 R f
+(software updates)1 675 1 4365 5247 t
+(the image in the)3 644 1 720 5367 t
+10 CW f
+(Bitmap)1390 5367 w
+10 R f
+(incrementally; the)1 725 1 1776 5367 t
+10 CW f
+(Bitmap)2528 5367 w
+10 R f
+(is not just an image, it is a data structure.)9 1653 1 2915 5367 t
+6 R f
+(18,19)4568 5317 w
+10 R f
+(The job)1 310 1 4730 5367 t
+( therefore to use as much as possible of the existing image \(con-)12 2622(of…
+( a sort of two-dimensional string insertion)6 1713(verting the text from ASCI…
+( details of this process are described in the next section.)10 2221(algorithm…
+(The)970 5883 w
+10 CW f
+(Frame)1173 5883 w
+10 R f
+(software has no code to support overlapping windows; its job is to keep a sin…
+10 CW f
+(Bitmap)720 6003 w
+10 R f
+( falls to the)3 471( It)1 119(up to date.)2 435 3 1113 6003 t
+10 CW f
+(Flayer)2171 6003 w
+10 R f
+(software to multiplex the various)4 1349 1 2564 6003 t
+10 CW f
+(Bitmaps)3945 6003 w
+10 R f
+(onto the screen.)2 643 1 4397 6003 t
+(The problem of maintaining overlapping)4 1638 1 720 6123 t
+10 CW f
+(Flayers)2386 6123 w
+10 R f
+(is easier than for)3 671 1 2834 6123 t
+10 CW f
+(Layers)3533 6123 w
+6 R f
+(17)3893 6073 w
+10 R f
+( are made)2 395(because changes)1 664 2 3981 6123 t
+( reconstructed from the data stored in the)7 1722(synchronously and because t…
+10 CW f
+(Frame)720 6363 w
+10 R f
+(; the)1 180 1 1020 6363 t
+10 CW f
+(Layers)1231 6363 w
+10 R f
+( In)1 139(software makes no such assumptions.)4 1525 2 1622 6363 t
+10 CW f
+(sam)3317 6363 w
+10 R f
+(, the window being changed is almost)6 1543 1 3497 6363 t
+( when)1 247( However,)1 446(always fully visible, because the current window …
+( it may be necessary to)5 927(multi-file changes are being made, or when more…
+(update partially obscured windows.)3 1420 1 720 6723 t
+( If)1 125( fully visible, invisible \(fully obscured\), or partially visible.…
+(fully visible, the)2 687 1 720 6999 t
+10 CW f
+(Bitmap)1449 6999 w
+10 R f
+(is part of the screen, so when the)7 1427 1 1851 6999 t
+10 CW f
+(Flayer)3320 6999 w
+10 R f
+(update routine calls the)3 977 1 3722 6999 t
+10 CW f
+(Frame)4740 6999 w
+10 R f
+( is invisible, there is no associated)6 1489( the window)2 534( If)1 136(upda…
+10 CW f
+(Bitmap)720 7239 w
+10 R f
+( necessary is to update the)5 1075(, and all that is)4 614 2 1080 7239 t
+10 CW f
+(Frame)2800 7239 w
+10 R f
+( the window is)3 604( If)1 122(data structure, not the image.)4 1183 3 3131 7…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 22 22
+%%Page: 23 23
+/saveobj save def
+mark
+23 pagesetup
+10 R f
+(- 23 -)2 216 1 2772 480 t
+(partially visible, the)2 817 1 720 840 t
+10 CW f
+(Frame)1572 840 w
+10 R f
+( called to update the image in the off-screen)8 1839(routine is)1 385 2 1907 …
+10 CW f
+(Bitmap)4167 840 w
+10 R f
+(, which may)2 513 1 4527 840 t
+( The)1 207(require regenerating it from the text of the window.)8 2078 2 720 …
+10 CW f
+(Flayer)3032 960 w
+10 R f
+(code then clips this)3 775 1 3419 960 t
+10 CW f
+(Bitmap)4221 960 w
+10 R f
+(against the)1 432 1 4608 960 t
+10 CW f
+(Bitmaps)720 1080 w
+10 R f
+(of all)1 208 1 1165 1080 t
+10 CW f
+(Frames)1398 1080 w
+10 R f
+(in front of the)3 552 1 1783 1080 t
+10 CW f
+(Frame)2360 1080 w
+10 R f
+(being modified, and the remainder is copied to the display.)9 2350 1 2685 108…
+( than recreating the image off-screen for every change, or clipping all the c…
+( amounts of)2 476( these caches can also consume prohibitive)6 1731( Unfortun…
+( freed fairly liberally \320 after every change to the front-to-back order of…
+10 CW f
+(Flayers)4595 1476 w
+10 R f
+(.)5015 1476 w
+(The result is that the off-screen)5 1274 1 720 1596 t
+10 CW f
+(Bitmaps)2025 1596 w
+10 R f
+(exist only while multi-window changes are occurring, which is)8 2564 1 2476 1…
+( the user interface causes fully-)5 1278( Also,)1 271( improvement they provi…
+( creating a canonically sized and placed window requires)8 2357(obscured wind…
+(only a button click \320 which reduces the need for caching still further.)12…
+10 B f
+(Screen update)1 608 1 720 2232 t
+10 R f
+( update:)1 321(Only two low-level primitives are needed for incremental)7 230…
+10 CW f
+(bitblt)3373 2388 w
+10 R f
+(, which copies rectangles of pix-)5 1307 1 3733 2388 t
+(els, and)1 328 1 720 2508 t
+10 CW f
+(string)1096 2508 w
+10 R f
+(\(which in turn calls)3 840 1 1503 2508 t
+10 CW f
+(bitblt)2390 2508 w
+10 R f
+(\), which draws a null-terminated character string in a)8 2290 1 2750 2508 t
+10 CW f
+(Bitmap)720 2628 w
+10 R f
+(. A)1 153 1 1080 2628 t
+10 CW f
+(Frame)1264 2628 w
+10 R f
+(contains a list of)3 676 1 1595 2628 t
+10 CW f
+(Boxes)2302 2628 w
+10 R f
+( win-)1 215(, each of which defines a horizontal strip of text in the)11 2223…
+( A)1 126(dow \(see Figure 7\).)3 788 2 720 2748 t
+10 CW f
+(Box)1663 2748 w
+10 R f
+(has a character string)3 856 1 1872 2748 t
+10 CW f
+(str)2757 2748 w
+10 R f
+(, and a)2 271 1 2937 2748 t
+10 CW f
+(Rectangle rect)1 809 1 3237 2748 t
+10 R f
+(that defines the location)3 966 1 4074 2748 t
+( text in)2 280( \(The)1 239(of the strip in the window.)5 1060 3 720 2868 t
+10 CW f
+(str)2325 2868 w
+10 R f
+(is stored in the)3 592 1 2531 2868 t
+10 CW f
+(Box)3150 2868 w
+10 R f
+(separately from the)2 774 1 3357 2868 t
+10 CW f
+(Rasp)4158 2868 w
+10 R f
+(associated with)1 615 1 4425 2868 t
+(the window's file, so)3 853 1 720 2988 t
+10 CW f
+(Boxes)1603 2988 w
+10 R f
+( the)1 151( invariant is that the image of)6 1201( The)1 210(are self-contain…
+10 CW f
+(Box)4298 2988 w
+10 R f
+(can be repro-)2 533 1 4507 2988 t
+(duced by calling)2 660 1 720 3108 t
+10 CW f
+(string)1405 3108 w
+10 R f
+(with argument)1 581 1 1790 3108 t
+10 CW f
+(str)2397 3108 w
+10 R f
+(to draw the string in)4 809 1 2603 3108 t
+10 CW f
+(rect)3438 3108 w
+10 R f
+(, and the resulting picture fits per-)6 1362 1 3678 3108 t
+(fectly within)1 509 1 720 3228 t
+10 CW f
+(rect)1255 3228 w
+10 R f
+( other words, the)3 674(. In)1 159 2 1495 3228 t
+10 CW f
+(Boxes)2354 3228 w
+10 R f
+( tiling may be compli-)4 889( The)1 206(define the tiling of the window.)5 12…
+( editors use horizontal scrolling to)5 1413( Some)1 288( of text, which are f…
+( be)1 119(avoid this complication, but to be comfortable this technique requi…
+10 I f
+(too)4193 3468 w
+10 R f
+(long;)4346 3468 w
+10 CW f
+(sam)4577 3468 w
+10 R f
+(has no)1 258 1 4782 3468 t
+( programs and terminals traditionally fold long)6 1907( and perhaps more impo…
+(lines to make their contents fully visible.)6 1630 1 720 3708 t
+(Two special kinds of)3 841 1 970 3864 t
+10 CW f
+(Boxes)1839 3864 w
+10 R f
+( and tabs)2 361( Newlines)1 430(contain a single character: either a newline …
+( newline)1 353( A)1 134(are white space.)2 663 3 720 3984 t
+10 CW f
+(Box)1907 3984 w
+10 R f
+( following)1 425(always extends to the right edge of the window, forcing the)…
+10 CW f
+(Box)720 4104 w
+10 R f
+( on where it is located: it forces the next)9 1618( width of a tab depends)5 …
+10 CW f
+(Box)4336 4104 w
+10 R f
+(to begin at a)3 497 1 4543 4104 t
+( a minimum width equivalent to a blank \(blanks are drawn by)11 2472( also ha…
+10 CW f
+(string)4363 4224 w
+10 R f
+(and are)1 291 1 4749 4224 t
+(not treated specially\); newlines have a minimum width of zero.)9 2524 1 720 …
+929 4506 929 4614 Dl
+1505 4506 929 4506 Dl
+1505 4614 1505 4506 Dl
+929 4614 1505 4614 Dl
+1505 4506 1505 4614 Dl
+2700 4506 1505 4506 Dl
+2700 4614 2700 4506 Dl
+1505 4614 2700 4614 Dl
+10 CW f
+(for\(i=0; i<NL; i++\){)2 1200 1 1502 4580 t
+2700 4506 2700 4614 Dl
+3132 4506 2700 4506 Dl
+3132 4614 3132 4506 Dl
+2700 4614 3132 4614 Dl
+3132 4506 3132 4614 Dl
+4471 4506 3132 4506 Dl
+4471 4614 4471 4506 Dl
+3132 4614 4471 4614 Dl
+(/* for each element */)4 1320 1 3141 4580 t
+4471 4506 4471 4614 Dl
+4831 4506 4471 4506 Dl
+4831 4614 4831 4506 Dl
+4471 4614 4831 4614 Dl
+8 I f
+( line of text showing its)5 747(Figure 7. A)2 368 2 720 4852 t
+8 CW f
+(Boxes)1857 4852 w
+8 I f
+( first two blank)3 487(. The)1 182 2 2097 4852 t
+8 CW f
+(Boxes)2788 4852 w
+8 I f
+( are handled)2 407( Spaces)1 263(contain tabs; the last contains a newline.)6…
+(as ordinary characters.)2 750 1 720 4952 t
+10 R f
+(The update algorithms always use the)5 1631 1 970 5228 t
+10 CW f
+(Bitmap)2651 5228 w
+10 R f
+(image of the text \(either the display or cache)8 1979 1 3061 5228 t
+10 CW f
+(Bitmap)720 5348 w
+10 R f
+( examine the characters within a)5 1318(\); they never)2 518 2 1080 5348 t
+10 CW f
+(Box)2947 5348 w
+10 R f
+(except when the)2 660 1 3158 5348 t
+10 CW f
+(Box)3849 5348 w
+10 R f
+(needs to be split in two.)5 980 1 4060 5348 t
+( the window consists of a tiling of)7 1365(Before a change,)2 672 2 720 5468 t
+10 CW f
+(Boxes)2783 5468 w
+10 R f
+(; after the change the window is tiled differently.)8 1957 1 3083 5468 t
+( algorithms are not strictly)4 1075( The)1 210( rearrange the tiles in place,…
+( move)1 250(optimal \320 for example, they can clear a pixel that is later go…
+( that doesn't need to be moved, and they move each tile at most once.)14 2975…
+10 CW f
+(Frinsert)3971 5828 w
+10 R f
+(on a Blit can)3 550 1 4490 5828 t
+(absorb over a thousand characters a second if the strings being inserted are …
+(Consider)970 6104 w
+10 CW f
+(frdelete)1368 6104 w
+10 R f
+( job is to delete a substring from a)8 1456(. Its)1 187 2 1848 6104 t
+10 CW f
+(Frame)3528 6104 w
+10 R f
+( the image of the)4 723(and restore)1 452 2 3865 6104 t
+10 CW f
+(Frame)720 6224 w
+10 R f
+( comprising possibly a partial line,)5 1428( image of a substring has a pecul…
+( reference, call this the)4 937( For)1 197( line.)1 208(zero or more full lin…
+10 I f
+(Z-shape.)4155 6344 w
+10 CW f
+(Frdelete)4560 6344 w
+10 R f
+( necessary, the)2 584(begins by splitting, if)3 854 2 720 6464 t
+10 CW f
+(Boxes)2183 6464 w
+10 R f
+(containing the ends of the substring so the substring begins and)10 2532 1 25…
+(ends on)1 311 1 720 6584 t
+10 CW f
+(Box)1059 6584 w
+10 R f
+( Z-shape is)2 446( the substring is being deleted, its image is not needed, s…
+( tiles \(that is, the images of)6 1092( Then,)1 282(then cleared.)1 511 3 720…
+10 CW f
+(Boxes)2632 6704 w
+10 R f
+(\) are copied, using)3 743 1 2932 6704 t
+10 CW f
+(bitblt)3701 6704 w
+10 R f
+(, from immediately after)3 979 1 4061 6704 t
+( \()1 92(the Z-shape to the beginning of the Z-shape, resulting in a new Z-sh…
+10 CW f
+(Boxes)3798 6824 w
+10 R f
+(whose contents would)2 908 1 4132 6824 t
+(span two lines in the new position must first be split.\))10 2142 1 720 6944 t
+( the)1 158(Copying the remainder of)3 1059 2 970 7100 t
+10 CW f
+(Frame)2223 7100 w
+10 R f
+(tile by tile this way will clearly accomplish the deletion but)10 2481 1 2559…
+( new)1 221(eventually, typically when the copying algorithm encounters a tab …
+10 CW f
+(x)4980 7220 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 23 23
+%%Page: 24 24
+/saveobj save def
+mark
+24 pagesetup
+10 R f
+(- 24 -)2 216 1 2772 480 t
+( correspondence implies that the Z-shape has its)7 2021( This)1 243( same.)1 …
+( aligned vertically, and a sequence of at most two)9 2073(beginning and endin…
+10 CW f
+(bitblts)3983 960 w
+10 R f
+(can be used to)3 601 1 4439 960 t
+( clear out the resulting empty space at the bottom of the win-)12 2526( last …
+( number of complete lines in the Z-shape closed by the final)11 2476(dow; the…
+10 CW f
+(bitblts.)720 1320 w
+10 R f
+( horizontally adjacent)2 877(The final step is to merge)5 1043 2 1230 1320 t
+10 CW f
+(Boxes)3181 1320 w
+10 R f
+( complete source to)3 797( The)1 211(of plain text.)2 520 3 3512 1320 t
+10 CW f
+(frdelete)720 1440 w
+10 R f
+(is less than 100 lines of C.)6 1053 1 1225 1440 t
+10 CW f
+(frinsert)970 1596 w
+10 R f
+( the)1 150(is more complicated because it must do four passes: one to constru…
+10 CW f
+(Box)4415 1596 w
+10 R f
+(list for the)2 417 1 4623 1596 t
+( one to reconnoitre, one to copy \(in opposite order to)10 2175(inserted stri…
+10 CW f
+(frdelete)3529 1716 w
+10 R f
+(\) the)1 187 1 4009 1716 t
+10 CW f
+(Boxes)4228 1716 w
+10 R f
+(to make the)2 480 1 4560 1716 t
+( though,)1 335( Overall,)1 381(hole for the new text, and finally one to copy…
+10 CW f
+(frinsert)4317 1836 w
+10 R f
+(has a)1 210 1 4830 1836 t
+(similar flavor to)2 676 1 720 1956 t
+10 CW f
+(frdelete)1437 1956 w
+10 R f
+(, and needn't be described further.)5 1445 1 1917 1956 t
+10 CW f
+(Frinsert)3428 1956 w
+10 R f
+(and its subsidiary routines)3 1092 1 3948 1956 t
+(comprise 211 lines of C.)4 980 1 720 2076 t
+(The terminal source code is 3024 lines of C, and the host source is 5797 line…
+10 B f
+(Discussion)720 2472 w
+(History)720 2712 w
+10 R f
+( of)1 113(The immediate ancestor)2 967 2 720 2868 t
+10 CW f
+(sam)1830 2868 w
+10 R f
+(was the original text editor for the Blit, called)8 1863 1 2040 2868 t
+10 CW f
+(jim)3933 2868 w
+10 R f
+(.)4113 2868 w
+10 CW f
+(Sam)4193 2868 w
+10 R f
+(inherited)4403 2868 w
+10 CW f
+(jim)4788 2868 w
+10 R f
+('s)4968 2868 w
+(two-process structure and mouse language almost unchanged, but)7 2721 1 720 2…
+10 CW f
+(jim)3480 2988 w
+10 R f
+(suffered from several drawbacks)3 1342 1 3698 2988 t
+( addressed in the design of)5 1112(that were)1 377 2 720 3108 t
+10 CW f
+(sam)2244 3108 w
+10 R f
+( most important of these was the lack of a command lan-)11 2376(. The)1 240 2…
+(guage. Although)1 694 1 720 3228 t
+10 CW f
+(jim)1442 3228 w
+10 R f
+( direct help with large or repetitive)6 1398(was easy to use for simple editi…
+( but this was no)4 640( it provided a command to pass selected text through a…
+(more satisfactory than could be expected of a stopgap measure.)9 2527 1 720 3…
+10 CW f
+(Jim)970 3624 w
+10 R f
+( interface to text, and)4 877(was written primarily as a vehicle for experime…
+(the experiment was successful.)3 1263 1 720 3744 t
+10 CW f
+(Jim)2041 3744 w
+10 R f
+(had some spin-offs:)2 804 1 2254 3744 t
+10 CW f
+(mux)3091 3744 w
+10 R f
+(, the second window system for the Blit, is)8 1769 1 3271 3744 t
+( the terminal part of)4 829(essentially a multiplexed version of)4 1453 2 720…
+10 CW f
+(jim)3036 3864 w
+10 R f
+(; and the debugger)3 767 1 3216 3864 t
+10 CW f
+(pi)4017 3864 w
+10 R f
+('s user interface)2 654 1 4137 3864 t
+6 R f
+(20)4791 3814 w
+10 R f
+(was)4885 3864 w
+(closely modeled on)2 785 1 720 3984 t
+10 CW f
+(jim)1534 3984 w
+10 R f
+( after a couple of years,)5 955('s. But)1 296 2 1714 3984 t
+10 CW f
+(jim)2994 3984 w
+10 R f
+( maintain and limiting)3 896(had become difficult to)3 941 2 3203 3984 t
+(to use, and its replacement was overdue.)6 1613 1 720 4104 t
+(I began the design of)4 837 1 970 4260 t
+10 CW f
+(sam)1832 4260 w
+10 R f
+(by asking)1 386 1 2037 4260 t
+10 CW f
+(jim)2448 4260 w
+10 R f
+( was probably a mistake;)4 997( This)1 229( they wanted.)2 537(customers what…
+( of the)2 259(the answers were essentially a list of features to be found in …
+( for a ``global substitute,'' but no)6 1359( instance, one common request was…
+( was looking for a scheme that would sup-)8 1724( I)1 87( it within a cut-and…
+( were)1 225( Ideas)1 267( context of some general command language.)6 1852(po…
+( line lengths)2 499(not forthcoming, though, particularly given my insistence…
+( a region of the screen that)6 1088( worse, I recognized that, since the mous…
+(was not an integral number of lines, the command language would best forget a…
+(and that meant the command language had to treat the file as a single string,…
+( I)1 96( very far and it was time to try building.)9 1710(Eventually, I decid…
+( of)1 115(knew that the terminal part could be built easily \320 that part)11…
+10 CW f
+(jim)3344 5496 w
+10 R f
+(behaved acceptably well \320 and that)5 1484 1 3556 5496 t
+( hard work was going to be in the host part: the file interface, command inte…
+(Moreover, I had some ideas about how the architecture of)9 2386 1 720 5736 t
+10 CW f
+(jim)3140 5736 w
+10 R f
+(could be improved without destroying its)5 1687 1 3353 5736 t
+( I began)2 331( So)1 161( worked out as well as I had hoped.)8 1451(basic str…
+( with the way)3 556(by designing the file data structure, starting)6 1770 2 7…
+10 CW f
+(jim)3076 5976 w
+10 R f
+(worked \320 comparable to a single structure)6 1754 1 3286 5976 t
+(merging)720 6096 w
+10 CW f
+(Disc)1080 6096 w
+10 R f
+(and)1347 6096 w
+10 CW f
+(Buffer)1518 6096 w
+10 R f
+( cache more general \320 and thinking about how glo-)9 2109(, which I split t…
+( answer was clearly that it had to be done in two passes, and the)14 2618( Th…
+(transcript-oriented implementation fell out naturally.)4 2106 1 720 6336 t
+10 CW f
+(Sam)970 6492 w
+10 R f
+( data structures and algorithms for manipulating text,)7 2190(was written bot…
+( retrospect, it turned out)4 973( In)1 138(through the command language and u…
+( were several times when I had)6 1247( There)1 284( general.)1 345(well, but …
+( command language, in)3 938( The)1 207( proceed with it.)3 655(a large body o…
+( beginning)1 429(particular, took almost a year to figure out, but can be imp…
+( inventing the)2 568( Similarly,)1 457(of that year\) in a day or two.)7 1199…
+10 CW f
+(Rasp)2978 7092 w
+10 R f
+(data structure delayed the connection of the)6 1788 1 3252 7092 t
+(host and terminal pieces by another few months.)7 1978 1 720 7212 t
+10 CW f
+(Sam)2754 7212 w
+10 R f
+(took about two years to write, although only about)8 2074 1 2966 7212 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 24 24
+%%Page: 25 25
+/saveobj save def
+mark
+25 pagesetup
+10 R f
+(- 25 -)2 216 1 2772 480 t
+(four months were spent actually working on it.)7 1870 1 720 840 t
+( process was unusual: the subset of the protocol that maintains the)11 2661(P…
+10 CW f
+(Rasp)4363 996 w
+10 R f
+(was simu-)1 410 1 4630 996 t
+(lated, debugged and verified by an automatic protocol analyzer,)8 2568 1 720 …
+6 R f
+(21)3288 1066 w
+10 R f
+( rest)1 171( The)1 207( from the start.)3 594(and was bug-free)2 692 4 3376 1…
+( keeping menus up to date, was unfortunately too unwieldy for such)11 2757(of…
+( in and)2 276(analysis, and was debugged by more traditional methods, primari…
+(out of the host.)3 600 1 720 1476 t
+10 B f
+(Reflections)720 1716 w
+10 CW f
+(Sam)720 1872 w
+10 R f
+( of the computing science)4 1078(is essentially the only interactive editor u…
+( same could not be said of)6 1055( The)1 207(research center in which I work.…
+10 CW f
+(jim)3303 1992 w
+10 R f
+( kept)1 198(; the lack of a command language)6 1359 2 3483 1992 t
+( of a user interface as comfortable as)7 1485( union)1 255( The)1 207(some pe…
+10 CW f
+(jim)3905 2112 w
+10 R f
+('s with a command lan-)4 955 1 4085 2112 t
+(guage as powerful as)3 851 1 720 2232 t
+10 CW f
+(ed)1600 2232 w
+10 R f
+( to)1 108('s\262 is essential)2 591 2 1720 2232 t
+10 CW f
+(sam)2449 2232 w
+10 R f
+( When)1 293('s success.)1 426 2 2629 2232 t
+10 CW f
+(sam)3378 2232 w
+10 R f
+(was first made available to the)5 1242 1 3588 2232 t
+10 CW f
+(jim)4860 2232 w
+10 R f
+( the months that followed, even)5 1315( In)1 144( to it within two or three d…
+(people who had never adopted)4 1219 1 720 2472 t
+10 CW f
+(jim)1964 2472 w
+10 R f
+(started using)1 508 1 2169 2472 t
+10 CW f
+(sam)2702 2472 w
+10 R f
+(exclusively.)2907 2472 w
+(To be honest,)2 555 1 970 2628 t
+10 CW f
+(ed)1558 2628 w
+10 R f
+(still gets occasional use, but usually when something quick needs to be done …
+(the overhead of downloading the terminal part of)7 1981 1 720 2748 t
+10 CW f
+(sam)2729 2748 w
+10 R f
+( as a `line' editor,)4 709( Also,)1 266(isn't worth the trouble.)3 922 3 2936…
+10 CW f
+(sam)4860 2748 w
+(-d)720 2868 w
+10 R f
+( it is)2 183( But)1 200( line editor.)2 468(is a bit odd; when using a good o…
+(fair to say that)3 598 1 720 2988 t
+10 CW f
+(sam)1351 2988 w
+10 R f
+('s command language has displaced)4 1464 1 1531 2988 t
+10 CW f
+(ed)3027 2988 w
+10 R f
+('s for most of the complicated editing that has)8 1893 1 3147 2988 t
+(kept line editors \(that is, command-driven editors\) with us.)8 2348 1 720 3…
+10 CW f
+(Sam)970 3264 w
+10 R f
+('s command language is even fancier than)6 1745 1 1150 3264 t
+10 CW f
+(ed)2931 3264 w
+10 R f
+('s, and most)2 508 1 3051 3264 t
+10 CW f
+(sam)3595 3264 w
+10 R f
+(customers don't come near to)4 1229 1 3811 3264 t
+( think the answer is yes, for two reasons.)8 1625( I)1 83( it need to be so s…
+(First, the)1 362 1 970 3540 t
+10 I f
+(model)1363 3540 w
+10 R f
+(for)1638 3540 w
+10 CW f
+(sam)1786 3540 w
+10 R f
+('s command language is really relatively simple, and certainly simpler than)1…
+(that of)1 262 1 720 3660 t
+10 CW f
+(ed)1011 3660 w
+10 R f
+( instance, there is only one kind of textual loop in)10 2009(. For)1 217 2 11…
+10 CW f
+(sam)3385 3660 w
+10 R f
+(\320 the)1 250 1 3593 3660 t
+10 CW f
+(x)3871 3660 w
+10 R f
+(command \320 while)2 772 1 3959 3660 t
+10 CW f
+(ed)4759 3660 w
+10 R f
+(has)4907 3660 w
+(three \(the)1 381 1 720 3780 t
+10 CW f
+(g)1128 3780 w
+10 R f
+( implicit loop over lines in multi-line substi-)7 1792(command, the global fl…
+(tutions\). Also,)1 598 1 720 3900 t
+10 CW f
+(ed)1346 3900 w
+10 R f
+( within lines, but in)4 784('s substitute command is necessary to make change…
+10 CW f
+(sam)4392 3900 w
+10 R f
+(the)4599 3900 w
+10 CW f
+(s)4748 3900 w
+10 R f
+(com-)4835 3900 w
+(mand is more of a familiar convenience than a necessity;)9 2270 1 720 4020 t
+10 CW f
+(c)3015 4020 w
+10 R f
+(and)3100 4020 w
+10 CW f
+(t)3269 4020 w
+10 R f
+(can do all the work.)4 790 1 3354 4020 t
+( to be about as powerful as)6 1082(Second, given a community that expects an …
+10 CW f
+(ed)4078 4176 w
+10 R f
+(, it's hard to see how)5 842 1 4198 4176 t
+10 CW f
+(sam)720 4296 w
+10 R f
+( want to do ``global substi-)5 1150( People)1 336( that expectation.)2 713(co…
+( sophistication)1 587( The)1 211( fancy changes.)2 629(tutes,'' and most are …
+( do global substitutes)3 848(of the command language is really just a veneer …
+( always want something more, however, and it's gratifying to be able)11 2831(…
+( real power of)3 595( The)1 218(to provide it.)2 542 3 720 4776 t
+10 CW f
+(sam)2113 4776 w
+10 R f
+('s command language comes from composability of the operators,)8 2747 1 2293 …
+( other words,)2 542( In)1 142( orthogonal to the underlying model.)5 1511(whi…
+10 CW f
+(sam)3708 4896 w
+10 R f
+(is not itself complex, but it)5 1118 1 3922 4896 t
+( you don't want to do anything complex, you can ignore the complexity)12 2915…
+(altogether, and many people do so.)5 1395 1 720 5136 t
+(Sometimes I am asked the opposite question: why didn't I just make)11 2801 1 …
+10 CW f
+(sam)3802 5292 w
+10 R f
+(a real programmable edi-)3 1026 1 4014 5292 t
+( main reason is a matter of taste: I like the editor to be the)14 2400( The)1…
+( a wor-)2 284( is one technical reason, though: programmability in editors is…
+( usually short-)2 604( editors are used to make particular,)6 1529( Programma…
+( things are generally easy)4 1038( If)1 122( providing shorthands for common …
+(to do in the first place, shorthands are not as helpful.)10 2197 1 720 5892 t
+10 CW f
+(Sam)2977 5892 w
+10 R f
+(makes common editing operations very easy,)5 1848 1 3192 5892 t
+( Also,)1 274( to complex editing problems seem commensurate with the problems…
+(the ability to edit the)4 840 1 720 6132 t
+10 CW f
+(sam)1588 6132 w
+10 R f
+( only takes a mouse button click)6 1300(window makes it easy to repeat comman…
+(to execute a command again.)4 1161 1 720 6252 t
+8 S1 f
+(__________________)720 6900 w
+8 R f
+(\262 The people who criticize)4 872 1 720 6990 t
+8 CW f
+(ed)1618 6990 w
+8 R f
+( and its close relative)4 698(as an interactive program often forget that it)…
+8 CW f
+(sed)3906 6990 w
+4 R f
+(7)4050 6950 w
+8 R f
+(still thrive as pro-)3 583 1 4097 6990 t
+( strength of these programs is independent of their convenience for interacti…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 25 25
+%%Page: 26 26
+/saveobj save def
+mark
+26 pagesetup
+10 R f
+(- 26 -)2 216 1 2772 480 t
+10 B f
+(Pros and cons)2 595 1 720 840 t
+10 CW f
+(Sam)720 996 w
+10 R f
+( the good things is the idea of struc-)8 1472( Among)1 355( its share of prob…
+( were arrived at serendipi-)4 1054( They)1 258(tural regular expressions, who…
+(tously when I attempted to distill the essence of)8 1936 1 720 1236 t
+10 CW f
+(ed)2685 1236 w
+10 R f
+( global substitution and recognized that)5 1599('s way of doing)3 636 2 2805 …
+(the looping command in)3 975 1 720 1356 t
+10 CW f
+(ed)1720 1356 w
+10 R f
+(was implicitly imposing a structure \(an array of lines\) on the file.)11 260…
+(Another of)1 444 1 970 1512 t
+10 CW f
+(sam)1448 1512 w
+10 R f
+( used an editor with a true)6 1097( had never before)3 721( I)1 92('s good th…
+( Undo)1 276(undo, but I would never go back now.)7 1545 2 720 1632 t
+10 I f
+(must)2570 1632 w
+10 R f
+( exam-)1 278( For)1 193(be done well, but if it is, it can be relied on.)11 1…
+( not sure how to write some intricate command, because if you make a)13 2869(…
+( from writing)2 545( learned two things about undo)5 1270( I)1 90(mistake, it…
+10 CW f
+(sam)4456 1872 w
+10 R f
+(: first, it's)2 404 1 4636 1872 t
+( the system)2 458(easy to provide if you design it in from the beginning, and…
+(has some subtle properties that may be unfamiliar or error-prone for users.)1…
+10 CW f
+(Sam)970 2268 w
+10 R f
+( avoids all fixed-size tables and data)6 1519( it)1 92( Because)1 393('s lack…
+(structures,)720 2388 w
+10 CW f
+(sam)1159 2388 w
+10 R f
+( More-)1 299( to make global changes to files that some of our other tools ca…
+( admit)1 255(over, the design keeps the performance linear when doing such op…
+10 CW f
+(sam)4650 2508 w
+10 R f
+(does)4857 2508 w
+(get slow when editing a huge file.)6 1351 1 720 2628 t
+( is poorly integrated into the surrounding)6 1680( the most obvious is that i…
+( design, the user interface in)5 1135( By)1 169(window system.)1 653 3 720 29…
+10 CW f
+(sam)2704 2904 w
+10 R f
+(feels almost identical to that of)5 1245 1 2911 2904 t
+10 CW f
+(mux)4183 2904 w
+10 R f
+(, but a thick wall)4 677 1 4363 2904 t
+(separates text in)2 655 1 720 3024 t
+10 CW f
+(sam)1406 3024 w
+10 R f
+(from the programs running in)4 1206 1 1617 3024 t
+10 CW f
+(mux)2854 3024 w
+10 R f
+( instance, the `snarf buffer' in)5 1220(. For)1 221 2 3034 3024 t
+10 CW f
+(sam)4507 3024 w
+10 R f
+(must be)1 321 1 4719 3024 t
+( that in)2 284(maintained separately from)2 1100 2 720 3144 t
+10 CW f
+(mux)2132 3144 w
+10 R f
+( is regrettable, but probably necessary given the unusual con-)9 2472(. This)…
+(figuration of the system, with a programmable terminal on the far end of an R…
+10 CW f
+(Sam)970 3420 w
+10 R f
+( it was written over such a long time, and has)10 1846( But)1 199( it.)1 110(…
+( to clean up the code and remove)7 1323(so many new \(to me\) ideas in it, th…
+( worst part is in the interconnection of the host)9 1898( The)1 209( problems…
+( a redesign for a more conventional window sys-)8 1946(and terminal parts, wh…
+( of the connec-)3 598( program must be split in two to use the terminal effec…
+( design if performance is to be acceptable.)7 1712(tion forces the separation…
+( procedure call protocol driven by the host, emitting only graphics commands,…
+( the other hand, if the terminal)6 1284( On)1 184(easy to write but wouldn't …
+( simpler file services from the host, regular expression searches would)10 28…
+( A)1 131( would be unreasonably slow.)4 1226(require that the terminal read t…
+( retrospect, the communications protocol)4 1658( In)1 139(compromise in which…
+( designed and verified formally, although I do not know of any tool that can …
+(relate the protocol to its implementation.)5 1627 1 720 4860 t
+(Not all of)2 385 1 970 5016 t
+10 CW f
+(sam)1382 5016 w
+10 R f
+( \(vener-)1 314( Some)1 280('s users are comfortable with its command languag…
+( a sort of ``)4 471(able\) people use)2 664 2 720 5136 t
+10 CW f
+(ed)1855 5136 w
+10 R f
+(subset'' of)1 431 1 2007 5136 t
+10 CW f
+(sam)2470 5136 w
+10 R f
+('s command language, and even ask why)6 1680 1 2650 5136 t
+10 CW f
+(sam)4362 5136 w
+10 R f
+('s command)1 498 1 4542 5136 t
+(language is not exactly)3 942 1 720 5256 t
+10 CW f
+(ed)1695 5256 w
+10 R f
+( course, is that)3 604( reason, of)2 434('s. \(The)1 343 3 1815 5256 t
+10 CW f
+(sam)3230 5256 w
+10 R f
+('s model for text does not include new-)7 1630 1 3410 5256 t
+( central to)2 399(lines, which are)2 631 2 720 5376 t
+10 CW f
+(ed)1775 5376 w
+10 R f
+( the text an array of newlines to the command language would be too)13 2759(.…
+( editors, such as)3 647( Some)1 281( mouse.)1 314(much of a break from the se…
+10 CW f
+(vi)4328 5496 w
+10 R f
+(, are willing to)3 592 1 4448 5496 t
+( difficulty is that)3 706( The)1 219( though.\))1 375(make this break,)2 687 …
+10 CW f
+(sam)2746 5616 w
+10 R f
+('s syntax is so close to)5 967 1 2926 5616 t
+10 CW f
+(ed)3932 5616 w
+10 R f
+('s that people believe it)4 988 1 4052 5616 t
+10 I f
+(should)720 5736 w
+10 R f
+( in hindsight, that making)4 1050( thought, with some justification)4 1318( I…
+10 CW f
+(sam)4010 5736 w
+10 R f
+(similar to)1 387 1 4221 5736 t
+10 CW f
+(ed)4639 5736 w
+10 R f
+(would)4790 5736 w
+( and raised the users' expectations too)6 1573( I may have overstepped)4 1015…
+( hard to decide which way to resolve this problem.)9 2019(much. It's)1 430 2 …
+(Finally, there is a tradeoff in)5 1187 1 970 6132 t
+10 CW f
+(sam)2192 6132 w
+10 R f
+(that was decided by the environment in which it runs:)9 2235 1 2407 6132 t
+10 CW f
+(sam)4677 6132 w
+10 R f
+(is a)1 147 1 4893 6132 t
+( The)1 220( system there might instead be multiple single-file editors.)8 244…
+( choice)1 298( the)1 159( If)1 128(decision was made primarily because starti…
+( still choose the multi-file architecture, because it allows groups of)10 270…
+( is delightful)2 518( It)1 117( a unit; the usefulness of the multi-file comm…
+(to have the source to an entire program available at your fingertips.)11 2683…
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 26 26
+%%Page: 27 27
+/saveobj save def
+mark
+27 pagesetup
+10 R f
+(- 27 -)2 216 1 2772 480 t
+10 B f
+(Acknowledgements)720 840 w
+10 R f
+(Tom Cargill suggested the idea behind the)6 1699 1 720 996 t
+10 CW f
+(Rasp)2445 996 w
+10 R f
+( Wilson and Ken Thompson influ-)5 1384( Norman)1 379(data structure.)1 566 3 …
+( improved by comments from Al Aho, Jon Bentley, Chris)9 2392( paper was)2 448…
+(Fraser, Gerard Holzmann, Brian Kernighan, Ted Kowalski, Doug McIlroy and Denn…
+9 B f
+(REFERENCES)720 1476 w
+8 R f
+( Pike, `The Blit: a multiplexed graphics terminal,')7 1588(1. R.)1 184 2 760 …
+8 I f
+(AT&T Bell Labs. Tech. J.,)4 835 1 2552 1648 t
+8 B f
+(63)3407 1648 w
+8 R f
+(, \(8\), 1607-1631 \(1984\).)3 770 1 3487 1648 t
+( Johnson,)1 302(2. L.)1 179 2 760 1784 t
+8 I f
+(MacWrite,)1261 1784 w
+8 R f
+(Apple Computer Inc., Cupertino, Calif. 1983.)5 1453 1 1618 1784 t
+( Lampson, `Bravo Manual,' in)4 969(3. B.)1 184 2 760 1920 t
+8 I f
+(Alto User's Handbook,)2 738 1 1933 1920 t
+8 R f
+( 1979.)1 220(pp. 31-62, Xerox Palo Alto Research Center, Palo Alto, Calif.)9 …
+( Teitelman, `A tour through Cedar,')5 1138(4. W.)1 205 2 760 2056 t
+8 I f
+(IEEE Software,)1 497 1 2123 2056 t
+8 B f
+(1)2640 2056 w
+8 R f
+(\(2\), 44-73 \(1984\).)2 570 1 2700 2056 t
+( Gutknecht, `Concepts of the text editor Lara,')7 1473(5. J.)1 161 2 760 2192…
+8 I f
+(Comm. ACM,)1 439 1 2414 2192 t
+8 B f
+(28)2873 2192 w
+8 R f
+(, \(9\), 942-960 \(1985\).)3 690 1 2953 2192 t
+( Telephone Laboratories,)2 797(6. Bell)1 243 2 760 2328 t
+8 I f
+(UNIX Programmer's Manual,)2 965 1 1820 2328 t
+8 R f
+(Holt, Rinehart and Winston, New York 1983.)6 1456 1 2805 2328 t
+( W. Kernighan and R. Pike,)5 882(7. B.)1 184 2 760 2464 t
+8 I f
+(The Unix Programming Environment,)3 1216 1 1846 2464 t
+8 R f
+(Prentice-Hall, Englewood Cliffs, New Jersey 1984.)5 1637 1 3082 2464 t
+(8.)760 2600 w
+8 I f
+( Programmer's Manual, Research Version, Ninth Edition, Volume 1,)8 2215(Unix …
+8 R f
+(AT&T Bell Laboratories, Murray)3 1076 1 3964 2600 t
+(Hill, New Jersey 1986.)3 733 1 870 2700 t
+(9.)760 2836 w
+8 I f
+( Distribution, Volumes 1 and 2C,)5 1174(Unix Time-Sharing System Programmer's…
+8 R f
+(University of)1 445 1 4595 2836 t
+(California, Berkeley, Calif. 1981.)3 1068 1 870 2936 t
+( Pike, `Structural Regular Expressions,')4 1264(10. R.)1 224 2 720 3072 t
+8 I f
+(Proc. EUUG Spring Conf., Helsinki 1987,)5 1357 1 2229 3072 t
+8 R f
+(Eur. Unix User's Group, Buntingford, Herts,)5 1433 1 3607 3072 t
+(UK 1987.)1 316 1 870 3172 t
+( Goldberg,)1 341(11. A.)1 228 2 720 3308 t
+8 I f
+(Smalltalk-80 \261 The Interactive Programming Environment,)5 1891 1 1309 3308…
+8 R f
+(Addison-Wesley, Reading, Mass. 1984.)3 1269 1 3220 3308 t
+( Thompson, `Regular expression search algorithm,')5 1637(12. K.)1 228 2 720 3…
+8 I f
+(Comm. ACM,)1 439 1 2605 3444 t
+8 B f
+(11)3064 3444 w
+8 R f
+(, \(6\), 419-422 \(1968\).)3 690 1 3144 3444 t
+( V. Aho, J. E. Hopcroft and J. D. Ullman,)9 1344(13. A.)1 228 2 720 3580 t
+8 I f
+( of Computer Algorithms,)3 826(The Design and Analysis)3 802 2 2314 3580 t
+8 R f
+(Addison-Wesley, Reading, Mass.)2 1075 1 3965 3580 t
+(1974.)870 3680 w
+( W. Kernighan and D. M. Ritchie,)6 1085(14. B.)1 224 2 720 3816 t
+8 I f
+(The C Programming Language,)3 1023 1 2049 3816 t
+8 R f
+(Prentice-Hall, Englewood Cliffs, New Jersey 1978.)5 1637 1 3092 3816 t
+( M. Waite, `The cost of lexical analysis,')7 1297(15. W.)1 245 2 720 3952 t
+8 I f
+(Softw. Pract. Exp.,)2 599 1 2282 3952 t
+8 B f
+(16)2901 3952 w
+8 R f
+(, \(5\), 473-488 \(1986\).)3 690 1 2981 3952 t
+( W. Fraser, `A generalized text editor,')6 1232(16. C.)1 224 2 720 4088 t
+8 I f
+(Comm. ACM,)1 439 1 2196 4088 t
+8 B f
+(23)2655 4088 w
+8 R f
+(, \(3\), 154-158 \(1980\).)3 690 1 2735 4088 t
+( Pike, `Graphics in overlapping bitmap layers,')6 1493(17. R.)1 224 2 720 422…
+8 I f
+(ACM Trans. on Graph.,)3 765 1 2457 4224 t
+8 B f
+(2)3242 4224 w
+8 R f
+(, \(2\) 135-160 \(1983\).)3 670 1 3282 4224 t
+( J. Guibas and J. Stolfi, `A language for bitmap manipulation,')10 1990(18. L…
+8 I f
+(ACM Trans. on Graph.,)3 765 1 2949 4360 t
+8 B f
+(1)3734 4360 w
+8 R f
+(, \(3\), 191-214 \(1982\).)3 690 1 3774 4360 t
+( Pike, B. Locanthi and J. Reiser, `Hardware/software trade-offs for bitmap gr…
+8 I f
+(Softw. Pract. Exp.,)2 617 1 4153 4496 t
+8 B f
+(15)4799 4496 w
+8 R f
+(, \(2\),)1 161 1 4879 4496 t
+(131-151 \(1985\).)1 518 1 870 4596 t
+( A. Cargill, `The feel of Pi,')6 886(20. T.)1 219 2 720 4732 t
+8 I f
+(Winter USENIX Conference Proceedings, Denver 1986,)5 1791 1 1845 4732 t
+8 R f
+(62-71, USENIX Assoc., El Cerrito, CA.)5 1283 1 3656 4732 t
+( J. Holzmann, `Tracing protocols,')4 1098(21. G.)1 228 2 720 4868 t
+8 I f
+(AT&T Tech. J.,)2 491 1 2066 4868 t
+8 B f
+(64)2577 4868 w
+8 R f
+(, \(10\), 2413-2434 \(1985\).)3 810 1 2657 4868 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 27 27
+%%Trailer
+done
+%%Pages: 27
+%%DocumentFonts: Courier Times-Bold Times-Italic Times-Roman Times-Roman Symbol
diff --git a/doc/sam.tut.ms b/doc/sam.tut.ms
@@ -0,0 +1,1776 @@
+.de P1
+.KS
+.DS
+.ft CW
+.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n
+..
+.de P2
+.ft 1
+.DE
+.KE
+..
+.de CW
+.lg 0
+\%\&\\$3\fB\\$1\fP\&\\$2
+.lg
+..
+.de WC
+.lg 0
+\%\&\\$3\fI\\$1\fP\&\\$2
+.lg
+..
+.TL
+A tutorial for the
+.CW sam
+.B
+command language
+.AU
+Rob Pike
+.AI
+.MH
+.AB
+.CW sam
+is an interactive text editor with a command language that makes heavy use
+of regular expressions.
+Although the language is syntactically similar to
+.CW ed (1),
+the details are interestingly different.
+This tutorial introduces the command language, but does not discuss
+the screen and mouse interface.
+With apologies to those unfamiliar with the Ninth Edition Blit software,
+it is assumed that the similarity of
+.CW sam
+to
+.CW mux (9)
+at this level makes
+.CW sam 's
+mouse language easy to learn.
+.PP
+The
+.CW sam
+command language applies identically to two environments:
+when running
+.CW sam
+on an ordinary terminal
+(\f2via\f1\f1
+.CW sam\ -d ),
+and in the command window of a
+.I downloaded
+.CW sam ,
+that is, one using the bitmap display and mouse.
+.AE
+.SH
+Introduction
+.PP
+This tutorial describes the command language of
+.CW sam ,
+an interactive text editor that runs on Blits and
+some computers with bitmap displays.
+For most editing tasks, the mouse-based editing features
+are sufficient, and they are easy to use and to learn.
+.PP
+The command language is often useful, however, particularly
+when making global changes.
+Unlike the commands in
+.CW ed ,
+which are necessary to make changes,
+.CW sam
+commands tend to be used
+only for complicated or repetitive editing tasks.
+It is in these more involved uses that
+the differences between
+.CW sam
+and other text editors are most evident.
+.PP
+.CW sam 's
+language makes it easy to do some things that other editors,
+including programs like
+.CW sed
+and
+.CW awk ,
+do not handle gracefully, so this tutorial serves partly as a
+lesson in
+.CW sam 's
+manner of manipulating text.
+The examples below therefore concentrate entirely on the language,
+assuming that facility with the use of the mouse in
+.CW sam
+is at worst easy to pick up.
+In fact,
+.CW sam
+can be run without the mouse at all (not
+.I downloaded ),
+by specifying the
+.CW -d
+flag, and it is this domain that the tutorial
+occupies; the command language in these modes
+are identical.
+.PP
+A word to the Unix adept:
+although
+.CW sam
+is syntactically very similar to
+.CW ed ,
+it is fundamentally and deliberately different in design and detailed semantic…
+You might use knowledge of
+.CW ed
+to predict how the substitute command works,
+but you'd only be right if you had used some understanding of
+.CW sam 's
+workings to influence your prediction.
+Be particularly careful about idioms.
+Idioms form in curious nooks of languages and depend on
+undependable peculiarities.
+.CW ed
+idioms simply don't work in
+.CW sam :
+.CW 1,$s/a/b/
+makes one substitution in the whole file, not one per line.
+.CW sam
+has its own idioms.
+Much of the purpose of this tutorial is to publish them
+and make fluency in
+.CW sam
+a matter of learning, not cunning.
+.PP
+The tutorial depends on familiarity with regular expressions, although
+some experience with a more traditional Unix editor may be helpful.
+To aid readers familiar with
+.CW ed ,
+I have pointed out in square brackets [] some of
+the relevant differences between
+.CW ed
+and
+.CW sam .
+Read these comments only if you wish
+to understand the differences; the lesson is about
+.CW sam ,
+not
+.CW sam
+.I vs.
+.CW ed .
+Another typographic convention is that output appears in
+.CW "this font,
+while typed input appears as
+.WC "slanty text.
+.PP
+Nomenclature:
+.CW sam
+keeps a copy of the text it is editing.
+This copy is called a
+.I file .
+To avoid confusion, I have called the permanent storage on disc a
+.I
+Unix file.
+.R
+.SH
+Text
+.PP
+To get started, we need some text to play with.
+Any text will do; try something from
+James Gosling's Emacs manual:
+.P1
+$ \fIsam -d
+a
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+\&.
+.ft
+.P2
+.WC "sam -d
+starts
+.CW sam
+running.
+The
+.CW a
+command adds text until a line containing just a period, and sets the
+.I
+current text
+.R
+(also called
+.I dot )
+to what was typed \(em everything between the
+.CW a
+and the period.
+.CW ed "" [
+would leave dot set to only the last line.]
+The
+.CW p
+command prints the current text:
+.P1
+.WC p
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.P2
+[Again,
+.CW ed
+would print only the last line.]
+The
+.CW a
+command adds its text
+.I after
+dot; the
+.CW i
+command is like
+.CW a,
+but adds the text
+.I before
+dot.
+.P1
+.ft I
+i
+Introduction
+\&.
+p
+.ft
+Introduction
+.P2
+There is also a
+.CW c
+command that changes (replaces) the current text,
+and
+.CW d
+that deletes it; these are illustrated below.
+.PP
+To see all the text, we can specify what text to print;
+for the moment, suffice it to say that
+.WC 0,$
+specifies the entire file.
+.CW ed "" [
+users would probably type
+.WC 1,$ ,
+which in practice is the same thing, but see below.]
+.P1
+.WC 0,$p
+Introduction
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.P2
+Except for the
+.CW w
+command described below,
+.I all
+commands,
+including
+.CW p ,
+set dot to the text they touch.
+Thus,
+.CW a
+and
+.CW i
+set dot to the new text,
+.CW p
+to the text printed, and so on.
+Similarly, all commands
+(except
+.CW w )
+by default operate on the current
+text [unlike
+.CW ed ,
+for which some commands (such as
+.CW g )
+default to the entire file].
+.PP
+Things are not going to get very interesting until we can
+set dot arbitrarily.
+This is done by
+.I addresses ,
+which specify a piece of the file.
+The address
+.CW 1 ,
+for example, sets dot to the first line of the file.
+.P1
+.WC 1p
+Introduction
+.WC c
+.WC Preamble
+.WC .
+.P2
+The
+.CW c
+command didn't need to specify dot; the
+.CW p
+left it on line one.
+It's therefore easy to delete the first line utterly;
+the last command left dot set to line one:
+.P1
+.WC d
+.WC 1p
+This manual is organized in a rather haphazard manner. The first
+.P2
+(Line numbers change
+to reflect changes to the file.)
+.PP
+The address \f(CW/\f2text\f(CW/\f1
+sets dot to the first appearance of
+.I text ,
+after dot.
+.CW ed "" [
+matches the first line containing
+.I text .]
+If
+.I text
+is not found, the search restarts at the beginning of the file
+and continues until dot.
+.P1
+.WC /Emacs/p
+Emacs
+.P2
+It's difficult to indicate typographically, but in this example no newline app…
+after
+.CW Emacs :
+the text to be printed is the string
+.CW Emacs ', `
+exactly.
+(The final
+.CW p
+may be left off \(em it is the default command.
+When downloaded, however, the default is instead to select the text,
+to highlight it,
+and to make it visible by moving the window on the file if necessary.
+Thus,
+.CW /Emacs/
+indicates on the display the next occurrence of the text.)
+.PP
+Imagine we wanted to change the word
+.CW haphazard
+to
+.CW thoughtless .
+Obviously, what's needed is another
+.CW c
+command, but the method used so far to insert text includes a newline.
+The syntax for including text without newlines is to surround the
+text with slashes (which is the same as the syntax for
+text searches, but what is going on should be clear from context).
+The text must appear immediately after the
+.CW c
+(or
+.CW a
+or
+.CW i ).
+Given this, it is easy to make the required change:
+.P1
+.WC /haphazard/c/thoughtless/
+1p
+This manual is organized in a rather thoughtless manner. The first
+.P2
+[Changes can always be done with a
+.CW c
+command, even if the text is smaller than a line].
+You'll find that this way of providing text to commands is much
+more common than is the multiple-lines syntax.
+If you want to include a slash
+.CW /
+in the text, just precede it with a backslash
+.CW \e ,
+and use a backslash to protect a backslash itself.
+.P1
+.WC /Emacs/c/Emacs\e\e360/
+.WC 4p
+general introduction to the commands in Emacs\e360 and to try to show
+.P2
+We could also make this particular change by
+.P1
+.WC /Emacs/a/\e\e360/
+.P2
+.PP
+This is as good a place as any to introduce the
+.CW u
+command, which undoes the last command.
+A second
+.CW u
+will undo the penultimate command, and so on.
+.P1
+.WC u
+.WC 4p
+general introduction to the commands in Emacs and to try to show
+.WC u
+.WC 3p
+This manual is organized in a rather haphazard manner. The first
+.P2
+Undoing can only back up; there is no way to undo a previous
+.CW u .
+.SH
+Addresses
+.PP
+We've seen the simplest forms of addresses, but there is more
+to learn before we can get too much further.
+An address selects a region in the file \(em a substring \(em
+and therefore must define the beginning and the end of a region.
+Thus, the address
+.CW 13
+selects from the beginning of line thirteen to the end of line thirteen, and
+.CW /Emacs/
+selects from the beginning of the word
+.CW Emacs ' `
+to the end.
+.PP
+Addresses may be combined with a comma:
+.P1
+13,15
+.P2
+selects lines thirteen through fifteen. The definition of the comma
+operator is to select from the beginning of the left hand address (the
+beginning of line 13) to the end of the right hand address (the
+end of line 15).
+.PP
+A few special simple addresses come in handy:
+.CW .
+(a period) represents dot, the current text,
+.CW 0
+(line zero) selects the null string at the beginning of the file, and
+.CW $
+selects the null string at the end of the file
+[not the last line of the file].
+Therefore,
+.P1
+0,13
+.P2
+selects from the beginning of the file to the end of line thirteen,
+.P1
+\&.,$
+.P2
+selects from the beginning of the current text to the end of the file, and
+.P1
+0,$
+.P2
+selects the whole file [that is, a single string containing the whole file,
+not a list of all the lines in the file].
+.PP
+These are all
+.I absolute
+addresses: they refer to specific places in the file.
+.CW sam
+also has relative addresses, which depend
+on the value of dot,
+and in fact we have already seen one form:
+.CW /Emacs/
+finds the first occurrence of
+.CW Emacs
+searching forwards from dot.
+Which occurrence of
+.CW Emacs
+it finds depends on the value of dot.
+What if you wanted the first occurrence
+.CW before
+dot? Just precede the pattern with a minus sign, which reverses the direction
+of the search:
+.P1
+-/Emacs/
+.P2
+In fact, the complete syntax for forward searching is
+.P1
++/Emacs/
+.P2
+but the plus sign is the default, and in practice is rarely used.
+Here is an example that includes it for clarity:
+.P1
+0+/Emacs/
+.P2
+selects the first occurrence of
+.CW Emacs
+in the file; read it as ``go to line 0, then search forwards for
+.CW Emacs .''
+Since the
+.CW +
+is optional, this can be written
+.CW 0/Emacs/ .
+Similarly,
+.P1
+$-/Emacs/
+.P2
+finds the last occurrence in the file, so
+.P1
+0/Emacs/,$-/Emacs/
+.P2
+selects the text from the first to last
+.CW Emacs ,
+inclusive.
+Slightly more interesting:
+.P1
+/Emacs/+/Emacs/
+.P2
+(there is an implicit
+.CW .+
+at the beginning) selects the second
+.CW Emacs
+following dot.
+.PP
+Line numbers may also be relative.
+.P1
+-2
+.P2
+selects the second previous line, and
+.P1
++5
+.P2
+selects the fifth following line (here the plus sign is obligatory).
+.PP
+Since addresses may select (and dot may be) more than one line,
+we need a definition of `previous' and `following:'
+`previous' means
+.I
+before the beginning
+.R
+of dot, and `following'
+means
+.I
+after the end
+.R
+of dot.
+For example, if the file contains \fBA\fIAA\fBA\f1,
+with dot set to the middle two
+.CW A 's
+(the slanting characters),
+.CW -/A/
+sets dot to the first
+.CW A ,
+and
+.CW +/A/
+sets dot to the last
+.CW A .
+Except under odd circumstances (such as when the only occurrence of the
+text in the file is already the current text), the text selected by a
+search will be disjoint from dot.
+.PP
+To select the
+.CW "troff -ms
+paragraph containing dot, however long it is, use
+.P1
+-/.PP/,/.PP/-1
+.P2
+which will include the
+.CW .PP
+that begins the paragraph, and exclude the one that ends it.
+.PP
+When typing relative line number addresses, the default number is
+.CW 1 ,
+so the above could be written slightly more simply:
+.P1
+-/.PP/,/.PP/-
+.P2
+.PP
+What does the address
+.CW +1-1
+or the equivalent
+.CW +-
+mean? It looks like it does nothing, but recall that dot need not be a
+complete line of text.
+.CW +1
+selects the line after the end of the current text, and
+.CW -1
+selects the line before the beginning. Therefore
+.CW +1-1
+selects the line before the line after the end of dot, that is,
+the complete line containing the end of dot.
+We can use this construction to expand a selection to include a complete line,
+say the first line in the file containing
+.CW Emacs :
+.P1
+.WC 0/Emacs/+-p
+general introduction to the commands in Emacs and to try to show
+.P2
+The address
+.CW +-
+is an idiom.
+.SH
+Loops
+.PP
+Above, we changed one occurrence of
+.CW Emacs
+to
+.CW Emacs\e360 ,
+but if the name of the editor is really changing, it would be useful
+to change
+.I all
+instances of the name in a single command.
+.CW sam
+provides a command,
+.CW x
+(extract), for just that job.
+The syntax is
+\f(CWx/\f2pattern\f(CW/\f2command\f1.
+For each occurrence of the pattern in the selected text,
+.CW x
+sets dot to the occurrence and runs command.
+For example, to change
+.CW Emacs
+to
+.CW vi,
+.P1
+.WC 0,$x/Emacs/c/vi/
+.WC 0,$p
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in vi and to try to show
+the method in the madness that is the vi command structure.
+.P2
+This
+works by subdividing the current text
+.CW 0,$ "" (
+\(em the whole file) into appearances of its textual argument
+.CW Emacs ), (
+and then running the command that follows
+.CW c/vi/ ) (
+with dot set to the text.
+We can read this example as, ``find all occurrences of
+.CW Emacs
+in the file, and for each one,
+set the current text to the occurrence and run the command
+.CW c/vi/ ,
+which will replace the current text by
+.CW vi. ''
+[This command is somewhat similar to
+.CW ed 's
+.CW g
+command. The differences will develop below, but note that the
+default address, as always, is dot rather than the whole file.]
+.PP
+A single
+.CW u
+command is sufficient to undo an
+.CW x
+command, regardless of how many individual changes the
+.CW x
+makes.
+.P1
+.WC u
+.WC 0,$p
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.P2
+.PP
+Of course,
+.CW c
+is not the only command
+.CW x
+can run. An
+.CW a
+command can be used to put proprietary markings on
+.CW Emacs :
+.P1
+.WC 0,$x/Emacs/a/{TM}/
+.WC /Emacs/+-p
+general introduction to the commands in Emacs{TM} and to try to show
+.P2
+[There is no way to see the changes as they happen, as in
+.CW ed 's
+.CW g/Emacs/s//&{TM}/p ;
+see the section on Multiple Changes, below.]
+.PP
+The
+.CW p
+command is also useful when driven by an
+.CW x ,
+but be careful that you say what you mean;
+.P1
+.WC 0,$x/Emacs/p
+EmacsEmacs
+.P2
+since
+.CW x
+sets dot to the text in the slashes, printing only that text
+is not going to be very
+informative. But the command that
+.CW x
+runs can contain addresses. For example, if we want to print all
+lines containing
+.CW Emacs ,
+just use
+.CW +- :
+.P1
+.WC 0,$x/Emacs/+-p
+general introduction to the commands in Emacs{TM} and to try to show
+the method in the madness that is the Emacs{TM} command structure.
+.P2
+Finally, let's restore the state of the file with another
+.CW x
+command, and make use of a handy shorthand:
+a comma in an address has its left side default to
+.CW 0 ,
+and its right side default to
+.CW $ ,
+so the easy-to-type address
+.CW ,
+refers to the whole file:
+.P1
+.WC ",x/Emacs/ /{TM}/d
+.WC ,p
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.P2
+Notice what this
+.CW x
+does: for each occurrence of Emacs,
+find the
+.CW {TM}
+that follows, and delete it.
+.PP
+The `text'
+.CW sam
+accepts
+for searches in addresses and in
+.CW x
+commands is not simple text, but rather
+.I regular\ expressions.
+Unix has several distinct interpretations of regular expressions.
+The form used by
+.CW sam
+is that of
+.CW regexp (6),
+including parentheses
+.CW ()
+for grouping and an `or' operator
+.CW |
+for matching strings in parallel.
+.CW sam
+also matches the character sequence
+.CW \en
+with a newline character.
+Replacement text, such as used in the
+.CW a
+and
+.CW c
+commands, is still plain text, but the sequence
+.CW \en
+represents newline in that context, too.
+.PP
+Here is an example. Say we wanted to double space the document, that is,
+turn every newline into two newlines.
+The following all do the job:
+.P1
+.WC ",x/\en/ a/\en/
+.WC ",x/\en/ c/\en\en/
+.WC ",x/$/ a/\en/
+.WC ",x/^/ i/\en/
+.P2
+The last example is slightly different, because it puts a newline
+.I before
+each line; the other examples place it after.
+The first two examples manipulate newlines directly
+[something outside
+.CW ed 's
+ken]; the last two
+use regular expressions:
+.CW $
+is the empty string at the end of a line, while
+.CW ^
+is the empty string at the beginning.
+.PP
+These solutions all have a possible drawback: if there is already a blank line
+(that is, two consecutive newlines), they make it much larger (four
+consecutive newlines).
+A better method is to extend every group of newlines by one:
+.P1
+.WC ",x/\en+/ a/\en/
+.P2
+The regular expression operator
+.CW +
+means `one or more;'
+.CW \en+
+is identical to
+.CW \en\en* .
+Thus, this example
+takes every sequence of newlines and adds another
+to the end.
+.PP
+A more common example is indenting a block of text by a tab stop.
+The following all work,
+although the first is arguably the cleanest (the blank text in slashes is a ta…
+.P1
+.WC ",x/^/a/ /
+.WC ",x/^/c/ /
+.WC ",x/.*\en/i/ /
+.P2
+The last example uses the pattern (idiom, really)
+.CW .*\en
+to match lines:
+.CW .*
+matches the longest possible string of non-newline characters.
+Taking initial tabs away is just as easy:
+.P1
+.WC ",x/^ /d
+.P2
+In these examples I have specified an address (the whole file), but
+in practice commands like these are more likely to be run without
+an address, using the value of dot set by selecting text with the mouse.
+.SH
+Conditionals
+.PP
+The
+.CW x
+command is a looping construct:
+for each match of a regular expression,
+it extracts (sets dot to) the match and runs a command.
+.CW sam
+also has a conditional,
+.CW g :
+\f(CWg/\f2pattern\f(CW/\f2command\f1
+runs the command if dot contains a match of the pattern
+.I
+without changing the value of dot.
+.R
+The inverse,
+.CW v ,
+runs the command if dot does
+.I not
+contain a match of the pattern.
+(The letters
+.CW g
+and
+.CW v
+are historical and have no mnemonic significance. You might
+think of
+.CW g
+as `guard.')
+.CW ed "" [
+users should read the above definitions very carefully; the
+.CW g
+command in
+.CW sam
+is fundamentally different from that in
+.CW ed .]
+Here is an example of the difference between
+.CW x
+and
+.CW g:
+.P1
+,x/Emacs/c/vi/
+.P2
+changes each occurrence of the word
+.CW Emacs
+in the file to the word
+.CW vi ,
+but
+.P1
+,g/Emacs/c/vi/
+.P2
+changes the
+.I "whole file
+to
+.CW vi
+if there is the word
+.CW Emacs
+anywhere in the file.
+.PP
+Neither of these commands is particularly interesting in isolation,
+but they are valuable when combined with
+.CW x
+and with themselves.
+.SH
+Composition
+.PP
+One way to think about the
+.CW x
+command is that, given a selection (a value of dot)
+it iterates through interesting subselections (values of dot within).
+In other words, it takes a piece of text and cuts it into smaller pieces.
+But the text that it cuts up may already be a piece cut by a previous
+.CW x
+command or selected by a
+.CW g .
+.CW sam 's
+most interesting property is the ability to define a sequence of commands
+to perform a particular task.\(dg
+.FS
+\(dg
+The obvious analogy with shell pipelines is only partially valid,
+because the individual
+.CW sam
+commands are all working on the same text; it is only how the text is
+sliced up that is changing.
+.FE
+A simple example is to change all occurrences of
+.CW Emacs
+to
+.CW emacs ;
+certainly the command
+.P1
+.WC ",x/Emacs/ c/emacs/
+.P2
+will work, but we can use an
+.CW x
+command to save retyping most of the word
+.CW Emacs :
+.P1
+.WC ",x/Emacs/ x/E/ c/e/
+.P2
+(Blanks can be used
+to separate commands on a line to make them easier to read.)
+What this command does is find all occurrences of
+.CW Emacs
+.CW ,x/Emacs/ ), (
+and then
+.I
+with dot set to that text,
+.R
+find all occurrences of the letter
+.CW E
+.CW x/E/ ), (
+and then
+.I
+with dot set to that text,
+.R
+run the command
+.CW c/e/
+to change the character to lower case.
+Note that the address for the command \(em the whole file, specified by a comma
+\(em is only given to the leftmost
+piece of the command; the rest of the pieces have dot set for them by
+the execution of the pieces to their left.
+.PP
+As another simple example, consider a problem
+solved above: printing all lines in the file containing the word
+.CW Emacs:
+.P1
+.WC ",x/.*\en/ g/Emacs/p
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.P2
+This command says to break the file into lines
+.CW ,x/.*\en/ ), (
+and for each line that contains the string
+.CW Emacs
+.CW g/Emacs/ ), (
+run the command
+.CW p
+with dot set to the line (not the match of
+.CW Emacs ),
+which prints the line.
+To save typing, because
+.CW .*\en
+is a common pattern in
+.CW x
+commands,
+if the
+.CW x
+is followed immediately by a space, the pattern
+.CW .*\en
+is assumed.
+Therefore, the above could be written more succinctly:
+.P1
+.WC ",x g/Emacs/p
+.P2
+The solution we used before was
+.P1
+.WC ,x/Emacs/+-p
+.P2
+which runs the command
+.CW +-p
+with dot set to each match of
+.CW Emacs
+in the file (recall that the idiom
+.CW +-p
+prints the line containing the end of dot).
+.PP
+The two commands usually produce the same result
+(the
+.CW +-p
+form will print a line twice if it contains
+.CW Emacs
+twice). Which is better?
+.CW ,x/Emacs/+-p
+is easier to type and will be much faster if the file is large and
+there are few occurrences of the string, but it is really an odd special case.
+.CW ",x/.*\en/ g/Emacs/p
+is slower \(em it breaks each line out separately, then examines
+it for a match \(em but is conceptually cleaner, and generalizes more easily.
+For example, consider the following piece of the Emacs manual:
+.P1
+command name="append-to-file", key="[unbound]"
+Takes the contents of the current buffer and appends it to the
+named file. If the file doesn't exist, it will be created.
+
+command name="apropos", key="ESC-?"
+Prompts for a keyword and then prints a list of those commands
+whose short description contains that keyword. For example,
+if you forget which commands deal with windows, just type
+"@b[ESC-?]@t[window]@b[ESC]".
+
+\&\f2and so on\f(CW
+.P2
+This text consists of groups of non-empty lines, with a simple format
+for the text within each group.
+Imagine that we wanted to find the description of the `apropos'
+command.
+The problem is to break the file into individual descriptions,
+and then to find the description of `apropos' and to print it.
+The solution is straightforward:
+.P1
+.WC ,x/(.+\en)+/\ g/command\ name="apropos"/p
+command name="apropos", key="ESC-?"
+Prompts for a keyword and then prints a list of those commands
+whose short description contains that keyword. For example,
+if you forget which commands deal with windows, just type
+"@b[ESC-?]@t[window]@b[ESC]".
+.P2
+The regular expression
+.CW (.+\en)+
+matches one or more lines with one or more characters each, that is,
+the text between blank lines, so
+.CW ,x/(.+\en)+/
+extracts each description; then
+.CW g/command\ name="apropos"/
+selects the description for `apropos' and
+.CW p
+prints it.
+.PP
+Imagine that we had a C program containing the variable
+.CW n ,
+but we wanted to change it to
+.CW num .
+This command is a first cut:
+.P1
+.WC ",x/n/ c/num/
+.P2
+but is obviously flawed: it will change all
+.CW n 's
+in the file, not just the
+.I identifier
+.CW n .
+A better solution is to use an
+.CW x
+command to extract the identifiers, and then use
+.CW g
+to find the
+.CW n 's:
+.P1
+.WC ",x/[a-zA-Z_][a-zA-Z_0-9]*/ g/n/ v/../ c/num/
+.P2
+It looks awful, but it's fairly easy to understand when read
+left to right.
+A C identifier is an alphabetic or underscore followed by zero or more
+alphanumerics or underscores, that is, matches of the regular expression
+.CW [a-zA-Z_][a-zA-Z_0-9]* .
+The
+.CW g
+command selects those identifiers containing
+.CW n ,
+and the
+.CW v
+is a trick: it rejects those identifiers containing more than one
+character. Hence the
+.CW c/num/
+applies only to free-standing
+.CW n 's.
+.PP
+There is still a problem here:
+we don't want to change
+.CW n 's
+that are part of the character constant
+.CW \en .
+There is a command
+.CW y ,
+complementary to
+.CW x ,
+that is just what we need:
+\f(CWy/\f2pattern\f(CW/\f2command\f1
+runs the command on the pieces of text
+.I between
+matches of the pattern;
+if
+.CW x
+selects,
+.CW y
+rejects.
+Here is the final command:
+.P1
+.WC ",y/\e\en/ x/[a-zA-Z_][a-zA-Z_0-9]*/ g/n/ v/../ c/num/
+.P2
+The
+.CW y/\e\en/
+(with backslash doubled to make it a literal character)
+removes the two-character sequence
+.CW \en
+from consideration, so the rest of the command will not touch it.
+There is more we could do here; for example, another
+.CW y
+could be prefixed to protect comments in the code.
+I won't elaborate the example any further, but you should have
+an idea of the way in which the looping and conditional commands
+in
+.CW sam
+may be composed to do interesting things.
+.SH
+Grouping
+.PP
+There is another way to arrange commands.
+By enclosing them in brace brackets
+.CW {} ,
+commands may be applied in parallel.
+This example uses the
+.CW =
+command, which reports the line and character numbers of dot,
+together with
+.CW p ,
+to report on appearances of
+.CW Emacs
+in our original file:
+.P1
+.WC ,p
+This manual is organized in a rather haphazard manner. The first
+several sections were written hastily in an attempt to provide a
+general introduction to the commands in Emacs and to try to show
+the method in the madness that is the Emacs command structure.
+.ft I
+,x/Emacs/{
+ =
+ +-p
+}
+.ft
+3; #171,#176
+general introduction to the commands in Emacs and to try to show
+4; #234,#239
+the method in the madness that is the Emacs command structure.
+.P2
+(The number before the semicolon is the line number;
+the numbers beginning with
+.CW #
+are character numbers.)
+As a more interesting example, consider changing all occurrences of
+.CW Emacs
+to
+.CW vi
+and vice versa. We can type
+.P1
+.ft I
+,x/Emacs|vi/{
+ g/Emacs/ c/vi/
+ g/vi/ c/Emacs/
+}
+.ft
+.P2
+or even
+.P1
+.ft I
+,x/[a-zA-Z]+/{
+ g/Emacs/ v/....../ c/vi/
+ g/vi/ v/.../ c/Emacs/
+}
+.ft
+.P2
+to make sure we don't change strings embedded in words.
+.SH
+Multiple Changes
+.PP
+You might wonder why, once
+.CW Emacs
+has been changed to
+.CW vi
+in the above example,
+the second command in the braces doesn't put it back again.
+The reason is that the commands are run in parallel:
+within any top-level
+.CW sam
+command, all changes to the file refer to the state of the file
+before any of the changes in that command are made.
+After all the changes have been determined, they are all applied
+simultaneously.
+.PP
+This means, as mentioned, that commands within a compound
+command see the state of the file before any of the changes apply.
+This method of evaluation makes some things easier (such as the exchange of
+.CW Emacs
+and
+.CW vi ),
+and some things harder.
+For instance, it is impossible to use a
+.CW p
+command to print the changes as they happen,
+because they haven't happened when the
+.CW p
+is executed.
+An indirect ramification is that changes must occur in forward
+order through the file,
+and must not overlap.
+.SH
+Unix
+.PP
+.CW sam
+has a few commands to connect to Unix processes.
+The simplest is
+.CW ! ,
+which runs the command with input and output connected to the terminal.
+.P1
+.WC !date
+Wed May 28 23:25:21 EDT 1986
+!
+.P2
+(When downloaded, the input is connected to
+.CW /dev/null
+and only the first few lines of output are printed;
+any overflow is stored in
+.CW $HOME/sam.err .)
+The final
+.CW !
+is a prompt to indicate when the command completes.
+.PP
+Slightly more interesting is
+.CW > ,
+which provides the current text as standard input to the Unix command:
+.P1
+.WC "1,2 >wc
+ 2 22 131
+!
+.P2
+The complement of
+.CW >
+is, naturally,
+.CW < :
+it replaces the current text with the standard output of the Unix command:
+.P1
+.WC "1 <date
+!
+.WC 1p
+Wed May 28 23:26:44 EDT 1986
+.P2
+The last command is
+.CW | ,
+which is a combination of
+.CW <
+and
+.CW > :
+the current text is provided as standard input to the Unix command,
+and the Unix command's standard output is collected and used to
+replace the original text.
+For example,
+.P1
+.WC ",| sort
+.P2
+runs
+.CW sort (1)
+on the file, sorting the lines of the text lexicographically.
+Note that
+.CW < ,
+.CW >
+and
+.CW |
+are
+.CW sam
+commands, not Unix shell operators.
+.PP
+The next example converts all appearances of
+.CW Emacs
+to upper case using
+.CW tr (1):
+.P1
+.WC ",x/Emacs/ | tr a-z A-Z
+.P2
+.CW tr
+is run once for each occurrence of
+.CW Emacs .
+Of course, you could do this example more efficiently with a simple
+.CW c
+command, but here's a trickier one:
+given a Unix mail box as input,
+convert all the
+.CW Subject
+headers to distinct fortunes:
+.P1
+.WC ",x/^Subject:.*\en/ x/[^:]*\en/ < /usr/games/fortune
+.P2
+(The regular expression
+.CW [^:]
+refers to any character
+.I except
+.CW :
+and newline; the negation operator
+.CW ^
+excludes newline from the list of characters.)
+Again,
+.CW /usr/games/fortune
+is run once for each
+.CW Subject
+line, so each
+.CW Subject
+line is changed to a different fortune.
+.SH
+A few other text commands
+.PP
+For completeness, I should mention three other commands that
+manipulate text. The
+.CW m
+command moves the current text to after the text specified by the
+(obligatory) address after the command.
+Thus
+.P1
+.WC "/Emacs/+- m 0
+.P2
+moves the next line containing
+.CW Emacs
+to the beginning of the file.
+Similarly,
+.CW t
+(another historic character) copies the text:
+.P1
+.WC "/Emacs/+- t 0
+.P2
+would make, at the beginning of the file, a copy of the next line
+containing
+.CW Emacs .
+.PP
+The third command is more interesting: it makes substitutions.
+Its syntax is
+\f(CWs/\f2pattern\f(CW/\f2replacement\f(CW/\f1.
+Within the current text, it finds the first occurrence of
+the pattern and replaces it by the replacement text,
+leaving dot set to the entire address of the substitution.
+.P1
+.WC 1p
+This manual is organized in a rather haphazard manner. The first
+.WC s/haphazard/thoughtless/
+.WC p
+This manual is organized in a rather thoughtless manner. The first
+.P2
+Occurrences of the character
+.CW &
+in the replacement text stand for the text matching the pattern.
+.P1
+.WC s/T/"&&&&"/
+.WC p
+"TTTT"his manual is organized in a rather thoughtless manner. The first
+.P2
+There are two variants. The first is that a number may be specified
+after the
+.CW s ,
+to indicate which occurrence of the pattern to substitute; the default
+is the first.
+.P1
+.WC s2/is/was/
+.WC p
+"TTTT"his manual was organized in a rather thoughtless manner. The first
+.P2
+The second is that suffixing a
+.CW g
+(global) causes replacement of all occurrences, not just the first.
+.P1
+.WC s/[a-zA-Z]/x/g
+.WC p
+"xxxx"xxx xxxxxx xxx xxxxxxxxx xx x xxxxxx xxxxxxxxxxx xxxxxxx xxx xxxxx
+.P2
+Notice that in all these examples
+dot is left
+set to the entire line.
+.PP
+[The substitute command is vital to
+.CW ed,
+because it is the only way to make changes within a line.
+It is less valuable in
+.CW sam ,
+in which the concept of a line is much less important.
+For example, many
+.CW ed
+substitution idioms are handled well by
+.CW sam 's
+basic commands. Consider the commands
+.P1
+s/good/bad/
+s/good//
+s/good/& bye/
+.P2
+which are equivalent in
+.CW sam
+to
+.P1
+/good/c/bad/
+/good/d
+/good/a/ bye/
+.P2
+and for which the context search is likely unnecessary because the desired
+text is already dot.
+Also, beware this
+.CW ed
+idiom:
+.P1
+1,$s/good/bad/
+.P2
+which changes the first
+.CW good
+on each line; the same command in
+.CW sam
+will only change the first one in the whole file.
+The correct
+.CW sam
+version is
+.P1
+,x s/good/bad/
+.P2
+but what is more likely meant is
+.P1
+,x/good/ c/bad/
+.P2
+.CW sam
+operates under different rules.]
+.SH
+Files
+.PP
+So far, we have only been working with a single file,
+but
+.CW sam
+is a multi-file editor.
+Only one file may be edited at a time, but
+it is easy to change which file is the `current' file for editing.
+To see how to do this, we need a
+.CW sam
+with a few files;
+the easiest way to do this is to start it
+with a list of Unix file names to edit.
+.P1
+$ \fIecho *.ms\f(CW
+conquest.ms death.ms emacs.ms famine.ms slaughter.ms
+$ \fIsam -d *.ms\f(CW
+ -. conquest.ms
+.P2
+(I'm sorry the Horsemen don't appear in liturgical order.)
+The line printed by
+.CW sam
+is an indication that the Unix file
+.CW conquest.ms
+has been read, and is now the current file.
+.CW sam
+does not read the Unix file until
+the associated
+.CW sam
+file becomes current.
+.PP
+The
+.CW n
+command prints the names of all the files:
+.P1
+.WC n
+ -. conquest.ms
+ - death.ms
+ - emacs.ms
+ - famine.ms
+ - slaughter.ms
+.P2
+This list is also available in the menu on mouse button 3.
+The command
+.CW f
+tells the name of just the current file:
+.P1
+.WC f
+ -. conquest.ms
+.P2
+The characters to the left of the file name encode helpful information about
+the file.
+The minus sign becomes a plus sign if the file has a window open, and an
+asterisk if more than one is open.
+The period (another meaning of dot) identifies the current file.
+The leading blank changes to an apostrophe if the file is different
+from the contents of the associated Unix file, as far as
+.CW sam
+knows.
+This becomes evident if we make a change.
+.P1
+.WC 1d
+.WC f
+\&'-. conquest.ms
+.P2
+If the file is restored by an undo command, the apostrophe disappears.
+.P1
+.WC u
+.WC f
+ -. conquest.ms
+.P2
+The file name may be changed by providing a new name with the
+.CW f
+command:
+.P1
+.CW "f pestilence.ms
+\&'-. pestilence.ms
+.P2
+.WC f
+prints the new status of the file,
+that is, it changes the name if one is provided, and prints the
+name regardless.
+A file name change may also be undone.
+.P1
+.WC u
+.WC f
+ -. conquest.ms
+.P2
+.PP
+When
+.CW sam
+is downloaded, the current file may be changed simply by selecting
+the desired file from the menu (selecting the same file subsequently
+cycles through the windows opened on the file).
+Otherwise, the
+.CW b
+command can be used to choose the desired file:\(dg
+.FS
+\(dg A bug prevents the
+.CW b
+command from working when downloaded.
+Because the menu is more convenient anyway, and
+because the method
+of choosing files from the command language is slated to change,
+the bug hasn't been fixed.
+.FE
+.P1
+.WC "b emacs.ms
+ -. emacs.ms
+.P2
+Again,
+.CW sam
+prints the name (actually, executes an implicit
+.CW f
+command) because the Unix file
+.CW emacs.ms
+is being read for the first time.
+It is an error to ask for a file
+.CW sam
+doesn't know about, but the
+.CW B
+command will prime
+.CW sam 's
+menu with a new file, and make it current.
+.P1
+.WC "b flood.pic
+?no such file `flood.pic'
+.WC "B flood.pic
+ -. flood.pic
+.WC n
+ - conquest.ms
+ - death.ms
+ - emacs.ms
+ - famine.ms
+ -. flood.pic
+ - slaughter.ms
+.P2
+Both
+.CW b
+and
+.CW B
+will accept a list of file names.
+.CW b
+simply takes the first file in the list, but
+.CW B
+loads them all.
+The list may be typed on one line \(em
+.P1
+.WC "B devil.tex satan.tex 666.tex emacs.tex
+.P2
+\(em or generated by a Unix command \(em
+.P1
+.WC "B <echo *.tex
+.P2
+The latter form requires a Unix command;
+.CW sam
+does not understand the shell file name metacharacters, so
+.CW "B *.tex
+attempts to load a single file named
+.CW *.tex .
+(The
+.CW <
+form is of course derived from
+.CW sam 's
+.CW <
+command.)
+.CW echo
+is not the only useful command to run subservient to
+.CW B ;
+for example,
+.P1
+.WC "B <grep -l Emacs *
+.P2
+will load only those files containing the string
+.CW Emacs .
+Finally, a special case: a
+.CW B
+with no arguments creates an empty, nameless file within
+.CW sam .
+.PP
+The complement of
+.CW B
+is
+.CW D :
+.P1
+.WC "D devil.tex satan.tex 666.tex emacs.tex
+.P2
+eradicates the files from
+.CW sam 's
+memory (not from the Unix machine's disc).
+.CW D
+without any file names removes the current file from
+.CW sam .
+.PP
+There are three other commands that relate the current file
+to Unix files.
+The
+.CW w
+command writes the file to disc;
+without arguments, it writes the entire file to the Unix file associated
+with the current file in
+.CW sam
+(it is the only command whose default address is not dot).
+Of course, you can specify an address to be written,
+and a different file name, with the obvious syntax:
+.P1
+.WC "1,2w /tmp/revelations
+/tmp/revelations: #44
+.P2
+.CW sam
+responds with the file name and the number of characters written to the file.
+The
+.CW write
+command on the button 3 menu is identical in function to an unadorned
+.CW w
+command.
+.PP
+The other two commands,
+.CW e
+and
+.CW r ,
+read data from Unix files.
+The
+.CW e
+command clears out the current file,
+reads the data from the named file (or uses the current file's old name if
+none is explicitly provided), and sets the file name.
+It's much like a
+.CW B
+command, but puts the information in the current file instead of a new one.
+.CW e
+without any file name is therefore an easy way to refresh
+.CW sam 's
+copy of a Unix file.
+[Unlike in
+.CW ed ,
+.CW e
+doesn't complain if the file is modified. The principle is not
+to protect against things that can be undone if wrong.]
+Since its job is to replace the whole text,
+.CW e
+never takes an address.
+.PP
+The
+.CW r
+command is like
+.CW e ,
+but it doesn't clear the file:
+the text in the Unix file replaces dot, or the specified text if an
+address is given.
+.P1
+.WC "r emacs.ms
+.P2
+has essentially the effect of
+.P1
+.WC "<cat emacs.ms
+.P2
+The commands
+.CW r
+and
+.CW w
+will set the name of the file if the current file has no name already defined;
+.CW e
+sets the name even if the file already has one.
+.PP
+There is a command, analogous to
+.CW x ,
+that iterates over files instead of pieces of text:
+.CW X
+(capital
+.CW x ).
+The syntax is easy; it's just like that of
+.CW x
+\(em \f(CWX/\f2pattern\f(CW/\f2command\f1.
+(The complementary command is
+.CW Y ,
+analogous to
+.CW y .)
+The effect is to run the command in each file whose menu entry
+(that is, whose line printed by an
+.CW f
+command) matches the pattern.
+For example, since an apostrophe identifies modified files,
+.P1
+.WC "X/'/ w
+.P2
+writes the changed files out to disc.
+Here is a longer example: find all uses of a particular variable
+in the C source files:
+.P1
+.WC "X/\e.c$/ ,x/variable/+-p
+.P2
+We can use an
+.CW f
+command to identify which file the variable appears in:
+.P1
+.ft I
+X/\e.c$/ ,g/variable/ {
+ f
+ ,x/variable/+-{
+ =
+ p
+ }
+}
+.ft
+.P2
+Here, the
+.CW g
+command guarantees that only the names of files containing the variable
+will be printed (but beware that
+.CW sam
+may confuse matters by printing the names of files it reads in during
+the command).
+The
+.CW =
+command shows where in the file the variable appears, and the
+.CW p
+command prints the line.
+.PP
+The
+.CW D
+command is handy as the target of an
+.CW X .
+This example deletes from the menu all C files that do not contain
+a particular variable:
+.P1
+.WC "X/\e.c$/ ,v/variable/ D
+.P2
+If no pattern is provided for the
+.CW X ,
+the command (which defaults to
+.CW f )
+is run in all files, so
+.P1
+.WC "X D
+.P2
+cleans
+.CW sam
+up for a fresh start.
+.PP
+But rather than working any further, let's stop now:
+.P1
+.WC q
+$
+.P2
+.fi
+.PP
+Some of the file manipulating commands can be undone:
+undoing a
+.CW f ,
+.CW e ,
+or
+.CW r
+restores the previous state of the file,
+but
+.CW w ,
+.CW B
+and
+.CW D
+are irrevocable.
+And, of course, so is
+.CW q .
diff --git a/doc/se.ps b/doc/se.ps
@@ -0,0 +1,1487 @@
+%!PS
+%%Version: 3.3.2
+%%DocumentFonts: (atend)
+%%Pages: (atend)
+%%EndComments
+%
+% Version 3.3.2 prologue for troff files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/linewidth .3 def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/resolution 720 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/R /Times-Roman def
+/I /Times-Italic def
+/B /Times-Bold def
+/BI /Times-BoldItalic def
+/H /Helvetica def
+/HI /Helvetica-Oblique def
+/HB /Helvetica-Bold def
+/HX /Helvetica-BoldOblique def
+/CW /Courier def
+/CO /Courier def
+/CI /Courier-Oblique def
+/CB /Courier-Bold def
+/CX /Courier-BoldOblique def
+/PA /Palatino-Roman def
+/PI /Palatino-Italic def
+/PB /Palatino-Bold def
+/PX /Palatino-BoldItalic def
+/Hr /Helvetica-Narrow def
+/Hi /Helvetica-Narrow-Oblique def
+/Hb /Helvetica-Narrow-Bold def
+/Hx /Helvetica-Narrow-BoldOblique def
+/KR /Bookman-Light def
+/KI /Bookman-LightItalic def
+/KB /Bookman-Demi def
+/KX /Bookman-DemiItalic def
+/AR /AvantGarde-Book def
+/AI /AvantGarde-BookOblique def
+/AB /AvantGarde-Demi def
+/AX /AvantGarde-DemiOblique def
+/NR /NewCenturySchlbk-Roman def
+/NI /NewCenturySchlbk-Italic def
+/NB /NewCenturySchlbk-Bold def
+/NX /NewCenturySchlbk-BoldItalic def
+/ZD /ZapfDingbats def
+/ZI /ZapfChancery-MediumItalic def
+/S /S def
+/S1 /S1 def
+/GR /Symbol def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/show {show} bind def % so later references don't bind
+/widthshow {widthshow} bind def
+/stringwidth {stringwidth} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+ /scaling 72 resolution div def
+ linewidth setlinewidth
+ 1 setlinecap
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ margin 2 div dup neg translate
+ magnification dup aspectratio mul scale
+ scaling scaling scale
+
+ addmetrics
+ 0 0 moveto
+} def
+
+/pagedimensions {
+ useclippath userdict /gotpagebbox known not and {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox}…
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/addmetrics {
+ /Symbol /S null Sdefs cf
+ /Times-Roman /S1 StandardEncoding dup length array copy S1defs cf
+} def
+
+/pagesetup {
+ /page exch def
+ currentdict /pagedict known currentdict page known and {
+ page load pagedict exch get cvx exec
+ } if
+} def
+
+/decodingdefs [
+ {counttomark 2 idiv {y moveto show} repeat}
+ {neg /y exch def counttomark 2 idiv {y moveto show} repeat}
+ {neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll width…
+ {neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat}
+ {counttomark 2 idiv {y moveto show} repeat}
+ {neg setfunnytext}
+] def
+
+/setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def
+
+/w {neg moveto show} bind def
+/m {neg dup /y exch def moveto} bind def
+/done {/lastpage where {pop lastpage} if} def
+
+/f {
+ dup /font exch def findfont exch
+ dup /ptsize exch def scaling div dup /size exch def scalefont setfont
+ linewidth ptsize mul scaling 10 mul div setlinewidth
+ /spacewidth ( ) stringwidth pop def
+} bind def
+
+/changefont {
+ /fontheight exch def
+ /fontslant exch def
+ currentfont [
+ 1 0
+ fontheight ptsize div fontslant sin mul fontslant cos div
+ fontheight ptsize div
+ 0 0
+ ] makefont setfont
+} bind def
+
+/sf {f} bind def
+
+/cf {
+ dup length 2 idiv
+ /entries exch def
+ /chtab exch def
+ /newencoding exch def
+ /newfont exch def
+
+ findfont dup length 1 add dict
+ /newdict exch def
+ {1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall
+
+ newencoding type /arraytype eq {newdict /Encoding newencoding put} if
+
+ newdict /Metrics entries dict put
+ newdict /Metrics get
+ begin
+ chtab aload pop
+ 1 1 entries {pop def} for
+ newfont newdict definefont pop
+ end
+} bind def
+
+%
+% A few arrays used to adjust reference points and character widths in some
+% of the printer resident fonts. If square roots are too high try changing
+% the lines describing /radical and /radicalex to,
+%
+% /radical [0 -75 550 0]
+% /radicalex [-50 -75 500 0]
+%
+% Move braceleftbt a bit - default PostScript character is off a bit.
+%
+
+/Sdefs [
+ /bracketlefttp [201 500]
+ /bracketleftbt [201 500]
+ /bracketrighttp [-81 380]
+ /bracketrightbt [-83 380]
+ /braceleftbt [203 490]
+ /bracketrightex [220 -125 500 0]
+ /radical [0 0 550 0]
+ /radicalex [-50 0 500 0]
+ /parenleftex [-20 -170 0 0]
+ /integral [100 -50 500 0]
+ /infinity [10 -75 730 0]
+] def
+
+/S1defs [
+ /underscore [0 80 500 0]
+ /endash [7 90 650 0]
+] def
+%
+% Tries to round clipping path dimensions, as stored in array pagebbox, so they
+% match one of the known sizes in the papersizes array. Lower left coordinates
+% are always set to 0.
+%
+
+/roundpagebbox {
+ 7 dict begin
+ /papersizes [8.5 inch 11 inch 14 inch 17 inch] def
+
+ /mappapersize {
+ /val exch def
+ /slop .5 inch def
+ /diff slop def
+ /j 0 def
+ 0 1 papersizes length 1 sub {
+ /i exch def
+ papersizes i get val sub abs
+ dup diff le {/diff exch def /j i def} {pop} ifelse
+ } for
+ diff slop lt {papersizes j get} {val} ifelse
+ } def
+
+ pagebbox 0 0 put
+ pagebbox 1 0 put
+ pagebbox dup 2 get mappapersize 2 exch put
+ pagebbox dup 3 get mappapersize 3 exch put
+ end
+} bind def
+
+%%EndProlog
+%%BeginSetup
+mark
+/linewidth 0.5 def
+/#copies 1 store
+/landscape false def
+/resolution 720 def
+%
+% Encoding vector and redefinition of findfont for the ISO Latin1 standard.
+% The 18 characters missing from ROM based fonts on older printers are noted
+% below.
+%
+
+/ISOLatin1Encoding [
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /space
+ /exclam
+ /quotedbl
+ /numbersign
+ /dollar
+ /percent
+ /ampersand
+ /quoteright
+ /parenleft
+ /parenright
+ /asterisk
+ /plus
+ /comma
+ /minus
+ /period
+ /slash
+ /zero
+ /one
+ /two
+ /three
+ /four
+ /five
+ /six
+ /seven
+ /eight
+ /nine
+ /colon
+ /semicolon
+ /less
+ /equal
+ /greater
+ /question
+ /at
+ /A
+ /B
+ /C
+ /D
+ /E
+ /F
+ /G
+ /H
+ /I
+ /J
+ /K
+ /L
+ /M
+ /N
+ /O
+ /P
+ /Q
+ /R
+ /S
+ /T
+ /U
+ /V
+ /W
+ /X
+ /Y
+ /Z
+ /bracketleft
+ /backslash
+ /bracketright
+ /asciicircum
+ /underscore
+ /quoteleft
+ /a
+ /b
+ /c
+ /d
+ /e
+ /f
+ /g
+ /h
+ /i
+ /j
+ /k
+ /l
+ /m
+ /n
+ /o
+ /p
+ /q
+ /r
+ /s
+ /t
+ /u
+ /v
+ /w
+ /x
+ /y
+ /z
+ /braceleft
+ /bar
+ /braceright
+ /asciitilde
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /dotlessi
+ /grave
+ /acute
+ /circumflex
+ /tilde
+ /macron
+ /breve
+ /dotaccent
+ /dieresis
+ /.notdef
+ /ring
+ /cedilla
+ /.notdef
+ /hungarumlaut
+ /ogonek
+ /caron
+ /space
+ /exclamdown
+ /cent
+ /sterling
+ /currency
+ /yen
+ /brokenbar % missing
+ /section
+ /dieresis
+ /copyright
+ /ordfeminine
+ /guillemotleft
+ /logicalnot
+ /hyphen
+ /registered
+ /macron
+ /degree % missing
+ /plusminus % missing
+ /twosuperior % missing
+ /threesuperior % missing
+ /acute
+ /mu % missing
+ /paragraph
+ /periodcentered
+ /cedilla
+ /onesuperior % missing
+ /ordmasculine
+ /guillemotright
+ /onequarter % missing
+ /onehalf % missing
+ /threequarters % missing
+ /questiondown
+ /Agrave
+ /Aacute
+ /Acircumflex
+ /Atilde
+ /Adieresis
+ /Aring
+ /AE
+ /Ccedilla
+ /Egrave
+ /Eacute
+ /Ecircumflex
+ /Edieresis
+ /Igrave
+ /Iacute
+ /Icircumflex
+ /Idieresis
+ /Eth % missing
+ /Ntilde
+ /Ograve
+ /Oacute
+ /Ocircumflex
+ /Otilde
+ /Odieresis
+ /multiply % missing
+ /Oslash
+ /Ugrave
+ /Uacute
+ /Ucircumflex
+ /Udieresis
+ /Yacute % missing
+ /Thorn % missing
+ /germandbls
+ /agrave
+ /aacute
+ /acircumflex
+ /atilde
+ /adieresis
+ /aring
+ /ae
+ /ccedilla
+ /egrave
+ /eacute
+ /ecircumflex
+ /edieresis
+ /igrave
+ /iacute
+ /icircumflex
+ /idieresis
+ /eth % missing
+ /ntilde
+ /ograve
+ /oacute
+ /ocircumflex
+ /otilde
+ /odieresis
+ /divide % missing
+ /oslash
+ /ugrave
+ /uacute
+ /ucircumflex
+ /udieresis
+ /yacute % missing
+ /thorn % missing
+ /ydieresis
+] def
+
+/NewFontDirectory FontDirectory maxlength dict def
+
+%
+% Apparently no guarantee findfont is defined in systemdict so the obvious
+%
+% systemdict /findfont get exec
+%
+% can generate an error. So far the only exception is a VT600 (version 48.0).
+%
+
+userdict /@RealFindfont known not {
+ userdict begin
+ /@RealFindfont systemdict begin /findfont load end def
+ end
+} if
+
+/findfont {
+ dup NewFontDirectory exch known not {
+ dup
+ %dup systemdict /findfont get exec % not always in syst…
+ dup userdict /@RealFindfont get exec
+ dup /Encoding get StandardEncoding eq {
+ dup length dict begin
+ {1 index /FID ne {def}{pop pop} ifelse} forall
+ /Encoding ISOLatin1Encoding def
+ currentdict
+ end
+ /DummyFontName exch definefont
+ } if
+ NewFontDirectory 3 1 roll put
+ } if
+ NewFontDirectory exch get
+} bind def
+
+setup
+2 setdecoding
+%%EndSetup
+%%Page: 1 1
+/saveobj save def
+mark
+1 pagesetup
+12 B f
+(Structural Regular Expressions)2 1622 1 2069 1230 t
+10 I f
+(Rob Pike)1 363 1 2698 1470 t
+10 R f
+(AT&T Bell Laboratories)2 993 1 2383 1650 t
+(Murray Hill, New Jersey 07974)4 1267 1 2246 1770 t
+10 I f
+(ABSTRACT)2643 2150 w
+10 R f
+(The current)1 465 1 1330 2410 t
+9 R f
+(UNIX)1821 2410 w
+10 R f
+( the built\255in concept of a)5 999(\256 text processing tools are weakened b…
+( describe the `shape' of files when the typical)8 1908( is a simple notation …
+( regular)1 316( Using)1 298( is regular expressions.)3 942( notation)1 361( T…
+( files has interesting)3 841(expressions to describe the structure in additio…
+(applications, and yields elegant methods for dealing with some problems the c…
+( are composed, the result is)5 1157( operations using these expressions)4 146…
+(reminiscent of shell pipelines.)3 1199 1 1080 3130 t
+10 B f
+(The Peter\255On\255Silicon Problem)2 1299 1 720 3490 t
+10 R f
+( model,)1 301(In the traditional)2 666 2 970 3646 t
+9 R f
+(UNIX)1961 3646 w
+10 R f
+(text files are arrays of lines, and all the familiar tools)10 2120 1 2212 364…
+10 S1 f
+(\320)4358 3646 w
+10 CW f
+(grep)4484 3646 w
+10 R f
+(,)4724 3646 w
+10 CW f
+(sort)4775 3646 w
+10 R f
+(,)5015 3646 w
+10 CW f
+(awk)720 3766 w
+10 R f
+(, etc.)1 197 1 900 3766 t
+10 S1 f
+(\320)1128 3766 w
+10 R f
+( of)1 113( output)1 287( The)1 211(expect arrays of lines as input.)5 1244 4 …
+10 CW f
+(ls)3144 3766 w
+10 R f
+(\(regardless of options\) is a list of files, one)8 1746 1 3294 3766 t
+(per line, that may be selected by tools such as)9 1825 1 720 3886 t
+10 CW f
+(grep)2570 3886 w
+10 R f
+(:)2810 3886 w
+10 CW f
+(ls \255l /usr/ken/bin | grep 'rws.*root')5 2220 1 1080 4066 t
+10 R f
+(\(I assume that the reader is familiar with the)8 1803 1 720 4246 t
+9 R f
+(UNIX)2551 4246 w
+10 R f
+( model is powerful, but it is also pervasive,)8 1769(tools.\) The)1 464 2 280…
+( Many)1 298(sometimes overly so.)2 877 2 720 4366 t
+9 R f
+(UNIX)1933 4366 w
+10 R f
+( more general, and more useful, if they could be)9 2041(programs would be)2 8…
+( example,)1 400( For)1 201(applied to arbitrarily structured input.)4 1549 3 …
+10 CW f
+(diff)2907 4486 w
+10 R f
+( C)1 105(could in principle report differences at the)6 1751 2 3184 4486 t
+( if the interesting quantum of information isn't a line, most of)11 2537( But…
+(the tools \(including)2 804 1 720 4726 t
+10 CW f
+(diff)1562 4726 w
+10 R f
+( solution so the line\255)4 873( perverting the)2 608( Worse,)1 348(\) don't …
+(oriented tools can implement it often obscures the original problem.)9 2714 1…
+( consider the problem of turning)5 1320(To see how a line oriented view of te…
+( input is an array of blank and non\255blank characters, like this:)11 2451( …
+10 CW f
+(#######)1320 5252 w
+(#########)1260 5322 w
+(#### #####)1 660 1 1200 5392 t
+( #)1 180(#### ####)1 720 2 1140 5462 t
+(#### #####)1 840 1 1140 5532 t
+(#### ###)1 840 1 1080 5602 t
+(######## #####)1 960 1 1080 5672 t
+(#### #########)1 840 1 1080 5742 t
+( ####)1 300( #)1 180(#### #)1 360 3 1080 5812 t
+( ##)1 300( ###)1 300(## #)1 240 3 1080 5882 t
+( ###)1 300(### #)1 480 2 1080 5952 t
+(### ##)1 540 1 1080 6022 t
+(## #)1 360 1 1140 6092 t
+(# ####)1 480 1 1200 6162 t
+(# #)1 180 1 1200 6232 t
+(## # ##)2 660 1 1080 6302 t
+10 R f
+(The output is to be statements in a language for laying out integrated circui…
+10 CW f
+(rect minx miny maxx maxy)4 1440 1 1080 6662 t
+10 R f
+( simplify the problem slightly,)4 1247( To)1 169(The statements encode where …
+(the coordinate system has)3 1032 1 720 6962 t
+10 I f
+(x)1778 6962 w
+10 R f
+(positive to the right and)4 954 1 1848 6962 t
+10 I f
+(y)2828 6962 w
+10 R f
+( output need not be efficient in its)7 1346( The)1 206(positive down.)1 590 3…
+(use of rectangles.)2 723 1 720 7082 t
+10 CW f
+(Awk)1507 7082 w
+10 R f
+( which is a mixture of text processing and)8 1790(is the obvious language for…
+( the input is an array of lines, as)8 1345( Since)1 281(geometry, hence arith…
+10 CW f
+(awk)3511 7202 w
+10 R f
+(expects, the job should be fairly)5 1316 1 3724 7202 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 1 1
+%%Page: 2 2
+/saveobj save def
+mark
+2 pagesetup
+10 R f
+(\255 2 \255)2 166 1 2797 480 t
+( is an)2 211( Here)1 243(easy, and in fact it is.)5 846 3 720 840 t
+10 CW f
+(awk)2045 840 w
+10 R f
+(program for the job:)3 807 1 2250 840 t
+10 CW f
+(BEGIN{)1080 1020 w
+(y=1)1330 1140 w
+(})1080 1260 w
+(/^/{)1080 1380 w
+(for\(x=1; x<=length\($0\); x++\))2 1680 1 1330 1500 t
+(if\(substr\($0, x, 1\)=="#"\))2 1500 1 1580 1620 t
+(print "rect", x, y, x+1, y+1)5 1680 1 1830 1740 t
+(y++)1330 1860 w
+(})1080 1980 w
+10 R f
+(Although it is certainly easy to write, there is something odd about this pro…
+10 CW f
+(awk)720 2280 w
+10 R f
+(results in only one obvious advantage)5 1512 1 926 2280 t
+10 S1 f
+(\320)2464 2280 w
+10 R f
+(the ease of tracking)3 781 1 2590 2280 t
+10 CW f
+(y)3397 2280 w
+10 R f
+( breaking out the pieces of)5 1056( task of)2 296(. The)1 231 3 3457 2280 t
+( simple procedural code that does not use any advanced technology such as)12 …
+( peculiarity becomes more evident if the problem is)8 2234( This)1 250( manip…
+(rephrased to demand that each horizontal run of rectangles be folded into a s…
+10 CW f
+(BEGIN{)1080 2820 w
+(y=1)1330 2940 w
+(})1080 3060 w
+(/^/{)1080 3180 w
+(for\(x=1; x<=length\($0\); x++\))2 1680 1 1330 3300 t
+(if\(substr\($0, x, 1\)=="#"\){)2 1560 1 1580 3420 t
+(x0=x;)1830 3540 w
+(while\(++x<=length\($0\) && substr\($0, x, 1\)=="#"\))4 2820 1 1830 3660 t
+(;)2080 3780 w
+(print "rect", x0, y, x, y+1)5 1620 1 1830 3900 t
+(})1580 4020 w
+(y++)1330 4140 w
+(})1080 4260 w
+10 R f
+( In)1 133(Here a considerable amount of code is being spent to do a job a reg…
+(fact, the only regular expression in the program is)8 2044 1 720 4560 t
+10 CW f
+(^)2796 4560 w
+10 R f
+( ver\255)1 191( \(Newer)1 354( input.)1 262(, which is almost irrelevant to t…
+(sions of)1 324 1 720 4680 t
+10 CW f
+(awk)1079 4680 w
+10 R f
+(have mechanisms to use regular expressions within actions, but even there the…
+(between the patterns that match text and the actions that manipulate the text…
+10 CW f
+(Awk's)970 4956 w
+10 R f
+(patterns)1302 4956 w
+10 S1 f
+(\320)1650 4956 w
+10 R f
+( in slashes)2 427(the text)1 304 2 1782 4956 t
+10 CW f
+(//)2546 4956 w
+10 R f
+(that select the input on which to run the actions, the pro\255)11 2341 1 2699…
+( braces)1 280(grams in the)2 498 2 720 5076 t
+10 CW f
+({})1524 5076 w
+10 S1 f
+(\320)1670 5076 w
+10 R f
+( But)1 196(pass to the actions the entire line containing the text matched by…
+( that)1 176( Imagine)1 378( can only be a line.)5 759(much of the power of th…
+10 CW f
+(awk)4860 5196 w
+10 R f
+( so the patterns instead passed precisely the text they matched, with no impl…
+( first program could then be written:)6 1448(aries. Our)1 418 2 720 5436 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 2 2
+%%Page: 3 3
+/saveobj save def
+mark
+3 pagesetup
+10 R f
+(\255 3 \255)2 166 1 2797 480 t
+10 CW f
+(BEGIN{)1080 900 w
+(x=1)1330 1020 w
+(y=1)1330 1140 w
+(})1080 1260 w
+(/ /{)1 240 1 1080 1380 t
+(x++)1330 1500 w
+(})1080 1620 w
+(/#/{)1080 1740 w
+(print "rect", x, x+1, y, y+1)5 1680 1 1330 1860 t
+(x++)1330 1980 w
+(})1080 2100 w
+(/\\n/{)1080 2220 w
+(x=1)1330 2340 w
+(y++)1330 2460 w
+(})1080 2580 w
+10 R f
+( regular expressions to break out complete strings of blanks and)10 2606(and …
+10 CW f
+(#)4699 2760 w
+10 R f
+('s sim\255)1 281 1 4759 2760 t
+(ply:)720 2880 w
+10 CW f
+(BEGIN{)1080 3060 w
+(x=1)1330 3180 w
+(y=1)1330 3300 w
+(})1080 3420 w
+(/ +/{)1 300 1 1080 3540 t
+(x+=length\($0\))1330 3660 w
+(})1080 3780 w
+(/#+/{)1080 3900 w
+(print "rect", x, x+length\($0\), y, y+1)5 2220 1 1330 4020 t
+(x+=length\($0\))1330 4140 w
+(})1080 4260 w
+(/\\n/{)1080 4380 w
+(x=1)1330 4500 w
+(y++)1330 4620 w
+(})1080 4740 w
+10 R f
+( are)1 148(In these programs, regular expressions are being used to do more t…
+(used in all the traditional)4 1050 1 720 5040 t
+9 R f
+(UNIX)1806 5040 w
+10 R f
+( the expressions are doing a simple parsing \(or at least a)11 2375(tools. In…
+( expressions are called)3 900( Such)1 250(breaking into lexical tokens\) of t…
+10 I f
+(structural regular expressions)2 1213 1 3547 5160 t
+10 R f
+(or just)1 254 1 4786 5160 t
+10 I f
+(structural expressions.)1 911 1 720 5280 t
+10 R f
+( notably shorter than the originals, but they are conceptually simpler, becau…
+( The)1 208(the structure of the input is expressed in the structure of the pr…
+( between the patterns and the actions: the patterns select portions of the in…
+( actions contain no code to disassemble the input.)8 1979( The)1 205(while th…
+(The lexical analysis generator)3 1233 1 970 5952 t
+10 CW f
+(lex)2241 5952 w
+10 R f
+( but its)2 301(uses regular expressions to define the structure of text,)8 22…
+( \(its output must be run through the C)8 1594(implementation is poor, and si…
+( conve\255)1 302( even ignoring issues of speed and)6 1400( But)1 200(compile…
+(nience,)720 6312 w
+10 CW f
+(lex)1041 6312 w
+10 R f
+( the next)2 364( As)1 171( structural expressions.)2 938(still misses out on …
+( be nested to describe the structure of a file recursively, with)11 2510(sect…
+(surprising results.)1 711 1 720 6552 t
+10 B f
+(Interactive Text Editing)2 1027 1 720 6792 t
+10 R f
+(It is ironic that)3 589 1 970 6948 t
+9 R f
+(UNIX)1583 6948 w
+10 R f
+( typ\255)1 188(files are uninterpreted byte streams, yet the style of program…
+(ifies)720 7068 w
+9 R f
+(UNIX)925 7068 w
+10 R f
+( imposed on files)3 713(has a fairly rigid structure)4 1071 2 1185 7068 t
+10 S1 f
+(\320)3003 7068 w
+10 R f
+( silent limits)2 514( \(The)1 247(arrays of not\255too\255long lines.)3 1142 …
+( the)1 153( Although)1 434( line lengths by most tools can be frustrating.\))…
+10 CW f
+(awk)3611 7188 w
+10 R f
+(variant introduced above does)3 1218 1 3822 7188 t
+(not exist, there is an interactive text editor,)7 1706 1 720 7308 t
+10 CW f
+(sam)2451 7308 w
+10 R f
+(, that treats its files as simple byte streams.)8 1710 1 2631 7308 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 3 3
+%%Page: 4 4
+/saveobj save def
+mark
+4 pagesetup
+10 R f
+(\255 4 \255)2 166 1 2797 480 t
+(The)970 840 w
+10 CW f
+(sam)1153 840 w
+10 R f
+(command language looks much like that of)6 1744 1 1361 840 t
+10 CW f
+(ed)3133 840 w
+10 R f
+(, but the details are different because)6 1483 1 3253 840 t
+10 CW f
+(sam)4764 840 w
+10 R f
+(is)4973 840 w
+( example, the simple address)4 1151( For)1 189(not line\255oriented.)1 688 3 …
+10 CW f
+(/string/)1080 1140 w
+10 R f
+( there are short\255)3 646( Although)1 434( not the next line containing ``st…
+(hands to simplify common actions, the idea of a line must be stated explicitl…
+10 CW f
+(sam)3941 1440 w
+10 R f
+(.)4121 1440 w
+10 CW f
+(Sam)970 1596 w
+10 R f
+(has the same simple text addition and modification commands)8 2509 1 1177 159…
+10 CW f
+(ed)3713 1596 w
+10 R f
+(has:)3860 1596 w
+10 CW f
+(a)4048 1596 w
+10 R f
+(adds text after the cur\255)4 905 1 4135 1596 t
+(rent location,)1 527 1 720 1716 t
+10 CW f
+(i)1272 1716 w
+10 R f
+(adds text before it,)3 743 1 1357 1716 t
+10 CW f
+(d)2125 1716 w
+10 R f
+(deletes it, and)2 552 1 2210 1716 t
+10 CW f
+(c)2787 1716 w
+10 R f
+(replaces it.)1 432 1 2872 1716 t
+(Unlike in)1 376 1 970 1872 t
+10 CW f
+(ed)1372 1872 w
+10 R f
+(, the current location in)4 933 1 1492 1872 t
+10 CW f
+(sam)2451 1872 w
+10 R f
+( simplifies some)2 660( This)1 230( \(and usually isn't\) a line.)5 1031(need…
+( example,)1 397( For)1 198(operations considerably.)1 985 3 720 1992 t
+10 CW f
+(ed)2334 1992 w
+10 R f
+( a file.)2 268(has several ways to delete all occurrences of a string in)10 2…
+(One method is)2 583 1 720 2112 t
+10 CW f
+(g/string/ s///g)1 900 1 1080 2292 t
+10 R f
+( substitute command is used to delete text within a line, while a delete comm…
+( deleted contains a newline, this technique doesn't work.)8 2271( if the stri…
+( just an array of characters, but some characters are more equal than others.…
+10 CW f
+(Sam)4496 2712 w
+10 R f
+(is more)1 318 1 4722 2712 t
+(forthright:)720 2832 w
+10 CW f
+(x/string/d)1080 3012 w
+10 R f
+(The)720 3192 w
+10 CW f
+(x)905 3192 w
+10 R f
+( runs the subsequent command)4 1256(\(`extract'\) command searches for each o…
+( that this is subtly different)5 1075( Note)1 244( \(not to the line containi…
+(from)720 3432 w
+10 CW f
+(ed)940 3432 w
+10 R f
+('s)1060 3432 w
+10 CW f
+(g)1159 3432 w
+10 R f
+(command:)1246 3432 w
+10 CW f
+(x)1695 3432 w
+10 R f
+(extracts the complete text for the command,)6 1767 1 1782 3432 t
+10 CW f
+(g)3576 3432 w
+10 R f
+( is also)2 282( There)1 284(merely selects lines.)2 811 3 3663 3432 t
+(a complement to)2 666 1 720 3552 t
+10 CW f
+(x)1411 3552 w
+10 R f
+(, called)1 288 1 1471 3552 t
+10 CW f
+(y)1784 3552 w
+10 R f
+(, that extracts the pieces)4 956 1 1844 3552 t
+10 I f
+(between)2825 3552 w
+10 R f
+(the matches of the pattern.)4 1056 1 3177 3552 t
+(The)970 3708 w
+10 CW f
+(x)1151 3708 w
+10 R f
+(command is a loop, and)4 956 1 1237 3708 t
+10 CW f
+(sam)2220 3708 w
+10 R f
+(has a corresponding conditional command, called)5 1990 1 2427 3708 t
+10 CW f
+(g)4444 3708 w
+10 R f
+(\(unrelated to)1 509 1 4531 3708 t
+10 CW f
+(ed)720 3828 w
+10 R f
+('s)840 3828 w
+10 CW f
+(g)937 3828 w
+10 R f
+(\):)997 3828 w
+10 CW f
+(g/pattern/command)1080 4008 w
+10 R f
+( that it does not loop, and it does not change)10 1783( Note)1 246( matches t…
+( lines con\255)2 424( the command to print all)5 1033( Hence)1 309(the curren…
+(taining a string is)3 692 1 720 4428 t
+10 CW f
+(x/.*\\n/ g/string/p)1 1080 1 1080 4608 t
+10 S1 f
+(\320)720 4788 w
+10 R f
+( reverse conditional is)3 891( The)1 209( contains the string.)3 795(extract …
+10 CW f
+(v)4512 4788 w
+10 R f
+(, so to print)3 468 1 4572 4788 t
+(all lines containing `rob' but not `robot':)6 1621 1 720 4908 t
+10 CW f
+(x/.*\\n/ g/rob/ v/robot/p)2 1440 1 1080 5088 t
+10 R f
+(A more dramatic example is to capitalize all occurrences of words `i':)11 279…
+10 CW f
+(x/[A\255Za\255z]+/ g/i/ v/../ c/I/)3 1680 1 1080 5448 t
+10 S1 f
+(\320)720 5628 w
+10 R f
+( more characters, and change the)5 1316(extract all the words, find those tha…
+( people have overcome the dif\255)5 1253( Some)1 282(string to `I' \(borrowin…
+( expressions,)1 530(ficulty of selecting words or identifiers using regular e…
+( the precise definition of `identifier' is immutable in the implementation.)1…
+(With)720 6108 w
+10 CW f
+(sam)945 6108 w
+10 R f
+(, the definition is part of the program and easy to change, although more lon…
+(The program to capitalize `i's should be writable as)8 2057 1 970 6264 t
+10 CW f
+(x/[A\255Za\255z]+/ g/^i$/ c/I/)2 1440 1 1080 6444 t
+10 R f
+(That is, the definition of)4 977 1 720 6624 t
+10 CW f
+(^)1724 6624 w
+10 R f
+(and)1811 6624 w
+10 CW f
+($)1982 6624 w
+10 R f
+( compatibility and because of)4 1188( For)1 192( input.)1 259(should reflect …
+(some problems in the implementation, however,)5 1929 1 720 6744 t
+10 CW f
+(^)2674 6744 w
+10 R f
+(and)2759 6744 w
+10 CW f
+($)2928 6744 w
+10 R f
+(in)3013 6744 w
+10 CW f
+(sam)3116 6744 w
+10 R f
+(always match line boundaries.)3 1209 1 3321 6744 t
+(In)970 6900 w
+10 CW f
+(ed)1078 6900 w
+10 R f
+( each global is still)4 754(, it would not be very useful to nest global comm…
+( However,)1 445(a line.)1 249 2 720 7020 t
+10 CW f
+(sam)1444 7020 w
+10 R f
+( benefit comes from separating)4 1256( \(This)1 266('s extract commands can b…
+( problem of changing all occurrences of the variable)8 2131( the)1 152( Consi…
+10 CW f
+(n)4980 7140 w
+10 R f
+(in a C program to some other name, say)8 1595 1 720 7260 t
+10 CW f
+(num)2340 7260 w
+10 R f
+( method above will work)4 999(. The)1 230 2 2520 7260 t
+10 S1 f
+(\320)3774 7260 w
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 4 4
+%%Page: 5 5
+/saveobj save def
+mark
+5 pagesetup
+10 R f
+(\255 5 \255)2 166 1 2797 480 t
+10 CW f
+(x/[a\255zA\255Z0\2559]+/ g/n/ v/../ c/num/)3 1980 1 1080 900 t
+10 S1 f
+(\320)720 1080 w
+10 R f
+( are places in C where the `identifier')7 1508(except that there)2 663 2 847 …
+10 CW f
+(n)3046 1080 w
+10 R f
+(occurs but not as a variable, in particular as the)9 1906 1 3134 1080 t
+(constant)720 1200 w
+10 CW f
+(\\n)1081 1200 w
+10 R f
+( cou\255)1 204( prevent incorrect changes, the command can be prefixed by a)1…
+(ple of)1 230 1 720 1320 t
+10 CW f
+(y)975 1320 w
+10 R f
+(commands to weed out characters and strings:)6 1841 1 1060 1320 t
+10 CW f
+(y/".*"/ y/'.*'/ x/[a\255zA\255Z0\2559]+/ g/n/ v/../ c/num/)5 2940 1 1080 1500…
+10 R f
+(This example illustrates the power of composing extractions and conditionals,…
+(was encountered when editing a real program \(in fact,)8 2192 1 720 1836 t
+10 CW f
+(sam)2942 1836 w
+10 R f
+( with shell pipe\255)3 659( is an obvious analogy)4 914(\). There)1 345 3 312…
+(lines, but these command)3 1019 1 720 1956 t
+10 I f
+(chains)1765 1956 w
+10 R f
+(are subtly)1 393 1 2052 1956 t
+10 S1 f
+(\320)2472 1956 w
+10 R f
+(and importantly)1 638 1 2599 1956 t
+10 S1 f
+(\320)3264 1956 w
+10 R f
+( flows into)2 432( Data)1 240(different from pipelines.)2 977 3 3391 1956 t
+( chains, the data flow is implicit:)6 1338( In)1 138( pipeline and emerges tr…
+( commands are operating on the same data \(except that the last element of th…
+( is being)2 345( What)1 269( flows through the chain.)4 1008(text\); the comp…
+( in the)2 262(passed from link to link in the chain is a view of the data, un…
+( data stays the same, only the structure is modified.)9 2045(chain. The)1 446…
+10 B f
+(More than one line, and less than one line)8 1771 1 720 2796 t
+10 R f
+(The standard)1 532 1 970 2952 t
+9 R f
+(UNIX)1539 2952 w
+10 R f
+(tools have difficulty handling several lines at a time, if they can do so at …
+10 CW f
+(Grep)720 3072 w
+10 R f
+(,)960 3072 w
+10 CW f
+(sort)1022 3072 w
+10 R f
+(and)1299 3072 w
+10 CW f
+(diff)1480 3072 w
+10 R f
+( if they could operate on larger)6 1296(work on lines only, although it would…
+( as a)2 197(pieces, such)1 491 2 720 3192 t
+10 CW f
+(refer)1443 3192 w
+10 R f
+(database.)1778 3192 w
+10 CW f
+(awk)2206 3192 w
+10 R f
+(can be tricked into accepting multiple\255line records, but then the)9 2619 1…
+( sub\255pieces \(typically ordinary lines\) by explicit code.)7 2236(actions …
+10 CW f
+(sed)4119 3312 w
+10 R f
+(has a unique and)3 704 1 4336 3312 t
+(clumsy mechanism for manipulating multiple lines, which few have mastered.)9 …
+( a)1 84( Consider)1 426(Structural expressions make it easy to specify multip…
+10 CW f
+(refer)4332 3588 w
+10 R f
+(database,)4672 3588 w
+( percent sign and)3 685( line of a record begins with a)7 1210( Each)1 252(wh…
+( the line:)2 366(a character indicating the type of information on)7 1981 2 7…
+10 CW f
+(A)3100 3828 w
+10 R f
+(for author,)1 429 1 3193 3828 t
+10 CW f
+(T)3655 3828 w
+10 R f
+( with)1 211( Staying)1 364(for title, etc.)2 504 3 3748 3828 t
+10 CW f
+(sam)4860 3828 w
+10 R f
+(notation, the command to search a)5 1370 1 720 3948 t
+10 CW f
+(refer)2115 3948 w
+10 R f
+(database for all papers written by Bimmler is:)7 1828 1 2440 3948 t
+10 CW f
+(x/\(.+\\n\)+/ g/%A.*Bimmler/p)1 1560 1 1080 4128 t
+10 S1 f
+(\320)720 4308 w
+10 R f
+( set of lines containing `Bimm\255)5 1257(break the file into non\255empty se…
+( be compatible with the other tools, a `)8 1572( \(To)1 198( after `%A'.)2 48…
+10 CW f
+(.)3498 4428 w
+10 R f
+( Except)1 331(' does not match a newline.\))5 1151 2 3558 4428 t
+(for the structural expression, this is a regular)7 1836 1 720 4548 t
+10 CW f
+(grep)2589 4548 w
+10 R f
+( that)1 184(operation, implying)1 797 2 2862 4548 t
+10 CW f
+(grep)3877 4548 w
+10 R f
+(could benefit from an)3 889 1 4151 4548 t
+( `stream)1 339( the short term, however, a)5 1147( In)1 149(additional regula…
+10 CW f
+(sam)720 4788 w
+10 R f
+(,' analogous to)2 591 1 900 4788 t
+10 CW f
+(sed,)1516 4788 w
+10 R f
+(would be convenient, and is currently being implemented.)7 2322 1 1781 4788 t
+( example, we can)3 713( For)1 196( search program.)2 681(The ability to compo…
+(select just the)2 544 1 720 5064 t
+10 I f
+(titles)1289 5064 w
+10 R f
+(of the papers written by Bimmler by applying another extraction:)9 2605 1 150…
+10 CW f
+(x/\(.+\\n\)+/ g/%A.*Bimmler/ x/.*\\n/ g/%T/p)3 2400 1 1080 5244 t
+10 R f
+( into individual lines, then prints the lines con\255)8 1912(This program bre…
+(taining)720 5544 w
+10 CW f
+(%T)1023 5544 w
+10 R f
+(.)1143 5544 w
+( examples of multiple\255line components of files that may profitably be extr…
+(such as C functions, messages in mail boxes, paragraphs in)9 2415 1 720 5820 t
+10 CW f
+(troff)3166 5820 w
+10 R f
+( records in on\255line telephone)4 1162(input and)1 381 2 3497 5820 t
+( that, unlike in systems that define file structures)8 1948(books. Note)1 509…
+10 I f
+(a priori)1 310 1 3203 5940 t
+10 R f
+(, the structures are applied by the pro\255)7 1527 1 3513 5940 t
+( sometimes a C)3 644( means the structure can change from application to appl…
+( and sometimes it is just a byte)7 1343(program is an array of functions, but…
+(stream.)720 6300 w
+( determine the appearance of their input,)6 1627(If the standard commands adm…
+( a version of)3 553(many currently annoying problems could become simple: ima…
+10 CW f
+(diff)4107 6576 w
+10 R f
+(that could print)2 649 1 4391 6576 t
+( or functions instead of changed lines, or a)8 1710(changed sentences)1 740 2…
+10 CW f
+(sort)3197 6696 w
+10 R f
+(that could sort a)3 647 1 3464 6696 t
+10 CW f
+(refer)4138 6696 w
+10 R f
+(database. The)1 575 1 4465 6696 t
+(case of)1 281 1 720 6816 t
+10 CW f
+(sort)1028 6816 w
+10 R f
+( the input records be described by a struc\255)8 1676(is particularly interes…
+( current bewildering maze of options to control the)8 2062( The)1 209(tural e…
+( be largely replaced by a structural expression to extract the key from the r…
+(multiple expressions to define multiple keys.)5 1794 1 720 7176 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 5 5
+%%Page: 6 6
+/saveobj save def
+mark
+6 pagesetup
+10 R f
+(\255 6 \255)2 166 1 2797 480 t
+10 B f
+(The)720 840 w
+10 CW f
+(awk)912 840 w
+10 B f
+(of the future?)2 582 1 1117 840 t
+10 R f
+(It is entertaining to imagine a version of)7 1622 1 970 996 t
+10 CW f
+(awk)2621 996 w
+10 R f
+( as discussed)2 524( First,)1 263(that applies these ideas throughout.)4 1423…
+( For)1 199( to the actions would be defined, rather than merely selected, by …
+(example,)720 1236 w
+10 CW f
+(/#+/ { print })3 840 1 1080 1416 t
+10 R f
+(would print only)2 667 1 720 1596 t
+10 CW f
+(#)1412 1596 w
+10 R f
+(characters; conventional)1 972 1 1497 1596 t
+10 CW f
+(awk)2494 1596 w
+10 R f
+(would instead print every line containing)5 1640 1 2699 1596 t
+10 CW f
+(#)4364 1596 w
+10 R f
+(characters.)4449 1596 w
+( of using the restrictive idea of a)7 1317( Instead)1 342( parsed.)1 314(Next…
+( instance, in)2 496( For)1 197( demarcate fields.)2 722(field separator, the …
+(the program)1 485 1 720 1992 t
+10 CW f
+(/\(.+\\n\)+/ {)1 660 1 1080 2172 t
+10 I f
+(action)1800 2172 w
+10 CW f
+(})2110 2172 w
+10 R f
+( lines, but the outermost closure \(the)6 1481(the action sees groups of)4 99…
+10 CW f
+(+)3229 2352 w
+10 R f
+(operator\) examines, and hence can extract,)5 1722 1 3318 2352 t
+(the individual lines.)2 802 1 720 2472 t
+10 CW f
+(ed)1577 2472 w
+10 R f
+( We)1 192( back\255referencing operators.)2 1128(uses parentheses to define s…
+( to define the `fields' in)5 980(can modify this idea)3 834 2 720 2592 t
+10 CW f
+(awk)2567 2592 w
+10 R f
+(, so)1 147 1 2747 2592 t
+10 CW f
+($1)2927 2592 w
+10 R f
+(defines the first element of the closure \(the first)8 1960 1 3080 2592 t
+(line\),)720 2712 w
+10 CW f
+($2)961 2712 w
+10 R f
+( arrays, so the)3 575( interestingly, the closures could generate indices for…
+( say,)1 191(fields would be called,)3 925 2 720 2832 t
+10 CW f
+(input[1])1869 2832 w
+10 R f
+(and so on, perhaps with the unadorned identifier)7 1986 1 2382 2832 t
+10 CW f
+(input)4401 2832 w
+10 R f
+(holding)4734 2832 w
+( generate multi\255dimensional)2 1163( has the advantage that nested closures…
+( is some subtlety involving the relationship between)7 2192( \(There)1 331(ar…
+10 CW f
+(input)4740 3072 w
+10 R f
+(indices and the order of the closures in the pattern, but the details are not…
+(Finally, as in)2 524 1 970 3348 t
+10 CW f
+(sam)1521 3348 w
+10 R f
+( expressions would be applicable to the output of structural expressions;)10 …
+( following program computes)3 1185( The)1 205( actions.)1 333(that is, we wou…
+(how many pages of articles Bimmler has written:)7 1967 1 720 3588 t
+10 CW f
+( break into records)3 1140(/\(.+\\n\)+/{ #)1 900 2 1080 3768 t
+( is Bimmler author? \(see text\))5 1800( #)1 300(input ~ /%A.*Bimmler/{)2 132…
+( extract page numbers)3 1260(/%P.*\([0\2559]+\)\255\([0\2559]+\)/{ #)1 1740 2…
+(pages+=input[2]\255input[1]+1)1830 4128 w
+(})1580 4248 w
+(})1330 4368 w
+(})1080 4488 w
+(END{)1080 4608 w
+(print pages)1 660 1 1330 4728 t
+(})1080 4848 w
+10 R f
+(Real)720 5028 w
+10 CW f
+(awk)935 5028 w
+10 R f
+( \(that is, regular expressions\) only like)6 1582(uses patterns)1 520 2 1147…
+10 CW f
+(sam)3282 5028 w
+10 R f
+('s)3462 5028 w
+10 CW f
+(g)3567 5028 w
+10 R f
+(command, but our)2 746 1 3660 5028 t
+10 CW f
+(awk)4439 5028 w
+10 R f
+('s patterns)1 421 1 4619 5028 t
+(are)720 5148 w
+10 CW f
+(x)871 5148 w
+10 R f
+( is why in the pro\255)5 750( This)1 232( we need both to exploit structural …
+(gram above the test for whether)5 1276 1 720 5268 t
+10 CW f
+(input)2024 5268 w
+10 R f
+(contains a paper by Bimmler must be written as an explicit pattern)11 2688 1 …
+( separated by a dash, which is how)7 1393( innermost pattern searches for lin…
+10 CW f
+(refer)720 5508 w
+10 R f
+(stores the starting and ending pages of the article.)8 1977 1 1045 5508 t
+( real)1 178( The)1 209(This is a contrived example, of course, but it illustr…
+10 CW f
+(awk)4261 5664 w
+10 R f
+(suffers from a)2 569 1 4471 5664 t
+( making the parsing actions of the)6 1366( would be improved by)4 939( It)1 1…
+( lan\255)1 185( A)1 127( pattern\255matching abilities available in the actio…
+(guage with regular expressions should not base its text manipulation on a)11 …
+10 CW f
+(substr)3673 6024 w
+10 R f
+(function.)4058 6024 w
+10 B f
+(Comments)720 6264 w
+10 R f
+( is a powerful and convenient, if unfa\255)7 1545(The use of regular expressi…
+( current)1 308(miliar, way to address a number of difficulties the)8 2010 2 7…
+9 R f
+(UNIX)3062 6540 w
+10 R f
+( is obviously around this)4 988( There)1 283(tools share.)1 456 3 3313 6540 t
+( all.)1 172(new notation a number of interesting problems, and I am not prete…
+( the possibilities,)2 678(Rather, I have skipped enthusiastically from exampl…
+( these ideas, and perhaps to)5 1117( hope is to encourage others to think abo…
+(apply them to old tools as well as new ones.)9 1760 1 720 7020 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 6 6
+%%Page: 7 7
+/saveobj save def
+mark
+7 pagesetup
+10 R f
+(\255 7 \255)2 166 1 2797 480 t
+10 B f
+(Acknowledgements)720 840 w
+10 R f
+( some of their ideas)4 806(John Linderman, Chris Van Wyk, Tom Duff and Norman…
+( hope I have not misrepresented them.)6 1522( I)1 83(in these notes.)2 569 3 …
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 7 7
+%%Trailer
+done
+%%Pages: 7
+%%DocumentFonts: Times-Roman Times-Bold Times-Italic Times-Roman Courier
diff --git a/include/frame.h b/include/frame.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+
+typedef struct Frbox Frbox;
+typedef struct Frame Frame;
+
+struct Frbox
+{
+ long wid; /* in pixels */
+ long nrune; /* <0 ==> negate and treat a…
+ union{
+ uchar *ptr;
+ struct{
+ short bc; /* break char */
+ short minwid;
+ } b;
+ } a;
+};
+
+struct Frame
+{
+ XftFont *font; /* of chars in the frame …
+ Bitmap *b; /* on which frame appears */
+ Rectangle r; /* in which text appears */
+ Rectangle entire; /* of full frame */
+ Frbox *box;
+ ulong p0, p1; /* selection */
+ short left; /* left edge of text */
+ ushort nbox, nalloc;
+ ushort maxtab; /* max size of tab, in pi…
+ ushort nchars; /* # runes in frame */
+ ushort nlines; /* # lines with text */
+ ushort maxlines; /* total # lines in frame */
+ ushort lastlinefull; /* last line fills frame */
+ ushort modified; /* changed since frselect() */
+};
+
+ulong frcharofpt(Frame*, Point);
+Point frptofchar(Frame*, ulong);
+int frdelete(Frame*, ulong, ulong);
+void frinsert(Frame*, Rune*, Rune*, ulong);
+void frselect(Frame*, Mouse*);
+void frselectp(Frame*, Fcode);
+void frselectf(Frame*, Point, Point, Fcode);
+void frinit(Frame*, Rectangle, XftFont*, Bitmap*);
+void frsetrects(Frame*, Rectangle, Bitmap*);
+void frclear(Frame*);
+void frgetmouse(void);
+
+uchar *_frallocstr(unsigned);
+void _frinsure(Frame*, int, unsigned);
+Point _frdraw(Frame*, Point);
+void _frgrowbox(Frame*, int);
+void _frfreebox(Frame*, int, int);
+void _frmergebox(Frame*, int);
+void _frdelbox(Frame*, int, int);
+void _frsplitbox(Frame*, int, int);
+int _frfindbox(Frame*, int, ulong, ulong);
+void _frclosebox(Frame*, int, int);
+int _frcanfit(Frame*, Point, Frbox*);
+void _frcklinewrap(Frame*, Point*, Frbox*);
+void _frcklinewrap0(Frame*, Point*, Frbox*);
+void _fradvance(Frame*, Point*, Frbox*);
+int _frnewwid(Frame*, Point, Frbox*);
+void _frclean(Frame*, Point, int, int);
+void _frredraw(Frame*, Point);
+void _fraddbox(Frame*, int, int);
+Point _frptofcharptb(Frame*, ulong, Point, int);
+Point _frptofcharnb(Frame*, ulong, int);
+int _frstrlen(Frame*, int);
+
+#define NRUNE(b) ((b)->nrune<0? 1 : (b)->nrune)
+#define NBYTE(b) strlen((char*)(b)->a.ptr)
diff --git a/include/libc.h b/include/libc.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+
+ /* Plan 9 C library interface */
+
+
+#define sprint sprintf
+#define dup(a,b) dup2(a,b)
+#define seek(a,b,c) lseek(a,b,c)
+#define create(name, mode, perm) creat(name, perm)
+#define exec(a,b) execv(a,b)
+#define USED(a)
+#define SET(a)
+
+#define _exits(v) if (v!=0) _exit(1); else _exit…
+
+enum
+{
+ OREAD = 0, /* open for read */
+ OWRITE = 1, /* open for write */
+ ORDWR = 2, /* open for read/write */
+ ERRLEN = 64 /* length of error message */
+};
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a u…
+ Runeself = 0x80, /* rune and utf sequences are t…
+ Runeerror = 0x80 /* decoding error in utf */
+};
+
+/*
+ * new rune routines
+ */
+extern int runetochar(char*, Rune*);
+extern int chartorune(Rune*, char*);
+extern int runelen(long);
+extern int fullrune(char*, int);
+
+/*
+ * rune routines from converted str routines
+ */
+extern int utflen(char*); /* was countrune */
+extern char* utfrune(char*, long);
+extern char* utfrrune(char*, long);
+extern char* utfutf(char*, char*);
+/*
+ * Miscellaneous functions
+ */
+extern void fprint(int, char *, ...);
+extern int notify (void(*)(void *, char *));
+extern int errstr(char *);
+extern char* getuser(void);
+extern void exits(char*);
diff --git a/include/libg.h b/include/libg.h
@@ -0,0 +1,225 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#ifndef _LIBG_H
+#define _LIBG_H
+
+#ifndef _LIBXG_EXTENSION
+ This header file is not defined in pure ANSI/POSIX
+#endif
+/*
+ * Like Plan9's libg.h, but suitable for inclusion on non-Plan9 machines
+ */
+
+#define Cursor xCursor
+#include <X11/Xft/Xft.h>
+#undef Cursor
+
+enum{ EMAXMSG = 128+8192 }; /* max event size */
+
+/*
+ * Types
+ */
+
+typedef struct Bitmap Bitmap;
+typedef struct Point Point;
+typedef struct Rectangle Rectangle;
+typedef struct Cursor Cursor;
+typedef struct Mouse Mouse;
+typedef struct Menu Menu;
+typedef struct Event Event;
+typedef struct RGB RGB;
+
+struct Point
+{
+ int x;
+ int y;
+};
+
+struct Rectangle
+{
+ Point min;
+ Point max;
+};
+
+struct Bitmap
+{
+ Rectangle r; /* rectangle in data area, local coords */
+ Rectangle clipr; /* clipping region */
+ int ldepth;
+ int id; /* as known by the X server */
+ Bitmap *cache; /* zero; distinguishes bitmap fro…
+ int flag; /* flag used by X implementation of li…
+};
+
+struct Mouse
+{
+ int buttons; /* bit array: LMR=124 */
+ Point xy;
+ unsigned long msec;
+};
+
+struct Cursor
+{
+ Point offset;
+ unsigned char clr[2*16];
+ unsigned char set[2*16];
+ int id; /* init to zero; used by library */
+};
+
+struct Menu
+{
+ char **item;
+ char *(*gen)(int);
+ int lasthit;
+};
+
+struct Event
+{
+ int kbdc;
+ Mouse mouse;
+ int n; /* number of characters in mesage…
+ unsigned char data[EMAXMSG]; /* message from an arbitrar…
+};
+
+struct RGB
+{
+ unsigned long red;
+ unsigned long green;
+ unsigned long blue;
+};
+
+/*
+ * Codes for bitblt etc.
+ *
+ * D
+ * 0 1
+ * ---------
+ * 0 | 1 | 2 |
+ * S |---|---|
+ * 1 | 4 | 8 |
+ * ---------
+ *
+ * Usually used as D|S; DorS is so tracebacks are readable.
+ */
+typedef
+enum Fcode
+{
+ Zero = 0x0,
+ DnorS = 0x1,
+ DandnotS = 0x2,
+ notS = 0x3,
+ notDandS = 0x4,
+ notD = 0x5,
+ DxorS = 0x6,
+ DnandS = 0x7,
+ DandS = 0x8,
+ DxnorS = 0x9,
+ D = 0xA,
+ DornotS = 0xB,
+ S = 0xC,
+ notDorS = 0xD,
+ DorS = 0xE,
+ F = 0xF
+} Fcode;
+
+/*
+ * Miscellany
+ */
+
+typedef void (*Errfunc)(char *);
+
+extern Point add(Point, Point);
+extern Point sub(Point, Point);
+extern Point mul(Point, int);
+extern Point divpt(Point, int);
+extern Rectangle rsubp(Rectangle, Point);
+extern Rectangle raddp(Rectangle, Point);
+extern Rectangle inset(Rectangle, int);
+extern Rectangle rmul(Rectangle, int);
+extern Rectangle rdiv(Rectangle, int);
+extern Rectangle rshift(Rectangle, int);
+extern Rectangle rcanon(Rectangle);
+extern Bitmap* balloc(Rectangle, int);
+extern void bfree(Bitmap*);
+extern int rectclip(Rectangle*, Rectangle);
+extern void xtbinit(Errfunc, char*, int*, char**, char**);
+extern void bclose(void);
+extern void berror(char*);
+extern void bitblt(Bitmap*, Point, Bitmap*, Rectangle, Fcode);
+extern void copymasked(Bitmap*, Point, Bitmap*, Bitmap*, Rectangle);
+extern int bitbltclip(void*);
+extern Point string(Bitmap*, Point, XftFont*, char*, Fcode);
+extern void segment(Bitmap*, Point, Point, int, Fcode);
+extern void point(Bitmap*, Point, int, Fcode);
+extern void arc(Bitmap*, Point, Point, Point, int, Fcode);
+extern void circle(Bitmap*, Point, int, int, Fcode);
+extern void disc(Bitmap*, Point, int, int, Fcode);
+extern void ellipse(Bitmap*, Point, int, int, int, Fcode);
+extern void polysegment(Bitmap *, int, Point *, int, Fcode);
+extern long strwidth(XftFont*, char*);
+extern Point strsize(XftFont*, char*);
+extern long charwidth(XftFont*, Rune);
+extern void texture(Bitmap*, Rectangle, Bitmap*, Fcode);
+extern void wrbitmap(Bitmap*, int, int, unsigned char*);
+extern void rdbitmap(Bitmap*, int, int, unsigned char*);
+extern void wrbitmapfile(int, Bitmap*);
+extern Bitmap* rdbitmapfile(int);
+extern int ptinrect(Point, Rectangle);
+extern int rectXrect(Rectangle, Rectangle);
+extern int eqpt(Point, Point);
+extern int eqrect(Rectangle, Rectangle);
+extern void border(Bitmap*, Rectangle, int, Fcode);
+extern void cursorswitch(Cursor*);
+extern void cursorset(Point);
+extern Rectangle bscreenrect(Rectangle*);
+extern void bflush(void);
+extern int clipline(Rectangle, Point*, Point*);
+extern int clipr(Bitmap*, Rectangle);
+extern int scrpix(int*,int*);
+
+extern void einit(unsigned long);
+extern unsigned long estart(unsigned long, int, int);
+extern unsigned long etimer(unsigned long, long);
+extern unsigned long event(Event*);
+extern unsigned long eread(unsigned long, Event*);
+extern Mouse emouse(void);
+extern int ekbd(void);
+extern int ecanread(unsigned long);
+extern int ecanmouse(void);
+extern int ecankbd(void);
+extern void ereshaped(Rectangle); /* supplied by user */
+extern void eflush(unsigned long);
+extern int menuhit(int, Mouse*, Menu*);
+extern Rectangle getrect(int, Mouse*);
+extern unsigned long rgbpix(Bitmap*, RGB);
+extern void rdcolmap(Bitmap*, RGB*);
+extern void wrcolmap(Bitmap*, RGB*);
+
+/* Extra functions supplied by libXg */
+extern int snarfswap(char*, int, char**);
+extern int scrollfwdbut(void);
+
+enum{
+ Emouse = 1,
+ Ekeyboard = 2
+};
+
+extern Point Pt(int, int);
+extern Rectangle Rect(int, int, int, int);
+extern Rectangle Rpt(Point, Point);
+
+
+#define Dx(r) ((r).max.x-(r).min.x)
+#define Dy(r) ((r).max.y-(r).min.y)
+
+
+extern Bitmap screen;
+extern XftFont *font;
+extern XftColor fontcolor;
+extern XftColor bgcolor;
+
+#define BGSHORT(p) (((p)[0]<<0) | ((p)[1]<<8))
+#define BGLONG(p) ((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
+#define BPSHORT(p, v) ((p)[0]=(v), (p)[1]=((v)>>8))
+#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>…
+
+#endif
diff --git a/include/regexp.h b/include/regexp.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+
+typedef struct Resub Resub;
+typedef struct Reclass Reclass;
+typedef struct Reinst Reinst;
+typedef struct Reprog Reprog;
+
+/*
+ * Sub expression matches
+ */
+struct Resub{
+ union
+ {
+ char *sp;
+ Rune *rsp;
+ }s;
+ union
+ {
+ char *ep;
+ Rune *rep;
+ }e;
+};
+
+/*
+ * character class, each pair of rune's defines a range
+ */
+struct Reclass{
+ Rune *end;
+ Rune spans[64];
+};
+
+/*
+ * Machine instructions
+ */
+struct Reinst{
+ int type;
+ union {
+ Reclass *cp; /* class pointer */
+ Rune r; /* character */
+ int subid; /* sub-expression id for RBRA…
+ Reinst *right; /* right child of OR */
+ }u1;
+ union { /* regexp relies on these two being in the same union */
+ Reinst *left; /* left child of OR */
+ Reinst *next; /* next instruction for CAT & LBR…
+ }u2;
+};
+
+/*
+ * Reprogram definition
+ */
+struct Reprog{
+ Reinst *startinst; /* start pc */
+ Reclass class[16]; /* .data */
+ Reinst firstinst[5]; /* .text */
+};
+
+extern Reprog *regcomp(char*);
+extern Reprog *regcomplit(char*);
+extern Reprog *regcompnl(char*);
+extern void regerror(char*);
+extern int regexec(Reprog*, char*, Resub*, int);
+extern void regsub(char*, char*, Resub*, int);
+extern int rregexec(Reprog*, Rune*, Resub*, int);
+extern void rregsub(Rune*, Rune*, Resub*, int);
diff --git a/include/u.h b/include/u.h
@@ -0,0 +1,117 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+typedef unsigned char uchar;
+typedef unsigned short Rune;
+
+ /* System configuration parameters */
+
+#ifdef SYSVR3
+#include <malloc.h>
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#define remove(v) unlink(v)
+#define WEXITSTATUS(s) (((s)>>8)&0xFF)
+extern char *getenv(char*);
+extern char *getlogin(void);
+extern char *strerror(int);
+extern void *memmove(void*, const void*, size_t);
+#define NEEDMEMMOVE
+#define NEEDSTRERROR
+#define NEEDVARARG
+#endif /* SYSVR3 */
+
+#ifdef IRIX5
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif /* IRIX5 */
+
+#ifdef IRIX
+extern void *memmove(void*, const void*, size_t);
+#define NEEDMEMMOVE
+#endif /* IRIX */
+
+#ifdef UMIPS
+typedef unsigned long ulong;
+typedef unsigned short ushort;
+#define const /* mips compiler doesn't support c…
+extern char *strerror(int);
+extern void *memmove(void*, const void*, size_t);
+#define NEEDMEMMOVE
+#define NEEDSTRERROR
+#define NEEDVARARG
+#define NOFIFO /* turn off exstart in samterm/un…
+#endif /* UMIPS */
+
+#ifdef SUNOS
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+extern char *strerror(int);
+extern void *memmove(void*, const void*, size_t);
+extern void *memcpy(void*, const void*, size_t);
+#define NEEDMEMMOVE
+#define NEEDSTRERROR
+#endif /* SUNOS */
+
+#ifdef SOLARIS
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif
+
+#ifdef AIX
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif /* AIX */
+
+#ifdef OSF1
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+extern void *memmove(void*, const void*, size_t);
+#endif /* OSF1 */
+
+#ifdef HPUX
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#define NEEDSTRERROR
+#endif /* HPUX */
+
+#ifdef APOLLO
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif /* APOLLO */
+
+#ifdef CONVEX
+typedef unsigned long ulong;
+#endif /* CONVEX */
+
+#ifdef DYNIX
+#define SIG_ERR BADSIG
+#define NEEDMEMMOVE
+#define remove(v) unlink(v)
+#define WEXITSTATUS(s) (((s)>>8)&0xFF)
+#define NEEDMEMMOVE
+#define NOFIFO /* turn off exstart in samterm/un…
+#endif /* DYNIX */
+
+#ifdef PTX
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif /* PTX */
+
+#ifdef BSDi
+typedef unsigned long ulong;
+#endif /* BSDi */
+
+#ifdef v10
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+#endif
diff --git a/libXg/Gwin.h b/libXg/Gwin.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#ifndef GWIN_H
+#define GWIN_H
+
+/* New resource names */
+
+#define XtNscrollForwardR "scrollForwardR"
+#define XtCScrollForwardR "ScrollForwardR"
+#define XtNreshaped "reshaped"
+#define XtCReshaped "Reshaped"
+#define XtNgotchar "gotchar"
+#define XtCGotchar "Gotchar"
+#define XtNgotmouse "gotmouse"
+#define XtCGotmouse "Gotmouse"
+#define XtNp9font "p9font"
+#define XtCP9font "P9font"
+#define XtNcomposeMod "composeMod"
+#define XtCComposeMod "ComposeMod"
+
+/* External reference to the class record pointer */
+extern WidgetClass gwinWidgetClass;
+
+/* Type definition for gwin widgets */
+typedef struct _GwinRec *GwinWidget;
+
+/* Type definition for gwin resources */
+typedef struct {
+ int buttons;
+ struct {
+ int x;
+ int y;
+ } xy;
+ unsigned long msec;
+ } Gwinmouse;
+
+typedef void (*Reshapefunc)(int, int, int, int);
+typedef void (*Charfunc)(int);
+typedef void (*Mousefunc)(Gwinmouse*);
+
+/* Method declarations */
+extern String GwinSelectionSwap(Widget, String);
+
+#endif /* GWIN_H */
diff --git a/libXg/GwinP.h b/libXg/GwinP.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#ifndef GWINP_H
+#define GWINP_H
+
+#include "Gwin.h"
+
+/* Gwin is derived from Core */
+
+/* Gwin instance part */
+typedef struct {
+ /* New resource fields */
+ Pixel foreground;
+ Boolean forwardr; /* does right button scroll fo…
+ Reshapefunc reshaped; /* Notify app of reshape */
+ Charfunc gotchar; /* Notify app of char arrival */
+ Mousefunc gotmouse; /* Notify app of mouse change */
+ String selection; /* Current selection */
+ int compose;
+} GwinPart;
+
+/* Full instance record */
+typedef struct _GwinRec {
+ CorePart core;
+ GwinPart gwin;
+} GwinRec;
+
+/* New type for class methods */
+typedef String (*SelSwapProc)(Widget, String);
+
+/* Class part */
+typedef struct {
+ SelSwapProc select_swap;
+ XtPointer extension;
+} GwinClassPart;
+
+/* Full class record */
+typedef struct _GwinClassRec {
+ CoreClassPart core_class;
+ GwinClassPart gwin_class;
+} GwinClassRec, *GwinWidgetClass;
+
+/* External definition for class record */
+extern GwinClassRec gwinClassRec;
+
+#endif /* GWINP_H */
diff --git a/libXg/Makefile b/libXg/Makefile
@@ -0,0 +1,58 @@
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+#
+# Prototype Makefile for libXg
+#
+# define operating system. ONE of:
+# -DIRIX -DSUNOS -DUMIPS -DSYSVR3 -DAIX -DOSF1
+# -DHPUX -DAPOLLO -DCONVEX -DDYNIX
+#
+# Additionally, -D_POSIX_SOURCE (or its equivalent) may be specified
+# if your compiler supports posix-compatible compilation
+include ../config.mk
+
+OS=-DIRIX5
+
+# add -Iincludedir for any include directories that need to be searched
+INCS=-I../include -I$(FREETYPEINC)
+
+# set this if your X libraries are in different locations
+# or if you need extra libraries to load with X11 applications
+XLIBS=-lXt
+
+# add name of library orderer - use ":" if none
+RANLIB=:
+
+# add name of librarian
+AR=ar
+
+# the name of the library
+LIB=libXg.a
+
+CFLAGS=$(OS) -D_LIBXG_EXTENSION $(INCS)
+CC=cc
+
+OBJS= arc.o arith.o balloc.o bitblt.o bitbltclip.o border.o bscreenrect…
+ circle.o clipline.o clipr.o copymasked.o cursorset.o cursorswitch.o\
+ disc.o ellipse.o font.o gcs.o getrect.o gwin.o ldconvert.o latin1.o\
+ menuhit.o point.o polysegment.o rdbitmap.o rdbitmapfile.o\
+ rectclip.o rune.o segment.o string.o strwidth.o texture.o\
+ wrbitmap.o wrbitmapfile.o xtbinit.o
+
+all install: $(LIB)
+compile: $(LIB)
+test: $(LIB) test.o
+ $(CC) -o $@ $? $(LIB) $(XLIBS) -lm
+ echo try running test
+clean:
+ rm -f *.o test *.a
+
+nuke: clean
+ rm -f $(LIB)
+
+$(LIB): $(OBJS)
+ $(AR) rv $(LIB) $(OBJS)
+ $(RANLIB) $(LIB)
+
+$(LIB)(%.o): %.o
+
+$(OBJS): ../include/libg.h libgint.h ../include/libc.h
diff --git a/libXg/arc.c b/libXg/arc.c
@@ -0,0 +1,45 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+#include <math.h>
+
+#define rad2deg(x) 180*((x)/3.141592653589793238462643383279502884197169399375…
+
+void
+arc(Bitmap *b, Point p0, Point p1, Point p2, int v, Fcode f)
+{
+ unsigned int d;
+ int x, y, r, start, end, delta;
+ GC g;
+
+ p1.x -= p0.x;
+ p1.y -= p0.y;
+ p2.x -= p0.x;
+ p2.y -= p0.y;
+ r = (int)sqrt((double)(p1.x*p1.x + p1.y*p1.y));
+ start = (int)(64*rad2deg(atan2(-p2.y, p2.x)));
+ end = (int)(64*rad2deg(atan2(-p1.y, p1.x)));
+ if(start < 0)
+ start += 64*360;
+ if(end < 0)
+ end += 64*360;
+ delta = end - start;
+ if(delta < 0)
+ delta += 64*360;
+ x = p0.x - r;
+ y = p0.y - r;
+ if(b->flag&SHIFT){
+ x -= b->r.min.x;
+ y -= b->r.min.y;
+ }
+ d = 2*r;
+ g = _getfillgc(f, b, v);
+ /*
+ * delta is positive, so this draws counterclockwise arc
+ * from start to start+delta
+ */
+ XDrawArc(_dpy, (Drawable)b->id, g, x, y, d, d, start, delta);
+}
+
diff --git a/libXg/arith.c b/libXg/arith.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+
+Point
+add(Point a, Point b)
+{
+ a.x += b.x;
+ a.y += b.y;
+ return a;
+}
+
+Point
+sub(Point a, Point b)
+{
+ a.x -= b.x;
+ a.y -= b.y;
+ return a;
+}
+
+Rectangle
+inset(Rectangle r, int n)
+{
+ r.min.x += n;
+ r.min.y += n;
+ r.max.x -= n;
+ r.max.y -= n;
+ return r;
+}
+
+Point
+divpt(Point a, int b)
+{
+ a.x /= b;
+ a.y /= b;
+ return a;
+}
+
+Point
+mul(Point a, int b)
+{
+ a.x *= b;
+ a.y *= b;
+ return a;
+}
+
+Rectangle
+rsubp(Rectangle r, Point p)
+{
+ r.min.x -= p.x;
+ r.min.y -= p.y;
+ r.max.x -= p.x;
+ r.max.y -= p.y;
+ return r;
+}
+
+Rectangle
+raddp(Rectangle r, Point p)
+{
+ r.min.x += p.x;
+ r.min.y += p.y;
+ r.max.x += p.x;
+ r.max.y += p.y;
+ return r;
+}
+
+Rectangle
+rmul(Rectangle r, int a)
+{
+ if (a != 1) {
+ r.min.x *= a;
+ r.min.y *= a;
+ r.max.x *= a;
+ r.max.y *= a;
+ }
+ return r;
+}
+
+Rectangle
+rdiv(Rectangle r, int a)
+{
+ if (a != 1) {
+ r.min.x /= a;
+ r.min.y /= a;
+ r.max.x /= a;
+ r.max.y /= a;
+ }
+ return r;
+}
+
+Rectangle
+rshift(Rectangle r, int a)
+{
+ if (a > 0) {
+ r.min.x <<= a;
+ r.min.y <<= a;
+ r.max.x <<= a;
+ r.max.y <<= a;
+ }
+ else if (a < 0) {
+ a = -a;
+ r.min.x >>= a;
+ r.min.y >>= a;
+ r.max.x >>= a;
+ r.max.y >>= a;
+ }
+ return r;
+}
+
+eqpt(Point p, Point q)
+{
+ return p.x==q.x && p.y==q.y;
+}
+
+eqrect(Rectangle r, Rectangle s)
+{
+ return r.min.x==s.min.x && r.max.x==s.max.x &&
+ r.min.y==s.min.y && r.max.y==s.max.y;
+}
+
+rectXrect(Rectangle r, Rectangle s)
+{
+ return r.min.x<s.max.x && s.min.x<r.max.x &&
+ r.min.y<s.max.y && s.min.y<r.max.y;
+}
+
+int
+rectinrect(Rectangle r, Rectangle s)
+{
+ /* !ptinrect(r.min, s) in line for speed */
+ if(!(r.min.x>=s.min.x && r.min.x<s.max.x &&
+ r.min.y>=s.min.y && r.min.y<s.max.y))
+ return 0;
+ return r.max.x<=s.max.x && r.max.y<=s.max.y;
+}
+
+ptinrect(Point p, Rectangle r)
+{
+ return p.x>=r.min.x && p.x<r.max.x &&
+ p.y>=r.min.y && p.y<r.max.y;
+}
+
+Rectangle
+rcanon(Rectangle r)
+{
+ int t;
+ if (r.max.x < r.min.x) {
+ t = r.min.x;
+ r.min.x = r.max.x;
+ r.max.x = t;
+ }
+ if (r.max.y < r.min.y) {
+ t = r.min.y;
+ r.min.y = r.max.y;
+ r.max.y = t;
+ }
+ return r;
+}
+
+Rectangle
+Rect(int x1, int y1, int x2, int y2)
+{
+ Rectangle r;
+
+ r.min.x = x1;
+ r.min.y = y1;
+ r.max.x = x2;
+ r.max.y = y2;
+ return r;
+}
+
+Rectangle
+Rpt(Point p1, Point p2)
+{
+ Rectangle r;
+
+ r.min = p1;
+ r.max = p2;
+ return r;
+}
+
+Point
+Pt(int x, int y)
+{
+ Point p;
+
+ p.x = x;
+ p.y = y;
+ return p;
+}
diff --git a/libXg/balloc.c b/libXg/balloc.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+Bitmap*
+balloc(Rectangle r, int ldepth)
+{
+ Bitmap *b;
+
+ b = _balloc(r, ldepth);
+ bitblt(b, r.min, b, r, Zero);
+ return b;
+}
+
+Bitmap*
+_balloc(Rectangle r, int ldepth)
+{
+ int id;
+ Bitmap *b;
+ int ld;
+ Rectangle rx;
+
+ b = (Bitmap *)malloc(sizeof(Bitmap));
+ if(b == 0)
+ berror("balloc malloc");
+ if (ldepth == 0)
+ ld = 0;
+ else
+ ld = screen.ldepth;
+ rx = r;
+ if (Dx(rx) == 0)
+ rx.max.x++;
+ if (Dy(rx) == 0)
+ rx.max.y++;
+ id = (int) XCreatePixmap(_dpy, (Drawable)screen.id,
+ Dx(rx), Dy(rx), _ld2d[ld]);
+ b->ldepth = ldepth;
+ b->r = r;
+ b->clipr = r;
+ b->id = id;
+ b->cache = 0;
+ if(ldepth == 0)
+ b->flag = DP1|BL1;
+ else
+ b->flag = screen.flag&BL1;
+ if(r.min.x==0 && r.min.y ==0)
+ b->flag |= ZORG;
+ else
+ b->flag |= SHIFT;
+ return b;
+}
+
+void
+bfree(Bitmap *b)
+{
+ XFreePixmap(_dpy, (Pixmap)b->id);
+ free(b);
+}
diff --git a/libXg/bitblt.c b/libXg/bitblt.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+bitblt(Bitmap *d, Point p, Bitmap *s, Rectangle r, Fcode f)
+{
+ int sx, sy, dx, dy, bfunc;
+ GC g;
+ unsigned long plane;
+ Bitmap *btmp;
+
+ if(Dx(r)<=0 || Dy(r)<=0)
+ return;
+ sx = r.min.x;
+ sy = r.min.y;
+ if(s->flag&SHIFT){
+ sx -= s->r.min.x;
+ sy -= s->r.min.y;
+ }
+ dx = p.x;
+ dy = p.y;
+ if(d->flag&SHIFT){
+ dx -= d->r.min.x;
+ dy -= d->r.min.y;
+ }
+ g = _getcopygc(f, d, s, &bfunc);
+ if(bfunc == UseCopyArea)
+ XCopyArea(_dpy, (Drawable)s->id, (Drawable)d->id, g,
+ sx, sy, Dx(r), Dy(r), dx, dy);
+ else if(bfunc == UseFillRectangle){
+ XFillRectangle(_dpy, (Drawable)d->id, g,
+ dx, dy, Dx(r), Dy(r));
+ }else{
+ /* bfunc == UseCopyPlane */
+ plane = _ld2dmask[s->ldepth];
+ plane &= ~(plane>>1);
+ if(0/*f == S*/)
+ XCopyPlane(_dpy, (Drawable)s->id, (Drawable)d->id, g,
+ sx, sy, Dx(r), Dy(r), dx, dy, plane);
+ else {
+ /*
+ * CopyPlane can only do func code S,
+ * so copy src rect into a bitmap with the same depth
+ * as the dest, then do the bitblt from the tmp.
+ * This won't recurse again because we only get
+ * UseCopyPlane with differing bitmap depths
+ */
+ btmp = _balloc(Rect(0,0,Dx(r),Dy(r)), d->ldepth);
+ XCopyPlane(_dpy, (Drawable)s->id, (Drawable)btmp->id, …
+ sx, sy, Dx(r), Dy(r), 0, 0, plane);
+ bitblt(d, p, btmp, btmp->r, f);
+ bfree(btmp);
+ }
+ }
+ XFlush(_dpy);
+}
diff --git a/libXg/bitbltclip.c b/libXg/bitbltclip.c
@@ -0,0 +1,68 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+int
+bitbltclip(void *vp)
+{
+ int dx, dy;
+ int i;
+ struct bbcarg{
+ Bitmap *dm;
+ Point p;
+ Bitmap *sm;
+ Rectangle r;
+ Fcode f;
+ }*bp;
+
+ bp = (struct bbcarg *)vp;
+ dx = Dx(bp->r);
+ dy = Dy(bp->r);
+ if(bp->p.x < bp->dm->clipr.min.x){
+ i = bp->dm->clipr.min.x-bp->p.x;
+ bp->r.min.x += i;
+ bp->p.x += i;
+ dx -= i;
+ }
+ if(bp->p.y < bp->dm->clipr.min.y){
+ i = bp->dm->clipr.min.y-bp->p.y;
+ bp->r.min.y += i;
+ bp->p.y += i;
+ dy -= i;
+ }
+ if(bp->p.x+dx > bp->dm->clipr.max.x){
+ i = bp->p.x+dx-bp->dm->clipr.max.x;
+ bp->r.max.x -= i;
+ dx -= i;
+ }
+ if(bp->p.y+dy > bp->dm->clipr.max.y){
+ i = bp->p.y+dy-bp->dm->clipr.max.y;
+ bp->r.max.y -= i;
+ dy -= i;
+ }
+ if(bp->r.min.x < bp->sm->clipr.min.x){
+ i = bp->sm->clipr.min.x-bp->r.min.x;
+ bp->p.x += i;
+ bp->r.min.x += i;
+ dx -= i;
+ }
+ if(bp->r.min.y < bp->sm->clipr.min.y){
+ i = bp->sm->clipr.min.y-bp->r.min.y;
+ bp->p.y += i;
+ bp->r.min.y += i;
+ dy -= i;
+ }
+ if(bp->r.max.x > bp->sm->clipr.max.x){
+ i = bp->r.max.x-bp->sm->clipr.max.x;
+ bp->r.max.x -= i;
+ dx -= i;
+ }
+ if(bp->r.max.y > bp->sm->clipr.max.y){
+ i = bp->r.max.y-bp->sm->clipr.max.y;
+ bp->r.max.y -= i;
+ dy -= i;
+ }
+ return dx>0 && dy>0;
+}
diff --git a/libXg/border.c b/libXg/border.c
@@ -0,0 +1,28 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+
+void
+border(Bitmap *l, Rectangle r, int i, Fcode c)
+{
+ if(i > 0){
+ bitblt(l, r.min,
+ l, Rect(r.min.x, r.min.y, r.max.x, r.min.y+i), c);
+ bitblt(l, Pt(r.min.x, r.max.y-i),
+ l, Rect(r.min.x, r.max.y-i, r.max.x, r.max.y), c);
+ bitblt(l, Pt(r.min.x, r.min.y+i),
+ l, Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i), c);
+ bitblt(l, Pt(r.max.x-i, r.min.y+i),
+ l, Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i), c);
+ }else if(i < 0){
+ bitblt(l, Pt(r.min.x, r.min.y+i),
+ l, Rect(r.min.x, r.min.y+i, r.max.x, r.min.y), c);
+ bitblt(l, Pt(r.min.x, r.max.y),
+ l, Rect(r.min.x, r.max.y, r.max.x, r.max.y-i), c);
+ bitblt(l, Pt(r.min.x+i, r.min.y+i),
+ l, Rect(r.min.x+i, r.min.y+i, r.min.x, r.max.y-i), c);
+ bitblt(l, Pt(r.max.x, r.min.y+i),
+ l, Rect(r.max.x, r.min.y+i, r.max.x-i, r.max.y-i), c);
+ }
+}
diff --git a/libXg/bscreenrect.c b/libXg/bscreenrect.c
@@ -0,0 +1,18 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/*
+ * The screen data structure should always be up to date
+ * (Not true in the Plan 9 library, which is why this
+ * function exists).
+ */
+Rectangle
+bscreenrect(Rectangle *clipr)
+{
+ if(clipr)
+ *clipr = screen.clipr;
+ return screen.r;
+}
diff --git a/libXg/circle.c b/libXg/circle.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+circle(Bitmap *b, Point p, int r, int v, Fcode f)
+{
+ unsigned int d;
+ int x, y;
+ GC g;
+
+ x = p.x - r;
+ y = p.y - r;
+ if (b->flag&SHIFT){
+ x -= b->r.min.x;
+ y -= b->r.min.y;
+ }
+ d = 2*r;
+ g = _getfillgc(f, b, v);
+ XDrawArc(_dpy, (Drawable)b->id, g, x, y, d, d, 0, 23040/* 360 deg */);
+}
diff --git a/libXg/clipline.c b/libXg/clipline.c
@@ -0,0 +1,225 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+typedef struct Linedesc
+{
+ int x0;
+ int y0;
+ char xmajor;
+ char slopeneg;
+ long dminor;
+ long dmajor;
+} Linedesc;
+
+int _clipline(Rectangle, Point*, Point*, Linedesc*);
+
+#define XYswap(p) t=(p)->x, (p)->x=(p)->y, (p)->y=t
+#define Swap(x, y) t=x, x=y, y=t
+
+static long
+lfloor(long x, long y) /* first integer <= x/y */
+{
+ if(y <= 0){
+ if(y == 0)
+ return x;
+ y = -y;
+ x = -x;
+ }
+ if(x < 0){ /* be careful; C div. is undefined */
+ x = -x;
+ x += y-1;
+ return -(x/y);
+ }
+ return x/y;
+}
+
+static long
+lceil(long x, long y) /* first integer >= x/y */
+{
+ if(y <= 0){
+ if(y == 0)
+ return x;
+ y = -y;
+ x = -x;
+ }
+ if(x < 0){
+ x = -x;
+ return -(x/y);
+ }
+ x += y-1;
+ return x/y;
+}
+
+int
+_gminor(long x, Linedesc *l)
+{
+ long y;
+
+ y = 2*(x-l->x0)*l->dminor + l->dmajor;
+ y = lfloor(y, 2*l->dmajor) + l->y0;
+ return l->slopeneg? -y : y;
+}
+
+int
+_gmajor(long y, Linedesc *l)
+{
+ long x;
+
+ x = 2*((l->slopeneg? -y : y)-l->y0)*l->dmajor - l->dminor;
+ x = lceil(x, 2*l->dminor) + l->x0;
+ if(l->dminor)
+ while(_gminor(x-1, l) == y)
+ x--;
+ return x;
+}
+
+void
+gsetline(Point *pp0, Point *pp1, Linedesc *l)
+{
+ long dx, dy, t;
+ Point endpt;
+ int swapped;
+ Point p0, p1;
+
+ swapped = 0;
+ p0 = *pp0;
+ p1 = *pp1;
+ l->xmajor = 1;
+ l->slopeneg = 0;
+ dx = p1.x - p0.x;
+ dy = p1.y - p0.y;
+ if(abs(dy) > abs(dx)){ /* Steep */
+ l->xmajor = 0;
+ XYswap(&p0);
+ XYswap(&p1);
+ Swap(dx, dy);
+ }
+ if(dx < 0){
+ swapped++;
+ Swap(p0.x, p1.x);
+ Swap(p0.y, p1.y);
+ dx = -dx;
+ dy = -dy;
+ }
+ if(dy < 0){
+ l->slopeneg = 1;
+ dy = -dy;
+ p0.y = -p0.y;
+ }
+ l->dminor = dy;
+ l->dmajor = dx;
+ l->x0 = p0.x;
+ l->y0 = p0.y;
+ p1.x = swapped? p0.x+1 : p1.x-1;
+ p1.y = _gminor(p1.x, l);
+ if(l->xmajor == 0){
+ XYswap(&p0);
+ XYswap(&p1);
+ }
+ if(pp0->x > pp1->x){
+ *pp1 = *pp0;
+ *pp0 = p1;
+ }else
+ *pp1 = p1;
+}
+/*
+ * Modified clip-to-rectangle algorithm
+ * works in bitmaps
+ * Everything in SCREEN coordinates.
+ *
+ * Newman & Sproull 124 (1st edition)
+ */
+
+static
+code(Point *p, Rectangle *r)
+{
+ return( (p->x<r->min.x? 1 : p->x>=r->max.x? 2 : 0) |
+ (p->y<r->min.y? 4 : p->y>=r->max.y? 8 : 0));
+}
+
+int
+clipline(Rectangle r, Point *p0, Point *p1)
+{
+ Linedesc l;
+
+ return _clipline(r, p0, p1, &l);
+}
+
+int
+_clipline(Rectangle r, Point *p0, Point *p1, Linedesc *l)
+{
+ int c0, c1, n;
+ long t, ret;
+ Point temp;
+ int swapped;
+
+ if(p0->x==p1->x && p0->y==p1->y)
+ return 0;
+ gsetline(p0, p1, l);
+ /* line is now closed */
+ if(l->xmajor == 0){
+ XYswap(p0);
+ XYswap(p1);
+ XYswap(&r.min);
+ XYswap(&r.max);
+ }
+ c0 = code(p0, &r);
+ c1 = code(p1, &r);
+ ret = 1;
+ swapped = 0;
+ n = 0;
+ while(c0 | c1){
+ if(c0 & c1){ /* no point of line in r */
+ ret = 0;
+ goto Return;
+ }
+ if(++n > 10){ /* horrible points; overflow etc. etc. */
+ ret = 0;
+ goto Return;
+ }
+ if(c0 == 0){ /* swap points */
+ temp = *p0;
+ *p0 = *p1;
+ *p1 = temp;
+ Swap(c0, c1);
+ swapped ^= 1;
+ }
+ if(c0 == 0)
+ break;
+ if(c0 & 1){ /* push towards left edge */
+ p0->x = r.min.x;
+ p0->y = _gminor(p0->x, l);
+ }else if(c0 & 2){ /* push towards right edge */
+ p0->x = r.max.x-1;
+ p0->y = _gminor(p0->x, l);
+ }else if(c0 & 4){ /* push towards top edge */
+ p0->y = r.min.y;
+ if(l->slopeneg)
+ p0->x = _gmajor(p0->y-1, l)-1;
+ else
+ p0->x = _gmajor(p0->y, l);
+ }else if(c0 & 8){ /* push towards bottom edge */
+ p0->y = r.max.y-1;
+ if(l->slopeneg)
+ p0->x = _gmajor(p0->y, l);
+ else
+ p0->x = _gmajor(p0->y+1, l)-1;
+ }
+ c0 = code(p0, &r);
+ }
+
+ Return:
+ if(l->xmajor == 0){
+ XYswap(p0);
+ XYswap(p1);
+ }
+ if(swapped){
+ temp = *p0;
+ *p0 = *p1;
+ *p1 = temp;
+ }
+ return ret;
+}
diff --git a/libXg/clipr.c b/libXg/clipr.c
@@ -0,0 +1,21 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+int
+clipr(Bitmap *d, Rectangle r)
+{
+ if(rectclip(&r, d->r) == 0)
+ return 0;
+ d->clipr = r;
+ if(r.min.x != d->r.min.x ||
+ r.min.y != d->r.min.y ||
+ r.max.x != d->r.max.x ||
+ r.max.y != d->r.max.y)
+ d->flag |= CLIP;
+ else
+ d->flag &= ~CLIP;
+ return 1;
+}
diff --git a/libXg/copymasked.c b/libXg/copymasked.c
@@ -0,0 +1,49 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/*
+ * m should be a 1-bit-deep bitmap with origin (0,0) and the
+ * same extents as r. s should have the same depth as d.
+ * Rectangle r of s is copied to d wherever corresponding
+ * bits of m are 1
+ */
+void
+copymasked(Bitmap *d, Point p, Bitmap *s, Bitmap *m, Rectangle r)
+{
+ int sx, sy, dx, dy;
+ XGCValues gcv;
+ GC g;
+
+ if(Dx(r)<=0 || Dy(r)<=0)
+ return;
+ sx = r.min.x;
+ sy = r.min.y;
+ if(s->flag&SHIFT){
+ sx -= s->r.min.x;
+ sy -= s->r.min.y;
+ }
+ dx = p.x;
+ dy = p.y;
+ if(d->flag&SHIFT){
+ dx -= d->r.min.x;
+ dy -= d->r.min.y;
+ }
+ gcv.fill_style = FillStippled;
+ gcv.stipple = (Pixmap)m->id;
+ gcv.function = GXclear;
+ gcv.ts_x_origin = dx;
+ gcv.ts_y_origin = dy;
+ gcv.fill_style = FillStippled;
+ g = _getgc(d, GCFunction|GCStipple|GCTileStipXOrigin
+ |GCTileStipYOrigin|GCFillStyle, &gcv);
+ XFillRectangle(_dpy, (Drawable)d->id, g,
+ dx, dy, Dx(r), Dy(r));
+ gcv.function = GXor;
+ gcv.fill_style = FillSolid;
+ g = _getgc(d, GCFunction|GCFillStyle, &gcv);
+ XCopyArea(_dpy, (Drawable)s->id, (Drawable)d->id, g,
+ sx, sy, Dx(r), Dy(r), dx, dy);
+}
diff --git a/libXg/cursorset.c b/libXg/cursorset.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/*
+ * Only allow cursor to move within screen Bitmap
+ */
+void
+cursorset(Point p)
+{
+ /* motion will be relative to window origin */
+ p = sub(p, screen.r.min);
+ XWarpPointer(_dpy, None, (Window)screen.id, 0, 0, 0, 0, p.x, p.y);
+}
diff --git a/libXg/cursorswitch.c b/libXg/cursorswitch.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/*
+ * Use the id field in Cursor to hold the X id corresponding
+ * to the cursor, so that it doesn't have to be recreated on
+ * each cursorswitch. This doesn't quite match the semantics
+ * of Plan9 libg, since the user could create a cursor (say
+ * with malloc) with garbage in the id field; or the user
+ * could change the contents of the other fields and we
+ * wouldn't know about it. Neither of these happen in
+ * existing uses of libg.
+ */
+static Cursor arrow =
+{
+ {-1, -1},
+ {0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xC0, 0xFF, 0x00,
+ 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xE0,
+ 0xE7, 0xF0, 0xE3, 0xF8, 0xC1, 0xFC, 0x00, 0xFE,
+ 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08,
+ },
+ {0x00, 0x00, 0x7F, 0xC0, 0x7F, 0x00, 0x7C, 0x00,
+ 0x7E, 0x00, 0x7F, 0x00, 0x6F, 0x80, 0x67, 0xC0,
+ 0x43, 0xE0, 0x41, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
+ 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00,
+ }
+};
+
+static Bitmap *bsrc, *bmask;
+static Rectangle crect = { 0, 0, 16, 16 };
+
+void
+cursorswitch(Cursor *c)
+{
+ if(c == 0)
+ c = &arrow;
+ if(c->id == 0){
+ if(bsrc == 0){
+ bsrc = balloc(crect, 0);
+ bmask = balloc(crect, 0);
+ }
+ /*
+ * Cursor should have fg where "set" is 1,
+ * and bg where "clr" is 1 and "set" is 0,
+ * and should leave places alone where "set" and "clr" are bot…
+ */
+ wrbitmap(bsrc, 0, 16, c->set);
+#ifdef CURSORBUG
+ /*
+ * Some X servers (e.g., Sun X-on-news for some color
+ * monitors) don't do XCreatePixmapCursor properly:
+ * only the mask gets displayed, all black
+ */
+ wrbitmap(bmask, 0, 16, c->set);
+#else
+ wrbitmap(bmask, 0, 16, c->clr);
+ bitblt(bmask, Pt(0,0), bsrc, crect, S|D);
+#endif
+ c->id = (int) XCreatePixmapCursor(_dpy, (Pixmap)bsrc->id, (Pix…
+ &_fgcolor, &_bgcolor, -c->offset.x, -c->offset.y);
+ }
+ XDefineCursor(_dpy, (Window)screen.id, (xCursor)c->id);
+}
diff --git a/libXg/disc.c b/libXg/disc.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+disc(Bitmap *b, Point p, int r, int v, Fcode f)
+{
+ unsigned int d;
+ int x, y;
+ GC g;
+
+ x = p.x - r;
+ y = p.y - r;
+ if (b->flag&SHIFT){
+ x -= b->r.min.x;
+ y -= b->r.min.y;
+ }
+ d = 2*r;
+ g = _getfillgc(f, b, v);
+ XFillArc(_dpy, (Drawable)b->id, g, x, y, d, d, 0, 23040/* 360 deg */);
+}
diff --git a/libXg/ellipse.c b/libXg/ellipse.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
+
+void
+ellipse(Bitmap *bp, Point p, int a, int b, int v, Fcode f)
+{
+ int x, y;
+ GC g;
+
+ x = p.x - a;
+ y = p.y - b;
+ if (bp->flag&SHIFT){
+ x -= bp->r.min.x;
+ y -= bp->r.min.y;
+ }
+ g = _getfillgc(f, bp, v);
+ XDrawArc(_dpy, (Drawable)bp->id, g, x, y, 2*a, 2*b, 0, 23040/* 360 deg…
+}
diff --git a/libXg/font.c b/libXg/font.c
@@ -0,0 +1,18 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+#define PJW 0 /* use NUL==pjw for invisible characters */
+
+long
+charwidth(XftFont *f, Rune r)
+{
+
+ char chars[UTFmax + 1] = {0};
+
+ runetochar(chars, &r);
+ return strwidth(f, chars);
+}
+
diff --git a/libXg/gcs.c b/libXg/gcs.c
@@ -0,0 +1,401 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+/*
+ * Libg applications are written assuming that black is ~0
+ * and white is 0. Some screens use the reverse convention.
+ * We get the effect the application desired by seeing what
+ * happens if both the source and dest are converted to the
+ * black==~0 convention, and then converting the dest back
+ * to whatever convention it uses.
+ *
+ * Offscreen bitmaps of depth 1 use the black==~0 convention.
+ *
+ * Bitmaps of depth > 1 are probably in color. Libg operations that
+ * would produce a 1 should produce the foreground color, and
+ * libg operations that would produce a 0 should produce the background
+ * color. Operations that use bitmaps of depth > 1 as source
+ * should interpret the foreground pixel as "black" (1) and the
+ * background pixel as "white" (0). It is hard to make this work,
+ * but the important cases are Fcodes Zero, F, S, and S^D, so
+ * we make sure that those work. When a fill value is given for
+ * a bitmap of depth > 1, assume ~0 means foreground, but otherwise
+ * take any other value literally (assume it came from rgbpix).
+ * This may be wrong for the case of 0, but libg programmers
+ * usually use Fcode Zero instead of passing 0 with Fcode S.
+ *
+ * We assume there are at most two depths of bitmaps: depth 1
+ * and depth of the screen.
+ */
+
+/*
+ * gx func code corresponding to libg func code when both
+ * source and dest use 1 for black. This is a straight translation.
+ */
+static int gx[16] = {
+ GXclear, /* Zero */
+ GXnor, /* DnorS */
+ GXandInverted, /* DandnotS */
+ GXcopyInverted, /* notS */
+ GXandReverse, /* notDandS */
+ GXinvert, /* notD */
+ GXxor, /* DxorS */
+ GXnand, /* DnandS */
+ GXand, /* DandS */
+ GXequiv, /* DxnorS */
+ GXnoop, /* D */
+ GXorInverted, /* DornotS */
+ GXcopy, /* S */
+ GXorReverse, /* notDorS */
+ GXor, /* DorS */
+ GXset, /* F */
+};
+
+/*
+ * gx func code corresponding to libg func code when 0 means black
+ * in dst and 1 means black in src. These means the table has op'
+ * where dst <- dst op' src == not ( not(dst) op src ).
+ * The comment on each line is op, in Fcode terms.
+ */
+static int d0s1gx[16] = {
+ GXset, /* Zero */
+ GXorReverse, /* DnorS */
+ GXor, /* DandnotS */
+ GXcopy, /* notS */
+ GXnand, /* notDandS */
+ GXinvert, /* notD */
+ GXxor, /* DxorS */
+ GXandReverse, /* DnandS */
+ GXorInverted, /* DandS */
+ GXequiv, /* DxnorS */
+ GXnoop, /* D */
+ GXand, /* DornotS */
+ GXcopyInverted, /* S */
+ GXnor, /* notDorS */
+ GXandInverted, /* DorS */
+ GXclear, /* F */
+};
+/*
+ * gx func code corresponding to libg func code when 1 means black
+ * in dst and 0 means black in src. These means the table has op'
+ * where dst <- dst op' src == dst op not(src) )
+ * The comment on each line is op, in Fcode terms.
+ */
+static int d1s0gx[16] = {
+ GXclear, /* Zero */
+ GXandReverse, /* DnorS */
+ GXand, /* DandnotS */
+ GXcopy, /* notS */
+ GXnor, /* notDandS */
+ GXinvert, /* notD */
+ GXequiv, /* DxorS */
+ GXorReverse, /* DnandS */
+ GXandInverted, /* DandS */
+ GXxor, /* DxnorS */
+ GXnoop, /* D */
+ GXor, /* DornotS */
+ GXcopyInverted, /* S */
+ GXnand, /* notDorS */
+ GXorInverted, /* DorS */
+ GXset, /* F */
+};
+
+/*
+ * gx func code corresponding to libg func code when 0 means black
+ * in both the src and the dst. These means the table has op'
+ * where dst <- dst op' src == not (not(dst) op not(src)) )
+ * The comment on each line is op, in Fcode terms.
+ */
+static int d0s0gx[16] = {
+ GXset, /* Zero */
+ GXnand, /* DnorS */
+ GXorInverted, /* DandnotS */
+ GXcopyInverted, /* notS */
+ GXorReverse, /* notDandS */
+ GXinvert, /* notD */
+ GXequiv, /* DxorS */
+ GXnor, /* DnandS */
+ GXor, /* DandS */
+ GXxor, /* DxnorS */
+ GXnoop, /* D */
+ GXandInverted, /* DornotS */
+ GXcopy, /* S */
+ GXandReverse, /* notDorS */
+ GXand, /* DorS */
+ GXclear, /* F */
+};
+
+/*
+ * 1 for those Fcodes that are degenerate (don't involve src)
+ */
+static int degengc[16] = {
+ 1, /* Zero */
+ 0, /* DnorS */
+ 0, /* DandnotS */
+ 0, /* notS */
+ 0, /* notDandS */
+ 1, /* notD */
+ 0, /* DxorS */
+ 0, /* DnandS */
+ 0, /* DandS */
+ 0, /* DxnorS */
+ 1, /* D */
+ 0, /* DornotS */
+ 0, /* S */
+ 0, /* notDorS */
+ 0, /* DorS */
+ 1, /* F */
+};
+
+/*
+ * GCs are all for same screen, and depth is either 1 or screen depth.
+ * Return a GC for the depth of b, with values as specified by gcv.
+ *
+ * Also, set (or unset) the clip rectangle if necessary.
+ * (This implementation should be improved if setting a clip rectangle is not …
+ */
+GC
+_getgc(Bitmap *b, unsigned long gcvm, XGCValues *pgcv)
+{
+ static GC gc0, gcn;
+ static clipset = 0;
+ GC g;
+ XRectangle xr;
+
+ g = (b->ldepth==0)? gc0 : gcn;
+ if(!g){
+ g = XCreateGC(_dpy, (Drawable)b->id, gcvm, pgcv);
+ if(b->ldepth==0)
+ gc0 = g;
+ else
+ gcn = g;
+ } else
+ XChangeGC(_dpy, g, gcvm, pgcv);
+ if(b->flag&CLIP){
+ xr.x = b->clipr.min.x;
+ xr.y = b->clipr.min.y;
+ xr.width = Dx(b->clipr);
+ xr.height = Dy(b->clipr);
+ if(b->flag&SHIFT){
+ xr.x -= b->r.min.x;
+ xr.y -= b->r.min.y;
+ }
+ XSetClipRectangles(_dpy, g, 0, 0, &xr, 1, YXBanded);
+ clipset = 1;
+ }else if(clipset){
+ pgcv->clip_mask = None;
+ XChangeGC(_dpy, g, GCClipMask, pgcv);
+ clipset = 0;
+ }
+ return g;
+}
+
+/*
+ * Return a GC that will fill bitmap b using a pixel value v and Fcode f.
+ * Pixel value v is according to libg convention, so 0 means
+ * white (or background) and ~0 means black (or foreground).
+ */
+GC
+_getfillgc(Fcode f, Bitmap *b, unsigned long val)
+{
+ int xf, m;
+ unsigned long v, fg, bg, spix, vmax;
+ XGCValues gcv;
+
+ f &= F;
+ vmax = _ld2dmask[b->ldepth];
+ v = val & vmax;
+ spix = v;
+ xf = GXcopy;
+ m = b->flag;
+ if(m & DP1){
+ xf = (m&BL1)? gx[f] : d0s1gx[f];
+ }else{
+ fg = _fgpixel;
+ bg = _bgpixel;
+ switch(f){
+ case Zero:
+ labZero:
+ spix = bg;
+ break;
+ case F:
+ labF:
+ spix = fg;
+ break;
+ case D:
+ labD:
+ xf = GXnoop;
+ break;
+ case notD:
+ labnotD:
+ xf = GXxor;
+ spix = fg^bg;
+ break;
+ case S:
+ if(val == ~0)
+ spix = fg;
+ else
+ spix = v;
+ break;
+ case notS:
+ if(val == ~0)
+ spix = bg;
+ else
+ spix = v;
+ break;
+ case DxorS:
+ xf = GXxor;
+ if(val == ~0)
+ spix = fg^bg;
+ else
+ spix = v;
+ break;
+ case DxnorS:
+ xf = GXxor;
+ if(val == 0)
+ spix = fg^bg;
+ else
+ spix = v;
+ break;
+ default:
+ /* hard to do anything other than v==0 or v==~0 case */
+ if(v < vmax-v){
+ /* v is closer to 0 than vmax */
+ switch(f&~S){
+ case D&~S: goto labD;
+ case notD&~S: goto labnotD;
+ case Zero&~S: goto labZero;
+ case F&~S: goto labF;
+ }
+ }else{
+ /* v is closer to vmax than 0 */
+ switch(f&S){
+ case D&S: goto labD;
+ case notD&S: goto labnotD;
+ case Zero&S: goto labZero;
+ case F&S: goto labF;
+ }
+ }
+
+ }
+ }
+ gcv.foreground = spix;
+ gcv.function = xf;
+ return _getgc(b, GCForeground|GCFunction, &gcv);
+}
+
+/*
+ * Return a GC to be used to copy an area from bitmap sb to
+ * bitmap db. Sometimes the calling function shouldn't use
+ * XCopyArea, but instead should use XCopyPlane or XFillRectangle.
+ * The *bltfunc arg is set to one of UseCopyArea, UseCopyPlane,
+ * UseFillRectangle.
+ */
+GC
+_getcopygc(Fcode f, Bitmap *db, Bitmap *sb, int *bltfunc)
+{
+ unsigned long spix, bg, fg, df, sf;
+ int xf, c;
+ XGCValues gcv;
+ unsigned long gcvm;
+
+ f &= F;
+ gcvm = 0;
+ df = db->flag;
+ if(degengc[f]){
+ *bltfunc = UseFillRectangle;
+ if(df&SCR || !(df&DP1)){
+ fg = _fgpixel;
+ bg = _bgpixel;
+ }else{
+ /* must be DP1 and BL1 */
+ fg = 1;
+ bg = 0;
+ }
+ switch(f){
+ case Zero:
+ xf = GXcopy;
+ spix = bg;
+ break;
+ case F:
+ xf = GXcopy;
+ spix = fg;
+ break;
+ case D:
+ xf = GXnoop;
+ spix = fg;
+ break;
+ case notD:
+ xf = GXxor;
+ spix = fg^bg;
+ break;
+ }
+ gcv.function = xf;
+ gcv.foreground = spix;
+ gcvm = GCFunction|GCForeground;
+ }else{
+ /* src is involved in f */
+
+#define code(f1,f2) ((((f1)&(DP1|BL1))<<2)|((f2)&(DP1|BL1)))
+
+ sf = sb->flag;
+ c = code(df,sf);
+ *bltfunc = UseCopyArea;
+ switch(code(df,sf)){
+ case code(DP1|BL1,DP1|BL1):
+ case code(BL1,BL1):
+ xf = gx[f];
+ break;
+ case code(DP1|BL1,DP1):
+ xf = d1s0gx[f];
+ break;
+ case code(DP1,DP1|BL1):
+ xf = d0s1gx[f];
+ break;
+ case code(DP1,DP1):
+ case code(0,0):
+ xf = d0s0gx[f];
+ break;
+ default:
+ /*
+ * One bitmap has depth 1, the other has screen depth.
+ * We know the bitmap must have BL1.
+ * CopyPlane must be used; it won't really work
+ * for more than fcode==S.
+ */
+
+ *bltfunc = UseCopyPlane;
+ xf = GXcopy;
+ switch(c){
+
+ case code(0,DP1|BL1):
+ case code(BL1,DP1|BL1):
+ fg = _fgpixel;
+ bg = _bgpixel;
+ break;
+ case code(DP1|BL1,0):
+ fg = 0;
+ bg = 1;
+ break;
+ case code(DP1|BL1,BL1):
+ fg = 1;
+ bg = 0;
+ break;
+ default:
+ berror("bad combination of copy bitmaps");
+ }
+ gcv.foreground = fg;
+ gcv.background = bg;
+ gcvm |= GCForeground|GCBackground;
+ }
+ gcv.function = xf;
+ gcvm |= GCFunction;
+
+#undef code
+ }
+
+ return _getgc(db, gcvm, &gcv);
+}
diff --git a/libXg/getrect.c b/libXg/getrect.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+static Cursor sweep={
+ {-7, -7},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07,
+ 0xE0, 0x07, 0xE0, 0x07, 0xE3, 0xF7, 0xE3, 0xF7,
+ 0xE3, 0xE7, 0xE3, 0xF7, 0xE3, 0xFF, 0xE3, 0x7F,
+ 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,},
+ {0x00, 0x00, 0x7F, 0xFE, 0x40, 0x02, 0x40, 0x02,
+ 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x41, 0xE2,
+ 0x41, 0xC2, 0x41, 0xE2, 0x41, 0x72, 0x40, 0x38,
+ 0x40, 0x1C, 0x40, 0x0E, 0x7F, 0xE6, 0x00, 0x00,}
+};
+
+static void
+grabcursor(void)
+{
+ /* Grab X server with an limp wrist. */
+ while (XGrabPointer(_dpy, screen.id, False,
+ ButtonPressMask|ButtonReleaseMask|
+ ButtonMotionMask|StructureNotifyMask,
+ GrabModeAsync, GrabModeAsync, None, None, CurrentTime)
+ != GrabSuccess)
+ sleep(2);
+ /* Grab the keyboard too */
+ XSetInputFocus(_dpy, screen.id, RevertToParent, CurrentTime);
+}
+
+static void
+ungrabcursor(void)
+{
+ XUngrabPointer(_dpy, CurrentTime);
+}
+
+Rectangle
+getrect(int but, Mouse *m){
+ Rectangle r, rc;
+
+ but = 1<<(but-1);
+ cursorswitch(&sweep);
+ while(m->buttons)
+ *m = emouse();
+ grabcursor();
+ while(!(m->buttons & but)){
+ *m = emouse();
+ if(m->buttons & (7^but))
+ goto Return;
+ }
+ r.min = m->xy;
+ r.max = m->xy;
+ do{
+ rc = rcanon(r);
+ border(&screen, rc, 2, F&~D);
+ *m = emouse();
+ border(&screen, rc, 2, F&~D);
+ r.max = m->xy;
+ }while(m->buttons & but);
+
+ Return:
+ cursorswitch((Cursor *)0);
+ if(m->buttons & (7^but)){
+ rc.min.x = rc.max.x = 0;
+ rc.min.y = rc.max.y = 0;
+ while(m->buttons)
+ *m = emouse();
+ }
+ ungrabcursor();
+ return rc;
+}
diff --git a/libXg/gwin.c b/libXg/gwin.c
@@ -0,0 +1,466 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#if defined(v10) || defined(HPUX)
+typedef char* caddr_t;
+#endif
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+
+#ifndef XtSpecificationRelease
+#define R3
+#define XtPointer caddr_t
+#define XtOffsetOf(s_type,field) XtOffset(s_type*,field)
+#define XtExposeCompressMultiple TRUE
+#endif
+
+#include "GwinP.h"
+
+/* Forward declarations */
+static void Realize(Widget, XtValueMask *, XSetWindowAttributes *);
+static void Resize(Widget);
+static void Redraw(Widget, XEvent *, Region);
+static void Mappingaction(Widget, XEvent *, String *, Cardinal*);
+static void Keyaction(Widget, XEvent *, String *, Cardinal*);
+static void Mouseaction(Widget, XEvent *, String *, Cardinal*);
+static String SelectSwap(Widget, String);
+
+/* Data */
+
+#define Offset(field) XtOffsetOf(GwinRec, gwin.field)
+
+static XtResource resources[] = {
+ {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
+ Offset(foreground), XtRString, (XtPointer)XtDefaultForeground},
+ {XtNscrollForwardR, XtCScrollForwardR, XtRBoolean, sizeof(Boolean),
+ Offset(forwardr), XtRImmediate, (XtPointer)TRUE},
+ {XtNreshaped, XtCReshaped, XtRFunction, sizeof(Reshapefunc),
+ Offset(reshaped), XtRFunction, (XtPointer) NULL},
+ {XtNgotchar, XtCGotchar, XtRFunction, sizeof(Charfunc),
+ Offset(gotchar), XtRFunction, (XtPointer) NULL},
+ {XtNgotmouse, XtCGotmouse, XtRFunction, sizeof(Mousefunc),
+ Offset(gotmouse), XtRFunction, (XtPointer) NULL},
+ {XtNselection, XtCSelection, XtRString, sizeof(String),
+ Offset(selection), XtRString, (XtPointer) NULL},
+ {XtNcomposeMod, XtCComposeMod, XtRInt, sizeof(int),
+ Offset(compose), XtRImmediate, (XtPointer) 0}
+};
+#undef Offset
+
+static XtActionsRec actions[] = {
+ {"key", Keyaction},
+ {"mouse", Mouseaction},
+ {"mapping", Mappingaction}
+};
+
+static char tms[] =
+ "<Key> : key() \n\
+ <Motion> : mouse() \n\
+ <BtnDown> : mouse() \n\
+ <BtnUp> : mouse() \n\
+ <Mapping> : mapping() \n";
+
+/* Class record declaration */
+
+GwinClassRec gwinClassRec = {
+ /* Core class part */
+ {
+ /* superclass */ (WidgetClass)&widgetClassRec,
+ /* class_name */ "Gwin",
+ /* widget_size */ sizeof(GwinRec),
+ /* class_initialize */ NULL,
+ /* class_part_initialize*/ NULL,
+ /* class_inited */ FALSE,
+ /* initialize */ NULL,
+ /* initialize_hook */ NULL,
+ /* realize */ Realize,
+ /* actions */ actions,
+ /* num_actions */ XtNumber(actions),
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ TRUE,
+ /* compress_exposure */ XtExposeCompressMultiple,
+ /* compress_enterleave*/ TRUE,
+ /* visible_interest */ FALSE,
+ /* destroy */ NULL,
+ /* resize */ Resize,
+ /* expose */ Redraw,
+ /* set_values */ NULL,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ XtInheritAcceptFocus,
+ /* version */ XtVersion,
+ /* callback_offsets */ NULL,
+ /* tm_table */ tms,
+ /* query_geometry */ XtInheritQueryGeometry,
+ /* display_accelerator */ NULL,
+ /* extension */ NULL
+ },
+ /* Gwin class part */
+ {
+ /* select_swap */ SelectSwap,
+ }
+};
+
+/* Class record pointer */
+WidgetClass gwinWidgetClass = (WidgetClass) &gwinClassRec;
+
+static XModifierKeymap *modmap;
+static int keypermod;
+
+static void
+Realize(Widget w, XtValueMask *valueMask, XSetWindowAttributes *attrs)
+{
+ XtValueMask mask;
+
+ *valueMask |= CWBackingStore;
+ attrs->backing_store = Always;
+
+ XtCreateWindow(w, InputOutput, (Visual *)0, *valueMask, attrs);
+ XtSetKeyboardFocus(w->core.parent, w);
+ if (modmap = XGetModifierMapping(XtDisplay(w)))
+ keypermod = modmap->max_keypermod;
+
+ Resize(w);
+}
+
+static void
+Resize(Widget w)
+{
+ if(XtIsRealized(w))
+ (*(XtClass(w)->core_class.expose))(w, (XEvent *)NULL, (Region)…
+}
+
+static void
+Redraw(Widget w, XEvent *e, Region r)
+{
+ Reshapefunc f;
+
+ f = ((GwinWidget)w)->gwin.reshaped;
+ if(f)
+ (*f)(w->core.x, w->core.y,
+ w->core.x+w->core.width, w->core.y+w->core.height);
+}
+
+static void
+Mappingaction(Widget w, XEvent *e, String *p, Cardinal *np)
+{
+ if (modmap)
+ XFreeModifiermap(modmap);
+ modmap = XGetModifierMapping(e->xany.display);
+ if (modmap)
+ keypermod = modmap->max_keypermod;
+}
+
+#define STUFFCOMPOSE() \
+ f = ((GwinWidget)w)->gwin.gotchar; \
+ if (f) \
+ for (c = 0; c < composing; c++) \
+ (*f)(compose[c])
+
+static void
+Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
+{
+ static unsigned char compose[5];
+ static int composing = -2;
+
+ int c, minmod;
+ KeySym k, mk;
+ Charfunc f;
+ Modifiers md;
+
+ /*
+ * I tried using XtGetActionKeysym, but it didn't seem to
+ * do case conversion properly
+ * (at least, with Xterminal servers and R4 intrinsics)
+ */
+ if(e->xany.type != KeyPress)
+ return;
+ XtTranslateKeycode(e->xany.display, (KeyCode)e->xkey.keycode,
+ e->xkey.state, &md, &k);
+ /*
+ * The following song and dance is so we can have our chosen
+ * modifier key behave like a compose key, i.e, press and release
+ * and then type the compose sequence, like Plan 9. We have
+ * to find out which key is the compose key first 'though.
+ */
+ if (IsModifierKey(k) && ((GwinWidget)w)->gwin.compose
+ && composing == -2 && modmap) {
+ minmod = (((GwinWidget)w)->gwin.compose+2)*keypermod;
+ for (c = minmod; c < minmod+keypermod; c++) {
+ XtTranslateKeycode(e->xany.display,
+ modmap->modifiermap[c],
+ e->xkey.state, &md, &mk);
+ if (k == mk) {
+ composing = -1;
+ break;
+ }
+ }
+ return;
+ }
+ /* Handle Multi_key separately, since it isn't a modifier */
+ if(k == XK_Multi_key) {
+ composing = -1;
+ return;
+ }
+ if(k == NoSymbol)
+ return;
+ if(k&0xFF00){
+ switch(k){
+ case XK_BackSpace:
+ case XK_Tab:
+ case XK_Escape:
+ case XK_Delete:
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ case XK_KP_Divide:
+ case XK_KP_Multiply:
+ case XK_KP_Subtract:
+ case XK_KP_Add:
+ case XK_KP_Decimal:
+ k &= 0x7F;
+ break;
+ case XK_Linefeed:
+ k = '\r';
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ k = '\n';
+ break;
+ case XK_Left:
+ case XK_Down:
+ case XK_Right:
+ case XK_Next:
+ k = 0x80; /* VIEW -- "Scroll" */
+ break;
+ case XK_Up:
+ case XK_Prior:
+ k = 0x81; /* PREVIEW -- "Scroll back" */
+ break;
+ default:
+ return; /* not ISO-1 or tty control */
+ }
+ }
+ /* Compensate for servers that call a minus a hyphen */
+ if(k == XK_hyphen)
+ k = XK_minus;
+ /* Do control mapping ourselves if translator doesn't */
+ if((e->xkey.state&ControlMask) && !(md&ControlMask))
+ k &= 0x9f;
+ if(k == NoSymbol)
+ return;
+ /* Check to see if we are in a composition sequence */
+ if (!((GwinWidget)w)->gwin.compose && (e->xkey.state & Mod1Mask)
+ && composing == -2)
+ composing = -1;
+ if (composing > -2) {
+ compose[++composing] = k;
+ if ((*compose == 'X') && (composing > 0)) {
+ if ((k < '0') || (k > 'f') ||
+ ((k > '9') && (k < 'a'))) {
+ STUFFCOMPOSE();
+ c = (unsigned short)k;
+ composing = -2;
+ } else if (composing == 4) {
+ c = (int)unicode(compose);
+ if (c == -1) {
+ STUFFCOMPOSE();
+ c = (unsigned short)compose[4];
+ }
+ composing = -2;
+ }
+ } else if (composing == 1) {
+ c = (int)latin1(compose);
+ if (c == -1) {
+ STUFFCOMPOSE();
+ c = (unsigned short)compose[1];
+ }
+ composing = -2;
+ }
+ } else {
+ if (composing >= 0) {
+ composing++;
+ STUFFCOMPOSE();
+ }
+ c = (unsigned short)k;
+ composing = -2;
+ }
+
+ if (composing >= -1)
+ return;
+
+ f = ((GwinWidget)w)->gwin.gotchar;
+ if(f)
+ (*f)(c);
+}
+
+static void
+Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
+{
+ int s;
+ XButtonEvent *be;
+ XMotionEvent *me;
+ Gwinmouse m;
+ Mousefunc f;
+
+ switch(e->type){
+ case ButtonPress:
+ be = (XButtonEvent *)e;
+ m.xy.x = be->x;
+ m.xy.y = be->y;
+ m.msec = be->time;
+ s = be->state; /* the previous state */
+ switch(be->button){
+ case 1: s |= Button1Mask; break;
+ case 2: s |= Button2Mask; break;
+ case 3: s |= Button3Mask; break;
+ case 4: s |= Button4Mask; break;
+ }
+ break;
+ case ButtonRelease:
+ be = (XButtonEvent *)e;
+ m.xy.x = be->x;
+ m.xy.y = be->y;
+ m.msec = be->time;
+ s = be->state;
+ switch(be->button){
+ case 1: s &= ~Button1Mask; break;
+ case 2: s &= ~Button2Mask; break;
+ case 3: s &= ~Button3Mask; break;
+ case 4: s &= ~Button4Mask; break;
+ }
+ break;
+ case MotionNotify:
+ me = (XMotionEvent *)e;
+ s = me->state;
+ m.xy.x = me->x;
+ m.xy.y = me->y;
+ m.msec = me->time;
+ break;
+ default:
+ return;
+ }
+ m.buttons = 0;
+ if(s & Button1Mask) m.buttons |= 1;
+ if(s & Button2Mask) m.buttons |= 2;
+ if(s & Button3Mask) m.buttons |= 4;
+ if(s & Button4Mask) m.buttons |= 8;
+ f = ((GwinWidget)w)->gwin.gotmouse;
+ if(f)
+ (*f)(&m);
+}
+
+static void
+SelCallback(Widget w, XtPointer cldata, Atom *sel, Atom *seltype,
+ XtPointer val, unsigned long *len, int *fmt)
+{
+ String s;
+ int n;
+ GwinWidget gw = (GwinWidget)w;
+
+ if(gw->gwin.selection)
+ XtFree(gw->gwin.selection);
+ if(*seltype != XA_STRING)
+ n = 0;
+ else
+ n = (*len) * (*fmt/8);
+ s = (String)XtMalloc(n+1);
+ if(n > 0)
+ memcpy(s, (char *)val, n);
+ s[n] = 0;
+ gw->gwin.selection = s;
+ XtFree(val);
+}
+
+static Boolean
+SendSel(Widget w, Atom *sel, Atom *target, Atom *rtype, XtPointer *ans,
+ unsigned long *anslen, int *ansfmt)
+{
+ GwinWidget gw = (GwinWidget)w;
+ static Atom targets = 0;
+ XrmValue src, dst;
+ char *s;
+
+ if(*target == XA_STRING){
+ s = gw->gwin.selection;
+ if(!s)
+ s = "";
+ *rtype = XA_STRING;
+ *ans = (XtPointer) XtNewString(s);
+ *anslen = strlen(*ans);
+ *ansfmt = 8;
+ return TRUE;
+ }
+#ifndef R3
+ if(targets == 0){
+ src.addr = "TARGETS";
+ src.size = strlen(src.addr)+1;
+ dst.size = sizeof(Atom);
+ dst.addr = (XtPointer) &targets;
+ XtConvertAndStore(w, XtRString, &src, XtRAtom, &dst);
+ }
+ if(*target == targets){
+ *rtype = XA_ATOM;
+ *ans = (XtPointer) XtNew(Atom);
+ *(Atom*) *ans = XA_STRING;
+ *anslen = 1;
+ *ansfmt = 32;
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+static String
+SelectSwap(Widget w, String s)
+{
+ GwinWidget gw;
+ String ans;
+
+ gw = (GwinWidget)w;
+ if(gw->gwin.selection){
+ XtFree(gw->gwin.selection);
+ gw->gwin.selection = 0;
+ }
+#ifdef R3
+ XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, SelCallback, 0,
+ CurrentTime);
+#else
+ XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, SelCallback, 0,
+ XtLastTimestampProcessed(XtDisplay(w)));
+#endif
+ while(gw->gwin.selection == 0)
+ XtAppProcessEvent(XtWidgetToApplicationContext(w) , XtIMAll);
+ ans = gw->gwin.selection;
+ gw->gwin.selection = XtMalloc(strlen(s)+1);
+ strcpy(gw->gwin.selection, s);
+#ifdef R3
+ XtOwnSelection(w, XA_PRIMARY, CurrentTime, SendSel, NULL, NULL);
+#else
+ XtOwnSelection(w, XA_PRIMARY, XtLastTimestampProcessed(XtDisplay(w)),
+ SendSel, NULL, NULL);
+#endif
+ return ans;
+}
+
+/* The returned answer should be free()ed when no longer needed */
+String
+GwinSelectionSwap(Widget w, String s)
+{
+ XtCheckSubclass(w, gwinWidgetClass, NULL);
+ return (*((GwinWidgetClass) XtClass(w))->gwin_class.select_swap)(w, s);
+}
+
diff --git a/libXg/latin1.c b/libXg/latin1.c
@@ -0,0 +1,313 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+/* Changes copyright 2014-2014 Rob King. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPPING_MAX 65535
+
+struct latin
+{
+ unsigned short l;
+ unsigned char c[2];
+};
+
+struct latin latintab[] = {
+ 0x00a1, '!','!', /* spanish initial ! */
+ 0x00a2, 'c','$', /* cent */
+ 0x00a3, 'l','$', /* pound sterling */
+ 0x00a4, 'g','$', /* general currency */
+ 0x00a5, 'y','$', /* yen */
+ 0x00a6, '|','|', /* broken vertical bar */
+ 0x00a7, 'S','S', /* section symbol */
+ 0x00a8, '\"','\"', /* dieresis */
+ 0x00a9, 'c','O', /* copyright */
+ 0x00aa, 's','a', /* super a, feminine ordinal */
+ 0x00ab, '<','<', /* left angle quotation */
+ 0x00ac, 'n','o', /* not sign, hooked overbar */
+ 0x00ad, '-','-', /* soft hyphen */
+ 0x00ae, 'r','O', /* registered trademark */
+ 0x00af, '_','_', /* macron */
+ 0x00b0, 'd','e', /* degree */
+ 0x00b1, '+','-', /* plus-minus */
+ 0x00b2, 's','2', /* sup 2 */
+ 0x00b3, 's','3', /* sup 3 */
+ 0x00b4, '\'','\'', /* acute accent */
+ 0x00b5, 'm','i', /* micron */
+ 0x00b6, 'p','g', /* paragraph (pilcrow) */
+ 0x00b7, '.','.', /* centered . */
+ 0x00b8, ',',',', /* cedilla */
+ 0x00b9, 's','1', /* sup 1 */
+ 0x00ba, 's','o', /* super o, masculine ordinal */
+ 0x00bb, '>','>', /* right angle quotation */
+ 0x00bc, '1','4', /* 1/4 */
+ 0x00bd, '1','2', /* 1/2 */
+ 0x00be, '3','4', /* 3/4 */
+ 0x00bf, '?','?', /* spanish initial ? */
+ 0x00c0, '`','A', /* A grave */
+ 0x00c1, '\'','A', /* A acute */
+ 0x00c2, '^','A', /* A circumflex */
+ 0x00c3, '~','A', /* A tilde */
+ 0x00c4, '\"','A', /* A dieresis */
+ 0x00c5, 'o','A', /* A circle */
+ 0x00c6, 'A','E', /* AE ligature */
+ 0x00c7, ',','C', /* C cedilla */
+ 0x00c8, '`','E', /* E grave */
+ 0x00c9, '\'','E', /* E acute */
+ 0x00ca, '^','E', /* E circumflex */
+ 0x00cb, '\"','E', /* E dieresis */
+ 0x00cc, '`','I', /* I grave */
+ 0x00cd, '\'','I', /* I acute */
+ 0x00ce, '^','I', /* I circumflex */
+ 0x00cf, '\"','I', /* I dieresis */
+ 0x00d0, 'D','-', /* Eth */
+ 0x00d1, '~','N', /* N tilde */
+ 0x00d2, '`','O', /* O grave */
+ 0x00d3, '\'','O', /* O acute */
+ 0x00d4, '^','O', /* O circumflex */
+ 0x00d5, '~','O', /* O tilde */
+ 0x00d6, '\"','O', /* O dieresis */
+ 0x00d7, 'm','u', /* times sign */
+ 0x00d8, '/','O', /* O slash */
+ 0x00d9, '`','U', /* U grave */
+ 0x00da, '\'','U', /* U acute */
+ 0x00db, '^','U', /* U circumflex */
+ 0x00dc, '\"','U', /* U dieresis */
+ 0x00dd, '\'','Y', /* Y acute */
+ 0x00de, '|','P', /* Thorn */
+ 0x00df, 's','s', /* sharp s */
+ 0x00e0, '`','a', /* a grave */
+ 0x00e1, '\'','a', /* a acute */
+ 0x00e2, '^','a', /* a circumflex */
+ 0x00e3, '~','a', /* a tilde */
+ 0x00e4, '\"','a', /* a dieresis */
+ 0x00e5, 'o','a', /* a circle */
+ 0x00e6, 'a','e', /* ae ligature */
+ 0x00e7, ',','c', /* c cedilla */
+ 0x00e8, '`','e', /* e grave */
+ 0x00e9, '\'','e', /* e acute */
+ 0x00ea, '^','e', /* e circumflex */
+ 0x00eb, '\"','e', /* e dieresis */
+ 0x00ec, '`','i', /* i grave */
+ 0x00ed, '\'','i', /* i acute */
+ 0x00ee, '^','i', /* i circumflex */
+ 0x00ef, '\"','i', /* i dieresis */
+ 0x00f0, 'd','-', /* eth */
+ 0x00f1, '~','n', /* n tilde */
+ 0x00f2, '`','o', /* o grave */
+ 0x00f3, '\'','o', /* o acute */
+ 0x00f4, '^','o', /* o circumflex */
+ 0x00f5, '~','o', /* o tilde */
+ 0x00f6, '\"','o', /* o dieresis */
+ 0x00f7, '-',':', /* divide sign */
+ 0x00f8, '/','o', /* o slash */
+ 0x00f9, '`','u', /* u grave */
+ 0x00fa, '\'','u', /* u acute */
+ 0x00fb, '^','u', /* u circumflex */
+ 0x00fc, '\"','u', /* u dieresis */
+ 0x00fd, '\'','y', /* y acute */
+ 0x00fe, '|','p', /* thorn */
+ 0x00ff, '\"','y', /* y dieresis */
+ 0x2654, 'w','k', /* chess white king */
+ 0x2655, 'w','q', /* chess white queen */
+ 0x2656, 'w','r', /* chess white rook */
+ 0x2657, 'w','b', /* chess white bishop */
+ 0x2658, 'w','n', /* chess white knight */
+ 0x2659, 'w','p', /* chess white pawn */
+ 0x265a, 'b','k', /* chess black king */
+ 0x265b, 'b','q', /* chess black queen */
+ 0x265c, 'b','r', /* chess black rook */
+ 0x265d, 'b','b', /* chess black bishop */
+ 0x265e, 'b','n', /* chess black knight */
+ 0x265f, 'b','p', /* chess black pawn */
+ 0x03b1, '*','a', /* alpha */
+ 0x03b2, '*','b', /* beta */
+ 0x03b3, '*','g', /* gamma */
+ 0x03b4, '*','d', /* delta */
+ 0x03b5, '*','e', /* epsilon */
+ 0x03b6, '*','z', /* zeta */
+ 0x03b7, '*','y', /* eta */
+ 0x03b8, '*','h', /* theta */
+ 0x03b9, '*','i', /* iota */
+ 0x03ba, '*','k', /* kappa */
+ 0x03bb, '*','l', /* lambda */
+ 0x03bc, '*','m', /* mu */
+ 0x03bd, '*','n', /* nu */
+ 0x03be, '*','c', /* xsi */
+ 0x03bf, '*','o', /* omicron */
+ 0x03c0, '*','p', /* pi */
+ 0x03c1, '*','r', /* rho */
+ 0x03c2, 't','s', /* terminal sigma */
+ 0x03c3, '*','s', /* sigma */
+ 0x03c4, '*','t', /* tau */
+ 0x03c5, '*','u', /* upsilon */
+ 0x03c6, '*','f', /* phi */
+ 0x03c7, '*','x', /* chi */
+ 0x03c8, '*','q', /* psi */
+ 0x03c9, '*','w', /* omega */
+ 0x0391, '*','A', /* Alpha */
+ 0x0392, '*','B', /* Beta */
+ 0x0393, '*','G', /* Gamma */
+ 0x0394, '*','D', /* Delta */
+ 0x0395, '*','E', /* Epsilon */
+ 0x0396, '*','Z', /* Zeta */
+ 0x0397, '*','Y', /* Eta */
+ 0x0398, '*','H', /* Theta */
+ 0x0399, '*','I', /* Iota */
+ 0x039a, '*','K', /* Kappa */
+ 0x039b, '*','L', /* Lambda */
+ 0x039c, '*','M', /* Mu */
+ 0x039d, '*','N', /* Nu */
+ 0x039e, '*','C', /* Xsi */
+ 0x039f, '*','O', /* Omicron */
+ 0x03a0, '*','P', /* Pi */
+ 0x03a1, '*','R', /* Rho */
+ 0x03a3, '*','S', /* Sigma */
+ 0x03a4, '*','T', /* Tau */
+ 0x03a5, '*','U', /* Upsilon */
+ 0x03a6, '*','F', /* Phi */
+ 0x03a7, '*','X', /* Chi */
+ 0x03a8, '*','Q', /* Psi */
+ 0x03a9, '*','W', /* Omega */
+ 0x2190, '<','-', /* left arrow */
+ 0x2191, 'u','a', /* up arrow */
+ 0x2192, '-','>', /* right arrow */
+ 0x2193, 'd','a', /* down arrow */
+ 0x2194, 'a','b', /* arrow both */
+ 0x21d0, 'V','=', /* left double-line arrow */
+ 0x21d2, '=','V', /* right double-line arrow */
+ 0x2200, 'f','a', /* forall */
+ 0x2203, 't','e', /* there exists */
+ 0x2202, 'p','d', /* partial differential */
+ 0x2205, 'e','s', /* empty set */
+ 0x2206, 'D','e', /* delta */
+ 0x2207, 'g','r', /* gradient */
+ 0x2208, 'm','o', /* element of */
+ 0x2209, '!','m', /* not element of */
+ 0x220d, 's','t', /* such that */
+ 0x2217, '*','*', /* math asterisk */
+ 0x2219, 'b','u', /* bullet */
+ 0x221a, 's','r', /* radical */
+ 0x221d, 'p','t', /* proportional */
+ 0x221e, 'i','f', /* infinity */
+ 0x2220, 'a','n', /* angle */
+ 0x2227, 'l','&', /* logical and */
+ 0x2228, 'l','|', /* logical or */
+ 0x2229, 'c','a', /* intersection */
+ 0x222a, 'c','u', /* union */
+ 0x222b, 'i','s', /* integral */
+ 0x2234, 't','f', /* therefore */
+ 0x2243, '~','=', /* asymptotically equal */
+ 0x2245, 'c','g', /* congruent */
+ 0x2248, '~','~', /* almost equal */
+ 0x2260, '!','=', /* not equal */
+ 0x2261, '=','=', /* equivalent */
+ 0x2266, '<','=', /* less than or equal */
+ 0x2267, '>','=', /* greater than or equal */
+ 0x2282, 's','b', /* proper subset */
+ 0x2283, 's','p', /* proper superset */
+ 0x2284, '!','b', /* not subset */
+ 0x2286, 'i','b', /* reflexive subset */
+ 0x2287, 'i','p', /* reflexive superset */
+ 0x2295, 'O','+', /* circle plus */
+ 0x2296, 'O','-', /* circle minus */
+ 0x2297, 'O','x', /* circle multiply */
+ 0x22a2, 't','u', /* turnstile */
+ 0x22a8, 'T','u', /* valid */
+ 0x22c4, 'l','z', /* lozenge */
+ 0x22ef, 'e','l', /* ellipses */
+ 0x2639, ':','(', /* saddy */
+ 0x263a, ':',')', /* white-face smiley */
+ 0x263b, ';',')', /* dark-face smiley */
+ 0, 0,
+};
+
+struct latin *mappings = NULL;
+
+void
+freelatin(void)
+{
+ free(mappings);
+}
+
+void
+initlatin(void)
+{
+ FILE *keyboard = NULL;
+ if (getenv("HOME"))
+ {
+ char path[1024] = {0};
+ snprintf(path, 1023, "%s/.keyboard", getenv("HOME"));
+ keyboard = fopen(path, "r");
+ }
+
+ if (!keyboard)
+ {
+ mappings = latintab;
+ return;
+ }
+
+ mappings = calloc(MAPPING_MAX + 1, sizeof(struct latin));
+ if (!mappings)
+ {
+ mappings = latintab;
+ fclose(keyboard);
+ return;
+ }
+
+ int j = 0;
+ while (j < MAPPING_MAX)
+ {
+ int count = fscanf(keyboard, " %c%c %hx%*[^\n]\n", &(mappings[…
+ if (count == 3)
+ {
+ j++;
+
+ }
+ else if (count == EOF)
+ {
+ memset(&(mappings[j]), 0, sizeof(struct latin));
+ break;
+ }
+ else
+ {
+ memset(&(mappings[j]), 0, sizeof(struct latin));
+ }
+ }
+
+ fclose(keyboard);
+ atexit(freelatin);
+}
+
+long
+latin1(unsigned char *k)
+{
+ struct latin *l;
+
+ for(l=mappings; l->l; l++)
+ if(k[0]==l->c[0] && k[1]==l->c[1])
+ return l->l;
+ return -1;
+}
+
+long
+unicode(unsigned char *k)
+{
+ long i, c;
+
+ k++; /* skip 'X' */
+ c = 0;
+ for(i=0; i<4; i++,k++){
+ c <<= 4;
+ if('0'<=*k && *k<='9')
+ c += *k-'0';
+ else if('a'<=*k && *k<='f')
+ c += 10 + *k-'a';
+ else if('A'<=*k && *k<='F')
+ c += 10 + *k-'A';
+ else
+ return -1;
+ }
+ return c;
+}
diff --git a/libXg/ldconvert.c b/libXg/ldconvert.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+_ldconvert(char *in, int inld, char *out, int outld, int w, int h)
+{
+ int a, b, i, j, i1, j1, j2, mask;
+ int ind, inl, outd, outl;
+ int hh, ww;
+ char *p, *q;
+
+ i1 = 8 >> inld;
+ j1 = 8 >> outld;
+ ind = 1 << inld;
+ outd = 1 << outld;
+ inl = ((w << inld) + 7)/8;
+ outl = ((w << outld) + 7)/8;
+ b = 0;
+
+ if (ind > outd) {
+ mask = 256 - (256 >> outd);
+ for (hh = 0; hh < h; hh++, in += inl, out += outl)
+ for (p = in, q = out, ww = 0; ww < w; ww++) {
+ for (j = j1; j > 0; ) {
+ a = *p++;
+ for (i = i1; i > 0; i--, j--) {
+ b |= a & mask;
+ a <<= ind;
+ b <<= outd;
+ }
+ }
+ *q++ = (b >> 8);
+ }
+ } else {
+ j2 = 1 << (outld - inld);
+ mask = 256 - (256 >> ind);
+ for (hh = 0; hh < h; hh++, in += inl, out += outl)
+ for (p = in, q = out, ww = 0; ww < w; ww++) {
+ a = *p++;
+ for (i = i1; i > 0; ) {
+ for (j = j1; j > 0; j--, i--) {
+ b |= a & mask;
+ a <<= ind;
+ b <<= outd;
+ }
+ for (j = j2; j > 0; j--)
+ b |= (b << ind);
+ *q++ = (b >> 8);
+ }
+ }
+ }
+}
diff --git a/libXg/libgint.h b/libXg/libgint.h
@@ -0,0 +1,93 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+/* internal libg implementation file - include after libg */
+
+/*
+ * include defs of standard library routines, if possible,
+ * and string routines
+ */
+#ifdef _POSIX_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#endif /* _POSIX_SOURCE */
+
+/*
+ * use defines to rename X11 types Cursor, Font, Event
+ */
+
+#define Cursor xCursor
+#define Font xFont
+#define Event xEvent
+
+#if defined(v10) || defined(HPUX)
+typedef char* caddr_t;
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+#undef Cursor
+#undef Font
+#undef Event
+
+/* Return a GCs for solid filling/strings/etc., segments/points, and tiling */
+extern GC _getfillgc(Fcode, Bitmap*, unsigned long);
+extern GC _getcopygc(Fcode, Bitmap*, Bitmap*, int*);
+extern GC _getgc(Bitmap*, unsigned long, XGCValues *);
+
+/* convert between different bitmap depths */
+extern void _ldconvert(char *, int, char *, int, int, int);
+
+/* balloc without zero init (which uses a gc!) */
+extern Bitmap *_balloc(Rectangle, int);
+
+/* X Display for this application's connection */
+extern Display *_dpy;
+
+/* screen depth foreground and background for this application */
+extern unsigned long _fgpixel, _bgpixel;
+extern XColor _fgcolor, _bgcolor;
+
+/* indexed by log depth (0 <= ld <= 5), to give depth and planemask */
+extern int _ld2d[];
+extern unsigned long _ld2dmask[];
+
+/* libg.h defines:
+ * extern Bitmap screen; -- Bitmap for application Window after xbinit()
+ * extern Font *font; -- Font for application default font after xbinit…
+ */
+
+/*
+ * Conventions:
+ * The .id field of a Bitmap is an X Pixmap unless the Bitmap is screen,
+ * in which case it is a Window.
+ * The .id field of a Cursor is set to the X xCursor the first time the
+ * cursor is used.
+ * The .id field of a Font is set to the X xFont.
+ *
+ * Coordinate conventions: libg bitmaps can have non (0,0) origins,
+ * but not X Pixmaps, so we have to subtract the min point of a Bitmap
+ * from coords in the Bitmap before using the point in the corresponding Pix…
+ * The screen Bitmap, however, contains the rectangle in X coords of the
+ * widget in which the application is started, relative to the window.
+ * The origin may or may not be (0,0), but in any case, coordinates should
+ * NOT be translated before using in X calls on the Window.
+ */
+
+/* values for bitmap flag field (see _getcopygc if change first two vals) */
+enum {
+ DP1= 0x1, /* depth == 1 (ldepth == 0) */
+ BL1= 0x2, /* black == 1 model */
+ SCR= 0x4, /* on screen */
+ ZORG= 0x8, /* r.min == Pt(0,0) */
+ SHIFT= 0x20, /* !SCR & !ZORG */
+ CLIP= 0x40 /* r != clipr */
+};
+
+/* values for return bltfunc arg of _getcopygc */
+enum {
+ UseCopyArea,
+ UseCopyPlane,
+ UseFillRectangle
+};
diff --git a/libXg/menuhit.c b/libXg/menuhit.c
@@ -0,0 +1,236 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+enum
+{
+ Margin = 3, /* outside to text */
+ Border = 2, /* outside to selection boxes */
+ Blackborder = 1, /* width of outlining border */
+ Vspacing = 1, /* extra spacing between lines of text */
+ Maxunscroll = 25, /* maximum #entries before scrolling turns on…
+ Nscroll = 20, /* number entries in scrolling part */
+ Scrollwid = 14, /* width of scroll bar */
+ Gap = 4 /* between text and scroll bar */
+};
+
+static Bitmap *menutxt;
+
+static uchar menutxtbits[] = {
+ 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
+ 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
+ 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
+ 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
+};
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the rectangle, including its black edge, holding element i.
+ */
+static Rectangle
+menurect(Rectangle r, int i)
+{
+ if(i < 0)
+ return Rect(0, 0, 0, 0);
+ r.min.y += (font->height+Vspacing)*i;
+ r.max.y = r.min.y+font->height+Vspacing;
+ return inset(r, Border-Margin);
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the element number containing p.
+ */
+static int
+menusel(Rectangle r, Point p)
+{
+ if(!ptinrect(p, r))
+ return -1;
+ return (p.y-r.min.y)/(font->height+Vspacing);
+}
+
+/*
+ * menur is a rectangle holding all the highlightable text elements.
+ * track mouse while inside the box, return what's selected when button
+ * is raised, -1 as soon as it leaves box.
+ * invariant: nothing is highlighted on entry or exit.
+ */
+static int
+menuscan(int but, Mouse *m, Rectangle menur, int lasti)
+{
+ int i;
+ Rectangle r;
+
+ r = menurect(menur, lasti);
+ bitblt(&screen, r.min, &screen, r, F&~D);
+ *m = emouse();
+ while(m->buttons & (1<<(but-1))){
+ *m = emouse();
+ i = menusel(menur, m->xy);
+ if(i == lasti)
+ continue;
+ bitblt(&screen, r.min, &screen, r, F&~D);
+ if(i == -1)
+ return i;
+ r = menurect(menur, i);
+ bitblt(&screen, r.min, &screen, r, F&~D);
+ lasti = i;
+ }
+ return lasti;
+}
+
+void
+menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
+{
+ int i;
+ Point pt;
+ Rectangle r;
+ char *item;
+
+ r = inset(textr, Border-Margin);
+ bitblt(&screen, r.min, &screen, r, 0);
+ pt = Pt(textr.min.x+textr.max.x, textr.min.y);
+ for(i = 0; i<nitemdrawn; i++, pt.y += font->height+Vspacing){
+ item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
+ string(&screen,
+ Pt((pt.x-strwidth(font, item))/2, pt.y),
+ font, item, S);
+ }
+}
+
+static void
+menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
+{
+ Rectangle r;
+
+ bitblt(&screen, scrollr.min, &screen, scrollr, 0);
+ r.min.x = scrollr.min.x;
+ r.max.x = scrollr.max.x;
+ r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
+ r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
+ if(r.max.y < r.min.y+2)
+ r.max.y = r.min.y+2;
+ border(&screen, r, 1, F);
+ if(menutxt == 0){
+ menutxt = balloc(Rect(0, 0, 16, 16), 0);
+ if(menutxt)
+ wrbitmap(menutxt, 0, 16, menutxtbits);
+ }
+ if(menutxt)
+ texture(&screen, inset(r, 1), menutxt, S);
+}
+
+int
+menuhit(int but, Mouse *m, Menu *menu)
+{
+ int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
+ int scrolling;
+ Rectangle r, menur, sc, textr, scrollr;
+ Bitmap *b;
+ Point pt;
+ char *item;
+
+ sc = screen.clipr;
+ clipr(&screen, screen.r);
+ maxwid = 0;
+ for(nitem = 0;
+ item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
+ nitem++){
+ i = strwidth(font, item);
+ if(i > maxwid)
+ maxwid = i;
+ }
+ if(menu->lasthit<0 || menu->lasthit>=nitem)
+ menu->lasthit = 0;
+ screenitem = (Dy(screen.r)-10)/(font->height+Vspacing);
+ if(nitem>Maxunscroll || nitem>screenitem){
+ scrolling = 1;
+ nitemdrawn = Nscroll;
+ if(nitemdrawn > screenitem)
+ nitemdrawn = screenitem;
+ wid = maxwid + Gap + Scrollwid;
+ off = menu->lasthit - nitemdrawn/2;
+ if(off < 0)
+ off = 0;
+ if(off > nitem-nitemdrawn)
+ off = nitem-nitemdrawn;
+ lasti = menu->lasthit-off;
+ }else{
+ scrolling = 0;
+ nitemdrawn = nitem;
+ wid = maxwid;
+ off = 0;
+ lasti = menu->lasthit;
+ }
+ r = inset(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin…
+ r = rsubp(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
+ r = raddp(r, m->xy);
+ pt = Pt(0, 0);
+ if(r.max.x>screen.r.max.x)
+ pt.x = screen.r.max.x-r.max.x;
+ if(r.max.y>screen.r.max.y)
+ pt.y = screen.r.max.y-r.max.y;
+ if(r.min.x<screen.r.min.x)
+ pt.x = screen.r.min.x-r.min.x;
+ if(r.min.y<screen.r.min.y)
+ pt.y = screen.r.min.y-r.min.y;
+ menur = raddp(r, pt);
+ textr.max.x = menur.max.x-Margin;
+ textr.min.x = textr.max.x-maxwid;
+ textr.min.y = menur.min.y+Margin;
+ textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
+ if(scrolling){
+ scrollr = inset(menur, Border);
+ scrollr.max.x = scrollr.min.x+Scrollwid;
+ }else
+ scrollr = Rect(0, 0, 0, 0);
+
+ b = balloc(menur, screen.ldepth);
+ if(b == 0)
+ b = &screen;
+ bitblt(b, menur.min, &screen, menur, S);
+ bitblt(&screen, menur.min, &screen, menur, 0);
+ border(&screen, menur, Blackborder, F);
+ r = menurect(textr, lasti);
+ cursorset(divpt(add(r.min, r.max), 2));
+ menupaint(menu, textr, off, nitemdrawn);
+ if(scrolling)
+ menuscrollpaint(scrollr, off, nitem, nitemdrawn);
+ r = menurect(textr, lasti);
+ cursorset(divpt(add(r.min, r.max), 2));
+ menupaint(menu, textr, off, nitemdrawn);
+ if(scrolling)
+ menuscrollpaint(scrollr, off, nitem, nitemdrawn);
+ while(m->buttons & (1<<(but-1))){
+ lasti = menuscan(but, m, textr, lasti);
+ if(lasti >= 0)
+ break;
+ while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
+ if(scrolling && ptinrect(m->xy, scrollr)){
+ noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scro…
+ noff -= nitemdrawn/2;
+ if(noff < 0)
+ noff = 0;
+ if(noff > nitem-nitemdrawn)
+ noff = nitem-nitemdrawn;
+ if(noff != off){
+ off = noff;
+ menupaint(menu, textr, off, nitemdrawn…
+ menuscrollpaint(scrollr, off, nitem, n…
+ }
+ }
+ *m = emouse();
+ }
+ }
+ bitblt(&screen, menur.min, b, menur, S);
+ if(b != &screen)
+ bfree(b);
+ clipr(&screen, sc);
+ if(lasti >= 0){
+ menu->lasthit = lasti+off;
+ return menu->lasthit;
+ }
+ return -1;
+}
diff --git a/libXg/point.c b/libXg/point.c
@@ -0,0 +1,21 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+point(Bitmap *b, Point p, int v, Fcode f)
+{
+ int x, y;
+ GC g;
+
+ x = p.x;
+ y = p.y;
+ if(b->flag&SHIFT){
+ x -= b->r.min.x;
+ y -= b->r.min.y;
+ }
+ g = _getfillgc(f, b, v);
+ XDrawPoint(_dpy, (Drawable)b->id, g, x, y);
+}
diff --git a/libXg/polysegment.c b/libXg/polysegment.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+polysegment(Bitmap *d, int n, Point *pp, int v, Fcode f)
+{
+ XPoint *xp;
+ int i;
+ GC g;
+
+ if (!(xp = (XPoint *)calloc(n, sizeof(XPoint))))
+ berror("polysegment: could not allocate XPoints");
+ for (i = 0; i < n; i++, pp++)
+ if(d->flag&SHIFT){
+ xp[i].x = pp->x - d->r.min.x;
+ xp[i].y = pp->y - d->r.min.y;
+ } else {
+ xp[i].x = pp->x;
+ xp[i].y = pp->y;
+ }
+ g = _getfillgc(f, d, v);
+ XDrawLines(_dpy, (Drawable)d->id, g, xp, n, CoordModeOrigin);
+ free(xp);
+}
diff --git a/libXg/rdbitmap.c b/libXg/rdbitmap.c
@@ -0,0 +1,63 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+rdbitmap(Bitmap *b, int miny, int maxy, unsigned char *data)
+{
+ XImage *gim, *eim;
+ int x, y, w, h, pix, l, offset, px;
+ int inld, outld;
+ char *tdata;
+
+ /*
+ * The XGetImage returned image may be wrong in a number of ways:
+ * wrong bit order, byte order, bit pad, scanline pad,
+ * and constant shift.
+ * So use a SLOW loop, for now
+ */
+ w = Dx(b->r);
+ h = maxy - miny;
+ outld = b->ldepth;
+ inld = (b->ldepth == 0) ? 0 : screen.ldepth;
+ gim = XGetImage(_dpy, (Drawable)b->id, 0, miny - b->r.min.y,
+ w, h, ~0, ZPixmap);
+ px = 1<<(3-outld); /* pixels per byte */
+ /* set l to number of bytes of data per scan line */
+ if(b->r.min.x >= 0)
+ offset = b->r.min.x % px;
+ else
+ offset = px - b->r.min.x % px;
+ l = (-b->r.min.x+px-1)/px;
+ if(b->r.max.x >= 0)
+ l += (b->r.max.x+px-1)/px;
+ else
+ l -= b->r.max.x/px;
+ l *= h;
+ if(l <= 0)
+ return;
+ tdata = (char *)malloc(l);
+ if (tdata == (char *) 0)
+ berror("rdbitmap malloc");
+ eim = XCreateImage(_dpy, 0, 1 << inld, ZPixmap, 0, tdata,
+ w+offset, h, 8, 0);
+ eim->bitmap_pad = 8;
+ eim->bitmap_bit_order = MSBFirst;
+ eim->byte_order = MSBFirst;
+
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w; x++) {
+ pix = XGetPixel(gim, x, y);
+ XPutPixel(eim, x+offset, y, pix);
+ }
+
+ if (inld == outld)
+ memcpy((char *)data, tdata, l);
+ else
+ _ldconvert(tdata, inld, (char*)data, outld, w, h);
+
+ XDestroyImage(gim);
+ XDestroyImage(eim);
+}
diff --git a/libXg/rdbitmapfile.c b/libXg/rdbitmapfile.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+#define CHUNK 6000
+
+Bitmap*
+rdbitmapfile(int fd)
+{
+ char hdr[5*12+1];
+ unsigned char *data;
+ long dy, px;
+ unsigned long l, t, n;
+ long miny, maxy;
+ Rectangle r;
+ int ld;
+ Bitmap *b;
+
+ if(read(fd, hdr, 5*12)!=5*12)
+ berror("rdbitmapfile read");
+ ld = atoi(hdr+0*12);
+ r.min.x = atoi(hdr+1*12);
+ r.min.y = atoi(hdr+2*12);
+ r.max.x = atoi(hdr+3*12);
+ r.max.y = atoi(hdr+4*12);
+ if(ld<0 || ld>1)
+ berror("rdbitmapfile ldepth");
+ if(r.min.x>r.max.x || r.min.y>r.max.y)
+ berror("rdbitmapfile rectangle");
+
+ miny = r.min.y;
+ maxy = r.max.y;
+ px = 1<<(3-ld); /* pixels per byte */
+ /* set l to number of bytes of data per scan line */
+ if(r.min.x >= 0)
+ l = (r.max.x+px-1)/px - r.min.x/px;
+ else{ /* make positive before divide */
+ t = (-r.min.x)+px-1;
+ t = (t/px)*px;
+ l = (t+r.max.x+px-1)/px;
+ }
+ b = balloc(r, ld);
+ if(b == 0)
+ return 0;
+ data = (unsigned char *)malloc(CHUNK);
+ if(data == 0)
+ berror("rdbitmapfile malloc");
+ while(maxy > miny){
+ dy = maxy - miny;
+ if(dy*l > CHUNK)
+ dy = CHUNK/l;
+ n = dy*l;
+ if(read(fd, data, n) != n){
+ free(data);
+ bfree(b);
+ berror("rdbitmapfile read");
+ }
+ wrbitmap(b, miny, miny+dy, data);
+ miny += dy;
+ }
+ free(data);
+ return b;
+}
diff --git a/libXg/rectclip.c b/libXg/rectclip.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+
+rectclip(Rectangle *rp, Rectangle b) /* first by reference, sec…
+{
+ Rectangle *bp = &b;
+ /*
+ * Expand rectXrect() in line for speed
+ */
+ if((rp->min.x<bp->max.x && bp->min.x<rp->max.x &&
+ rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0)
+ return 0;
+ /* They must overlap */
+ if(rp->min.x < bp->min.x)
+ rp->min.x = bp->min.x;
+ if(rp->min.y < bp->min.y)
+ rp->min.y = bp->min.y;
+ if(rp->max.x > bp->max.x)
+ rp->max.x = bp->max.x;
+ if(rp->max.y > bp->max.y)
+ rp->max.y = bp->max.y;
+ return 1;
+}
diff --git a/libXg/rune.c b/libXg/rune.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <string.h>
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 …
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 …
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 …
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+runenlen(Rune *r, int nrune)
+{
+ int nb, c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c <= Rune1)
+ nb++;
+ else
+ if(c <= Rune2)
+ nb += 2;
+ else
+ nb += 3;
+ }
+ return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(uchar*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
+
+char*
+utfrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ int n;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return 0;
+ if(c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if(r == c)
+ return s;
+ s += n;
+ }
+ return 0;
+}
+
+int
+utflen(char *s)
+{
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+ return 0;
+}
diff --git a/libXg/segment.c b/libXg/segment.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+segment(Bitmap *d, Point p1, Point p2, int v, Fcode f)
+{
+ int x1, y1, x2, y2;
+ GC g;
+
+ x1 = p1.x;
+ y1 = p1.y;
+ x2 = p2.x;
+ y2 = p2.y;
+ if(d->flag&SHIFT){
+ x1 -= d->r.min.x;
+ y1 -= d->r.min.y;
+ x2 -= d->r.min.x;
+ y2 -= d->r.min.y;
+ }
+ g = _getfillgc(f, d, v);
+ XDrawLine(_dpy, (Drawable)d->id, g, x1, y1, x2, y2);
+}
diff --git a/libXg/string.c b/libXg/string.c
@@ -0,0 +1,38 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <string.h>
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+enum { Max = 128 };
+
+Point
+string(Bitmap *b, Point p, XftFont *ft, char *s, Fcode f)
+{
+ size_t length = strlen(s);
+ XGlyphInfo extents = {0};
+ int x = p.x;
+ int y = p.y;
+
+ XftTextExtentsUtf8(_dpy, ft, s, length, &extents);
+
+ x = p.x;
+ y = p.y;
+ if (b->flag & SHIFT){
+ x -= b->r.min.x;
+ y -= b->r.min.y;
+ }
+ y += ft->ascent;
+
+ XftDraw *drawable = XftDrawCreate(_dpy, (Drawable)(b->id), DefaultVisu…
+
+ XftDrawStringUtf8(drawable, &fontcolor, ft, x, y, s, length);
+ XftDrawDestroy(drawable);
+
+ x += extents.xOff;
+
+ p.x = (b->flag & SHIFT) ? x + b->r.min.x : x;
+ p.x = x + b->r.min.x;
+ return p;
+}
diff --git a/libXg/strwidth.c b/libXg/strwidth.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+long
+strwidth(XftFont *f, char *s)
+{
+ XGlyphInfo extents = {0};
+ XftTextExtentsUtf8(_dpy, f, s, strlen(s), &extents);
+
+ return extents.xOff;
+}
+
+Point
+strsize(XftFont *f, char *s)
+{
+ XGlyphInfo extents = {0};
+ XftTextExtentsUtf8(_dpy, f, s, strlen(s), &extents);
+
+ return Pt(strwidth(f, s), extents.yOff);
+}
diff --git a/libXg/test.c b/libXg/test.c
@@ -0,0 +1,237 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <libc.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#include <libg.h>
+#include <stdio.h>
+
+void cont(char *);
+void putstring(char *);
+void colorinit(void);
+void printcolmap(void);
+void invertcolmap(void);
+
+unsigned char arrowset[] =
+ {0x00, 0x00, 0x7F, 0xC0, 0x7F, 0x00, 0x7C, 0x00,
+ 0x7E, 0x00, 0x7F, 0x00, 0x6F, 0x80, 0x67, 0xC0,
+ 0x43, 0xE0, 0x41, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
+ 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00};
+
+char *colors[] = { "Black", "Red", "Green", "Yellow",
+ "Cyan", "Magenta", "Blue", "White" };
+RGB colordefs[] = {
+ { 0,0,0 }, /* black */
+ {0xFFFFFFFF, 0x00000000, 0x00000000}, /* red */
+ {0x00000000, 0xFFFFFFFF, 0x00000000}, /* green */
+ {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000}, /* yellow */
+ {0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, /* cyan */
+ {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF}, /* magenta …
+ {0x00000000, 0x00000000, 0xFFFFFFFF}, /* blue */
+ {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}, /* white */
+};
+#define Ncol (sizeof(colordefs)/sizeof(colordefs[0]))
+unsigned long rgbval[Ncol];
+Bitmap *rgbbitmap[Ncol];
+
+main(int argc, char **argv)
+{
+ Point p1,p2,p3;
+ Mouse m;
+ int r,rx,ry;
+ int n, i;
+ char *m3gen(int);
+ static Menu menu3 = { (char **) 0, m3gen, 0 };
+ char *p, buf[200];
+ Bitmap *bm, *bm2;
+ RGB cmap[256];
+
+ xtbinit(0,0,&argc,argv,0);
+ einit(Ekeyboard|Emouse);
+ p1 = add(screen.r.min, Pt(15,15));
+ p2 = sub(screen.r.max, Pt(15,15));
+ p3 = divpt(add(p1,p2),2);
+ fprintf(stderr, "segment(&screen, (%d,%d), (%d,%d), ~0, S)\n",
+ p1.x,p1.y,p2.x,p2.y);
+ segment(&screen, p1, p2, ~0, S);
+ cont("point");
+ fprintf(stderr, "point(&screen, (%d,%d), ~0, S)\n", p1.x,p1.y);
+ point(&screen, p1, ~0, S);
+ cont("circle");
+ rx = p3.x - p1.x;
+ ry = p3.y - p1.y;
+ r = (rx < ry)? rx : ry;
+ fprintf(stderr, "circle(&screen, (%d,%d), %d, ~0, S)\n",
+ p3.x,p3.y,r);
+ circle(&screen, p3, r, ~0, S);
+ cont("disc");
+ fprintf(stderr, "disc(&screen, (%d,%d), %d, ~0, S)\n",
+ p3.x,p3.y,r);
+ disc(&screen, p3, r, ~0, S);
+ cont("clipped disc");
+ fprintf(stderr, "clipr(&screen, ((%d,%d)(%d,%d))\n",
+ p1.x+30, p1.y+5, p3.x-30, p3.y-5);
+ clipr(&screen, Rect(p1.x+30, p1.y+5, p3.x-30, p3.y-5));
+ fprintf(stderr, "disc(&screen, (%d,%d), %d, ~0, S)\n",
+ p3.x,p3.y,r);
+ disc(&screen, p3, r, ~0, S);
+ clipr(&screen, screen.r);
+ cont("ellipse");
+ fprintf(stderr, "ellipse(&screen, (%d,%d), %d, %d, ~0, S)\n",
+ p3.x,p3.y,r,r/2);
+ ellipse(&screen, p3, r, r/2, ~0, S);
+ cont("arc");
+ fprintf(stderr, "arc(&screen, (%d,%d), (%d,%d), (%d,%d), ~0, S)\n",
+ p3.x,p3.y, p3.x+r,p3.y, p3.x+r/2,p3.x-(int)(r*.866));
+ arc(&screen, p3, Pt(p3.x+r,p3.y), Pt(p3.x+r/2,p3.x-(int)(r*.866)), ~0,…
+ if(screen.ldepth > 1){
+ cont("color");
+ colorinit();
+ p3 = p1;
+ rx *= 2;
+ ry *= 2;
+ for(i = 0; i<Ncol; i++) {
+ texture(&screen, Rpt(p3,add(p3,Pt(rx,ry/Ncol))),
+ rgbbitmap[i], S);
+ string(&screen, add(p3,Pt(15,15)), font, colors[i], Dx…
+ p3.y += ry/Ncol;
+ }
+ printcolmap();
+ cont("invert colmap");
+ invertcolmap();
+ printcolmap();
+ p3 = p1;
+ for(i = 0; i<Ncol; i++) {
+ texture(&screen, Rpt(p3,add(p3,Pt(rx,ry/Ncol))),
+ rgbbitmap[i], S);
+ string(&screen, add(p3,Pt(15,15)), font, colors[i], Dx…
+ p3.y += ry/Ncol;
+ }
+ cont("restore colmap");
+ invertcolmap();
+ }
+ cont("wrbitmap, border, and bitblt(S)");
+ bm = balloc(Rect(0,0,16,16), 0);
+ fprintf(stderr, "border (%d,%d,%d,%d), -2, F)\n",
+ p1.x, p1.y, p1.x+16, p1.y+16);
+ border(&screen, Rpt(p1, add(p1,Pt(16,16))), -2, F);
+ wrbitmap(bm, 0, 16, arrowset);
+ fprintf(stderr, "bitblt(&screen, (%d,%d), bm, (0,0,16,16), S)\n",
+ p1.x,p1.y);
+ bitblt(&screen, p1, bm, Rect(0,0,16,16), S);
+ cont("mouse track (button 1)");
+ do{
+ m = emouse();
+ } while(!(m.buttons&1));
+ fprintf(stderr,"test tracking\n");
+ while(m.buttons&1){
+ point(&screen, m.xy, ~0, S);
+ m = emouse();
+ }
+ cursorswitch(0);
+ cont("menuhit (button 3)");
+ do {
+ do{
+ m = emouse();
+ } while(!(m.buttons&4));
+ n = menuhit(3, &m, &menu3);
+ fprintf(stderr, "button %d\n", n);
+ } while (n != 0);
+ cont("keyboard (end with \\n)");
+ fprintf(stderr, "type something\n");
+ for (p = buf; (*p = ekbd()) != '\n' && *p != '\r'; p++) {
+ fprintf(stderr, "%c", *p);
+ if (*p == '\b')
+ p -= 2;
+ if (p < buf-1)
+ p = buf-1;
+ p[1] = 0;
+ putstring(buf);
+ }
+ cont("done");
+ exit(0);
+}
+
+void colorinit(void) /* set up color definitions */
+{
+ int i;
+
+ for (i = 0; i < Ncol; i++) {
+ rgbval[i] = rgbpix(&screen, colordefs[i]);
+ rgbbitmap[i] = balloc(Rect(0,0,1,1), screen.ldepth);
+ point(rgbbitmap[i], Pt(0,0), rgbval[i], S);
+ }
+}
+
+void printcolmap(void)
+{
+ int i, n;
+ RGB cmap[256];
+
+ rdcolmap(&screen, cmap);
+ n = 1 << (1 << screen.ldepth);
+ fprintf(stderr, "colormap, %d entries\n", n);
+ for(i = 0; i < n; i++)
+ fprintf(stderr, "%d:\t%.8x\t%.8x\t%.8x\n",
+ i, cmap[i].red, cmap[i].green, cmap[i].blue);
+}
+
+void invertcolmap(void)
+{
+ int i, n;
+ RGB cmap[256];
+
+ rdcolmap(&screen, cmap);
+ n = 1 << (1 << screen.ldepth);
+ for(i = 0; i < n; i++) {
+ cmap[i].red = ~cmap[i].red;
+ cmap[i].green = ~cmap[i].green;
+ cmap[i].blue = ~cmap[i].blue;
+ }
+ wrcolmap(&screen, cmap);
+}
+
+void
+putstring(char *buf)
+{
+ Point p;
+ static int jmax = 0, l;
+
+ p = add(screen.r.min, Pt(20,20));
+ bitblt(&screen, p, &screen, Rect(p.x, p.y, p.x+jmax, p.y+font->height)…
+ string(&screen, p, font, buf, F);
+ if ((l = strwidth(font, buf)) > jmax)
+ jmax = l;
+}
+
+void
+cont(char *msg)
+{
+ Event ev;
+ Point mp;
+
+ while(event(&ev) != Ekeyboard)
+ continue;
+ bitblt(&screen, Pt(0,0), &screen, screen.r, Zero);
+ mp = add(screen.r.min, Pt(20,20));
+ string(&screen, mp, font, msg, S);
+ while(event(&ev) != Ekeyboard)
+ continue;
+ bitblt(&screen, Pt(0,0), &screen, screen.r, Zero);
+}
+
+char *
+m3gen(int n)
+{
+ static char *m3[] ={ "quit", "thing1", "thing2" };
+
+ if (n < 0 || n > 2)
+ return 0;
+ else
+ return m3[n];
+}
+
+void
+ereshaped(Rectangle r)
+{
+}
diff --git a/libXg/texture.c b/libXg/texture.c
@@ -0,0 +1,42 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+void
+texture(Bitmap *d, Rectangle r, Bitmap *s, Fcode f)
+{
+ int x, y, w, h, bfunc;
+ GC g;
+
+ x = r.min.x;
+ y = r.min.y;
+ if(d->flag&SHIFT){
+ x -= d->r.min.x;
+ y -= d->r.min.y;
+ }
+ g = _getcopygc(f, d, s, &bfunc);
+ if(d->flag&SHIFT){
+ XSetTSOrigin(_dpy, g, -d->r.min.x, -d->r.min.y);
+ }else
+ XSetTSOrigin(_dpy, g, 0, 0);
+ w = Dx(r);
+ h = Dy(r);
+ if(bfunc == UseFillRectangle){
+ /* source isn't involved at all */
+ XFillRectangle(_dpy, (Drawable)d->id, g, x, y, w, h);
+ }else if(bfunc == UseCopyArea){
+ XSetTile(_dpy, g, (Drawable)s->id);
+ XSetFillStyle(_dpy, g, FillTiled);
+ XFillRectangle(_dpy, (Drawable)d->id, g, x, y, w, h);
+ XSetFillStyle(_dpy, g, FillSolid);
+ }else{
+ if(s->ldepth != 0)
+ berror("unsupported texture");
+ XSetStipple(_dpy, g, (Drawable)s->id);
+ XSetFillStyle(_dpy, g, FillOpaqueStippled);
+ XFillRectangle(_dpy, (Drawable)d->id, g, x, y, w, h);
+ XSetFillStyle(_dpy, g, FillSolid);
+ }
+}
diff --git a/libXg/wrbitmap.c b/libXg/wrbitmap.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+#include <X11/Intrinsic.h>
+#ifndef XtSpecificationRelease
+#define R3
+#endif
+
+#include <stdio.h>
+void
+wrbitmap(Bitmap *b, int miny, int maxy, unsigned char *data)
+{
+ XImage *im;
+ int w, h, inld, outld, l, offset, px;
+ GC g;
+ char *tdata;
+
+ w = Dx(b->r);
+ h = maxy - miny;
+ inld = b->ldepth;
+ outld = (b->ldepth == 0) ? 0 : screen.ldepth;
+ px = 1<<(3-outld); /* pixels per byte */
+ /* set l to number of bytes of data per scan line */
+ if(b->r.min.x >= 0)
+ offset = b->r.min.x % px;
+ else
+ offset = px - b->r.min.x % px;
+ l = (-b->r.min.x+px-1)/px;
+ if(b->r.max.x >= 0)
+ l += (b->r.max.x+px-1)/px;
+ else
+ l -= b->r.max.x/px;
+ l *= h;
+
+ tdata = (char *)malloc(l);
+ if (tdata == (char *) 0)
+ berror("wrbitmap malloc");
+ if (inld == outld)
+ memcpy((void*)tdata, (void*)data, l);
+ else
+ _ldconvert((char*)data, inld, tdata, outld, w, h);
+
+ im = XCreateImage(_dpy, 0, 1 << outld, ZPixmap, 0, tdata, w, h, 8, 0);
+
+ /* Botched interface to XCreateImage doesn't let you set these: */
+ im->bitmap_bit_order = MSBFirst;
+ im->byte_order = MSBFirst;
+
+ g = _getfillgc(S, b, ~0);
+ XSetBackground(_dpy, g, b->flag&DP1 ? 0 : _bgpixel);
+ XPutImage(_dpy, (Drawable)b->id, g, im, offset, 0, 0, miny - b->r.min.…
+ XDestroyImage(im);
+}
diff --git a/libXg/wrbitmapfile.c b/libXg/wrbitmapfile.c
@@ -0,0 +1,50 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include "libgint.h"
+
+#define CHUNK 4096
+
+void
+wrbitmapfile(int fd, Bitmap *b)
+{
+ char hdr[5*12+1];
+ unsigned char *data;
+ long dy, px;
+ unsigned long l, t, n;
+ long miny, maxy;
+
+ sprint(hdr, "%11d %11d %11d %11d %11d ",
+ b->ldepth, b->r.min.x, b->r.min.y, b->r.max.x, b->r.max.y);
+ if(write(fd, hdr, 5*12) != 5*12)
+ berror("wrbitmapfile write");
+
+ px = 1<<(3-b->ldepth); /* pixels per byte */
+ /* set l to number of bytes of data per scan line */
+ if(b->r.min.x >= 0)
+ l = (b->r.max.x+px-1)/px - b->r.min.x/px;
+ else{ /* make positive before divide */
+ t = (-b->r.min.x)+px-1;
+ t = (t/px)*px;
+ l = (t+b->r.max.x+px-1)/px;
+ }
+ miny = b->r.min.y;
+ maxy = b->r.max.y;
+ data = (unsigned char *)malloc(CHUNK);
+ if(data == 0)
+ berror("wrbitmapfile malloc");
+ while(maxy > miny){
+ dy = maxy - miny;
+ if(dy*l > CHUNK)
+ dy = CHUNK/l;
+ rdbitmap(b, miny, miny+dy, data);
+ n = dy*l;
+ if(write(fd, data, n) != n){
+ free(data);
+ berror("wrbitmapfile write");
+ }
+ miny += dy;
+ }
+ free(data);
+}
diff --git a/libXg/xtbinit.c b/libXg/xtbinit.c
@@ -0,0 +1,830 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <stdio.h>
+#include "libgint.h"
+
+#define COMPRESSMOUSE
+
+#define Cursor xCursor
+#define Font xFont
+#define Event xEvent
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include "Gwin.h"
+
+#ifndef XtSpecificationRelease
+#define R3
+#define XtAppInitialize(a,b,c,d,e,f,g,h,i) XtInitialize(0,b,c,d,e,f)
+#define XtConvertAndStore(a,b,c,d,e) (XtConvert(a,b,c,d,e),1)
+#define XtAppPending(a) XtPending()
+#define XtAppProcessEvent(a,b) XtProcessEvent(b)
+#define XtAppAddTimeOut(a,b,c,d) XtAddTimeOut(b,c,d)
+#define XtAppAddInput(a,b,c,d,e) XtAddInput(b,c,d,e)
+#define XtPointer caddr_t
+#endif
+
+#undef Cursor
+#undef Font
+#undef Event
+
+/* libg globals */
+Bitmap screen;
+XftFont *font;
+XftColor fontcolor;
+XftColor bgcolor;
+
+/* implementation globals */
+Display *_dpy;
+Widget _toplevel;
+unsigned long _fgpixel, _bgpixel;
+XColor _fgcolor, _bgcolor;
+int _ld2d[6] = { 1, 2, 4, 8, 16, 24 };
+unsigned long _ld2dmask[6] = { 0x1, 0x3, 0xF, 0xFF, 0xFFFF, 0x00FFFFFF …
+Colormap _libg_cmap;
+int _cmap_installed;
+
+/* xbinit implementation globals */
+#ifndef R3
+static XtAppContext app;
+#endif
+static Widget widg;
+static int exposed = 0;
+static Atom wm_take_focus;
+static Mouse lastmouse;
+
+typedef struct Ebuf {
+ struct Ebuf *next;
+ int n;
+ unsigned char buf[2];
+} Ebuf;
+
+typedef struct Esrc {
+ int inuse;
+ int size;
+ int count;
+ Ebuf *head;
+ Ebuf *tail;
+} Esrc;
+
+#define MAXINPUT 1024 /* number of queued input e…
+#define MAXSRC 10
+
+static Esrc esrc[MAXSRC];
+static int nsrc;
+
+
+static int einitcalled = 0;
+static int Smouse = -1;
+static int Skeyboard = -1;
+static int Stimer = -1;
+
+
+static void reshaped(int, int, int, int);
+static void gotchar(int);
+static void gotmouse(Gwinmouse *);
+static int ilog2(int);
+static void pixtocolor(Pixel, XColor *);
+static Ebuf *ebread(Esrc *);
+static Ebuf *ebadd(Esrc *);
+static void focinit(Widget);
+static void wmproto(Widget, XEvent *, String *, Cardinal *);
+static void waitevent(void);
+void initlatin();
+
+static Errfunc onerr;
+
+String _fallbacks[] = {
+ "*gwin.width: 400",
+ "*gwin.height: 400",
+ NULL
+};
+
+#ifndef R3
+static char *shelltrans =
+ "<ClientMessage> WM_PROTOCOLS : WMProtocolAction()";
+static XtActionsRec wmpactions[] = {
+ {"WMProtocolAction", wmproto}
+};
+#endif
+
+ /* too many X options */
+static XrmOptionDescRec optable[] = {
+};
+
+
+
+
+void
+xtbinit(Errfunc f, char *class, int *pargc, char **argv, char **fallbacks)
+{
+ int n;
+ unsigned int depth;
+ Arg args[20];
+ char *p;
+ XSetWindowAttributes attr;
+ int compose;
+
+ initlatin();
+
+ if(!class && argv[0]){
+ p = strrchr(argv[0], '/');
+ if(p)
+ class = XtNewString(p+1);
+ else
+ class = XtNewString(argv[0]);
+ if(class[0] >= 'a' && class[0] <= 'z')
+ class[0] += 'A' - 'a';
+ }
+ onerr = f;
+ if (!fallbacks)
+ fallbacks = _fallbacks;
+ n = 0;
+ XtSetArg(args[n], XtNinput, TRUE); n++;
+
+
+ if (*pargc >= 3 && strcmp(argv[1], "-r") == 0)
+ {
+ char name[512] = {0};
+ snprintf(name, 511, "samterm on %s", argv[2]);
+ XtSetArg(args[n], XtNtitle, XtNewString(name)); n++;
+ XtSetArg(args[n], XtNiconName, XtNewString(name)); n++;
+ }
+ else
+ {
+ XtSetArg(args[n], XtNtitle, XtNewString("samterm on localhost")); n++;…
+ }
+ _toplevel = XtAppInitialize(&app, class,
+ optable, sizeof(optable)/sizeof(optable[0]),
+ pargc, argv, fallbacks, args, n);
+
+
+ n = 0;
+ XtSetArg(args[n], XtNreshaped, reshaped); n++;
+ XtSetArg(args[n], XtNgotchar, gotchar); n++;
+ XtSetArg(args[n], XtNgotmouse, gotmouse); n++;
+ widg = XtCreateManagedWidget("gwin", gwinWidgetClass, _toplevel, args, n);
+
+ _dpy = XtDisplay(widg);
+ XAllocNamedColor(_dpy, DefaultColormap(_dpy, DefaultScreen(_dpy)), getenv(…
+ XAllocNamedColor(_dpy, DefaultColormap(_dpy, DefaultScreen(_dpy)), getenv(…
+
+ n = 0;
+ XtSetArg(args[n], XtNdepth, &depth); n++;
+ XtSetArg(args[n], XtNcomposeMod, &compose); n++;
+ XtGetValues(widg, args, n);
+
+ if (compose < 0 || compose > 5) {
+ n = 0;
+ XtSetArg(args[n], XtNcomposeMod, 0); n++;
+ XtSetValues(widg, args, n);
+ }
+
+ font = XftFontOpenName(_dpy, DefaultScreen(_dpy), getenv("FONT") ? getenv(…
+ screen.id = 0;
+ XtRealizeWidget(_toplevel);
+
+ pid_t pid = getpid();
+ XChangeProperty(_dpy, XtWindow(_toplevel), XInternAtom(_dpy, "_NET_WM_PID"…
+
+ _fgpixel = _fgcolor.pixel;
+ _bgpixel = _bgcolor.pixel;
+
+ XRenderColor xrcolor = {0};
+ xrcolor.red = _fgcolor.red;
+ xrcolor.green = _fgcolor.green;
+ xrcolor.blue = _fgcolor.blue;
+ xrcolor.alpha = 65535;
+ XftColorAllocValue(_dpy, DefaultVisual(_dpy, DefaultScreen(_dpy)), Default…
+
+ xrcolor.red = _bgcolor.red;
+ xrcolor.green = _bgcolor.green;
+ xrcolor.blue = _bgcolor.blue;
+ XftColorAllocValue(_dpy, DefaultVisual(_dpy, DefaultScreen(_dpy)), Default…
+
+ screen.id = (int) XtWindow(widg);
+ screen.ldepth = ilog2(depth);
+ screen.flag = SCR;
+ if(_fgpixel != 0)
+ screen.flag |= BL1;
+ if(depth == 1)
+ screen.flag |= DP1;
+ /* leave screen rect at all zeros until reshaped() sets it */
+ while(!exposed) {
+ XFlush(_dpy);
+ XtAppProcessEvent(app, XtIMXEvent);
+ }
+ XFlush(_dpy);
+ focinit(_toplevel);
+}
+
+static void
+focinit(Widget w)
+{
+#ifndef R3
+ XrmValue src, dst;
+
+ src.addr = "WM_TAKE_FOCUS";
+ src.size = strlen((char *)src.addr)+1;
+ dst.addr = (XtPointer) &wm_take_focus;
+ dst.size = sizeof(Atom);
+ XtConvertAndStore(w, XtRString, &src, XtRAtom, &dst);
+ XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_take_focus, 1);
+ XtAppAddActions(app, wmpactions, XtNumber(wmpactions));
+ XtAugmentTranslations(w, XtParseTranslationTable(shelltrans));
+#endif
+}
+
+#ifndef R3
+static void
+wmproto(Widget w, XEvent *e , String *p, Cardinal *np)
+{
+ Time t;
+
+ if(e->type == ClientMessage &&
+ (Atom)(e->xclient.data.l[0]) == wm_take_focus) {
+ t = (Time) e->xclient.data.l[1];
+ XtCallAcceptFocus(widg, &t);
+ }
+}
+#endif
+
+static void
+reshaped(int minx, int miny, int maxx, int maxy)
+{
+ Ebuf *eb;
+ Mouse m;
+
+ screen.r = Rect(minx, miny, maxx, maxy);
+ screen.clipr = screen.r;
+ if (screen.id) {
+ exposed = 1;
+ ereshaped(screen.r);
+ }
+ if(einitcalled){
+ /*
+ * Cause a mouse event, so programs like sam
+ * will get out of eread and REALLY do the reshape
+ */
+ eb = ebadd(&esrc[Smouse]);
+ if (eb == 0)
+ berror("eballoc can't malloc");
+ memcpy((void*)eb->buf, (void*)&lastmouse, sizeof lastmouse);
+ esrc[Smouse].count++;
+ }
+}
+
+static void
+gotchar(int c)
+{
+ Ebuf *eb;
+
+ if(!einitcalled || Skeyboard == -1)
+ return;
+ eb = ebadd(&esrc[Skeyboard]);
+ if (eb == 0)
+ berror("eballoc can't malloc");
+ BPSHORT(eb->buf, (unsigned short)(c & 0xffff));
+ esrc[Skeyboard].count++;
+}
+
+static void
+gotmouse(Gwinmouse *gm)
+{
+ Ebuf *eb;
+ Mouse m;
+
+ if(!einitcalled || Smouse == -1)
+ return;
+ m.buttons = gm->buttons;
+ m.xy.x = gm->xy.x;
+ m.xy.y = gm->xy.y;
+ m.msec = gm->msec;
+ lastmouse = m;
+ eb = ebadd(&esrc[Smouse]);
+ if (eb == 0)
+ berror("eballoc can't malloc");
+ memcpy((void*)eb->buf, (void*)&m, sizeof m);
+ esrc[Smouse].count++;
+}
+
+static void
+gotinput(XtPointer cldata, int *pfd, XtInputId *id)
+{
+ Ebuf *eb, *lasttail, *newe;
+ Esrc *es;
+ int n;
+
+ if(!einitcalled)
+ return;
+ es = (Esrc *)cldata;
+ if (es->count >= MAXINPUT)
+ return;
+ lasttail = es->tail;
+ eb = ebadd(es);
+ if (eb == 0)
+ return;
+ if(es->size){
+ n = read(*pfd, (char *)eb->buf, es->size);
+ if (n < 0)
+ n = 0;
+ if(n < es->size) {
+ newe = realloc(eb, sizeof(Ebuf)+n);
+ newe->n = n;
+ if (es->head == eb)
+ es->head = newe;
+ else
+ lasttail->next = newe;
+ es->tail = newe;
+ }
+ }
+ es->count++;
+}
+
+static void
+gottimeout(XtPointer cldata, XtIntervalId *id)
+{
+ if(!einitcalled || Stimer == -1)
+ return;
+ /*
+ * Don't queue up timeouts, because there's
+ * too big a danger that they might pile up
+ * too quickly.
+ */
+ esrc[Stimer].head = (Ebuf *)1;
+ esrc[Stimer].count = 1;
+ XtAppAddTimeOut(app, (long)cldata, gottimeout, cldata);
+}
+
+static int
+ilog2(int n)
+{
+ int i, v;
+
+ for(i=0, v=1; i < 6; i++, v<<=1)
+ if(n <= v)
+ break;
+ return i;
+}
+
+static void
+pixtocolor(Pixel p, XColor *pc)
+{
+#ifdef R3
+ Colormap cmap;
+ Arg args[2];
+ int n;
+
+ n = 0;
+ XtSetArg(args[n], XtNcolormap, &cmap); n++;
+ XtGetValues(_toplevel, args, n);
+ pc->pixel = p;
+ XQueryColor(_dpy, cmap, pc);
+#else
+ XrmValue xvf, xvt;
+
+ xvf.size = sizeof(Pixel);
+ xvf.addr = (XtPointer)&p;
+ xvt.size = sizeof(XColor);
+ xvt.addr = (XtPointer)pc;
+ if(!XtConvertAndStore(_toplevel, XtRPixel, &xvf, XtRColor, &xvt))
+ pc->pixel = p; /* maybe that's enough */
+#endif
+}
+
+unsigned long
+rgbpix(Bitmap *b, RGB col)
+{
+ XColor c;
+ Colormap cmap;
+ Arg args[2];
+ int n, depth, dr, dg, db;
+ RGB map[256], *m;
+ unsigned long d, max, pixel;
+
+ if (!_cmap_installed) {
+ n = 0;
+ XtSetArg(args[n], XtNcolormap, &cmap); n++;
+ XtGetValues(_toplevel, args, n);
+ c.red = col.red>>16;
+ c.green = col.green>>16;
+ c.blue = col.blue>>16;
+ c.flags = DoRed|DoGreen|DoBlue;
+ if(XAllocColor(_dpy, cmap, &c))
+ return (unsigned long)(c.pixel);
+ }
+ depth = _ld2d[screen.ldepth];
+ rdcolmap(&screen, map);
+ max = -1;
+ for (n = 0, m = map; n < (1 << depth); n++, m++)
+ {
+ dr = m->red - col.red;
+ dg = m->green - col.green;
+ db = m->blue - col.blue;
+ d = dr*dr+dg*dg+db*db;
+ if (d < max || max == -1)
+ {
+ max = d;
+ pixel = n;
+ }
+ }
+ return pixel;
+}
+
+void
+rdcolmap(Bitmap *b, RGB *map)
+{
+ XColor cols[256];
+ int i, n, depth;
+ Colormap cmap;
+ Arg args[2];
+
+ if (_cmap_installed) {
+ cmap = _libg_cmap;
+ } else {
+ i = 0;
+ XtSetArg(args[i], XtNcolormap, &cmap); i++;
+ XtGetValues(_toplevel, args, i);
+ }
+
+ depth = _ld2d[screen.ldepth];
+ n = 1 << depth;
+ if (depth == 1) {
+ map[0].red = map[0].green = map[0].blue = ~0;
+ map[1].red = map[1].green = map[1].blue = 0;
+ }
+ else {
+ if (n > 256) {
+ berror("rdcolmap bitmap too deep");
+ return;
+ }
+ for (i = 0; i < n; i++)
+ cols[i].pixel = i;
+ XQueryColors(_dpy, cmap, cols, n);
+ for (i = 0; i < n; i++) {
+ map[i].red = (cols[i].red << 16) | cols[i].red;
+ map[i].green = (cols[i].green << 16) | cols[i].green;
+ map[i].blue = (cols[i].blue << 16) | cols[i].blue;
+ }
+ }
+}
+
+void
+wrcolmap(Bitmap *b, RGB *map)
+{
+ int i, n, depth;
+ Screen *scr;
+ XColor cols[256];
+ Arg args[2];
+ XVisualInfo vi;
+ Window w;
+
+ scr = XtScreen(_toplevel);
+ depth = _ld2d[screen.ldepth];
+ n = 1 << depth;
+ if (n > 256) {
+ berror("wrcolmap bitmap too deep");
+ return;
+ } else if (depth > 1) {
+ for (i = 0; i < n; i++) {
+ cols[i].red = map[i].red >> 16;
+ cols[i].green = map[i].green >> 16;
+ cols[i].blue = map[i].blue >> 16;
+ cols[i].pixel = i;
+ cols[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ if (!XMatchVisualInfo(_dpy, XScreenNumberOfScreen(scr),
+ depth, PseudoColor, &vi)) {
+ berror("wrcolmap can't get visual");
+ return;
+ }
+ w = XtWindow(_toplevel);
+ _libg_cmap = XCreateColormap(_dpy, w, vi.visual, AllocAll);
+ XStoreColors(_dpy, _libg_cmap, cols, n);
+
+ i = 0;
+ XtSetArg(args[i], XtNcolormap, _libg_cmap); i++;
+ XtSetValues(_toplevel, args, i);
+ _cmap_installed = 1;
+ }
+}
+
+int
+scrollfwdbut(void)
+{
+ Arg arg;
+ Boolean v;
+ String s;
+
+ XtSetArg(arg, XtNscrollForwardR, &v);
+ XtGetValues(widg, &arg, 1);
+ return v ? 3 : 1;
+}
+
+void
+einit(unsigned long keys)
+{
+ /*
+ * Make sure Smouse = ilog2(Emouse) and Skeyboard == ilog2(Ekeyboard)
+ */
+ nsrc = 0;
+ if(keys&Emouse){
+ Smouse = 0;
+ esrc[Smouse].inuse = 1;
+ esrc[Smouse].size = sizeof(Mouse);
+ esrc[Smouse].count = 0;
+ nsrc = Smouse+1;
+ }
+ if(keys&Ekeyboard){
+ Skeyboard = 1;
+ esrc[Skeyboard].inuse = 1;
+ esrc[Skeyboard].size = 1;
+ esrc[Skeyboard].count = 0;
+ if(Skeyboard >= nsrc)
+ nsrc = Skeyboard+1;
+ }
+ einitcalled = 1;
+}
+
+unsigned long
+estart(unsigned long key, int fd, int n)
+{
+ int i;
+
+ if(fd < 0)
+ berror("bad fd to estart");
+ if(n <= 0 || n > EMAXMSG)
+ n = EMAXMSG;
+ for(i=0; i<MAXSRC; i++)
+ if((key & ~(1<<i)) == 0 && !esrc[i].inuse){
+ if(nsrc <= i)
+ nsrc = i+1;
+ esrc[i].inuse = 1;
+ esrc[i].size = n;
+ esrc[i].count = 0;
+ XtAppAddInput(app, fd, (XtPointer)XtInputReadMask,
+ gotinput, (XtPointer) &esrc[i]);
+ return 1<<i;
+ }
+ return 0;
+}
+
+unsigned long
+etimer(unsigned long key, long n)
+{
+ int i;
+
+ if(Stimer != -1)
+ berror("timer started twice");
+ if(n <= 0)
+ n = 1000;
+ for(i=0; i<MAXSRC; i++)
+ if((key & ~(1<<i)) == 0 && !esrc[i].inuse){
+ if(nsrc <= i)
+ nsrc = i+1;
+ esrc[i].inuse = 1;
+ esrc[i].size = 0;
+ esrc[i].count = 0;
+ XtAppAddTimeOut(app, n, gottimeout, (XtPointer)n);
+ Stimer = i;
+ return 1<<i;
+ }
+ return 0;
+}
+
+unsigned long
+event(Event *e)
+{
+ return eread(~0L, e);
+}
+
+unsigned long
+eread(unsigned long keys, Event *e)
+{
+ Ebuf *eb;
+ int i;
+
+ if(keys == 0)
+ return 0;
+ /* Give Priority to X events */
+ if (XtAppPending(app) & XtIMXEvent)
+ XtAppProcessEvent(app, XtIMXEvent);
+
+ for(;;){
+ for(i=0; i<nsrc; i++)
+ if((keys & (1<<i)) && esrc[i].head){
+ if(i == Smouse)
+ e->mouse = emouse();
+ else if(i == Skeyboard)
+ e->kbdc = ekbd();
+ else if(i == Stimer) {
+ esrc[i].head = 0;
+ esrc[i].count = 0;
+ } else {
+ eb = ebread(&esrc[i]);
+ e->n = eb->n;
+ if(e->n > 0)
+ memcpy((void*)e->data, (void*)eb->…
+ free(eb);
+ }
+ return 1<<i;
+ }
+ waitevent();
+ }
+}
+
+void
+eflush(unsigned long keys)
+{
+ int i;
+ Ebuf *eb, *enext;
+
+ if(keys == 0)
+ return;
+
+ for(i=0; i<nsrc; i++)
+ if((keys & (1<<i))){
+ for (eb = esrc[i].head; eb; eb = enext) {
+ enext = eb->next;
+ free(eb);
+ }
+ esrc[i].count = 0;
+ esrc[i].head = 0;
+ esrc[i].tail = 0;
+ }
+}
+
+Mouse
+emouse(void)
+{
+ Mouse m;
+ Ebuf *eb;
+
+ if(!esrc[Smouse].inuse)
+ berror("mouse events not selected");
+ eb = ebread(&esrc[Smouse]);
+ memcpy((void*)&m, (void*)eb->buf, sizeof(Mouse));
+ free(eb);
+ return m;
+}
+
+int
+ekbd(void)
+{
+ Ebuf *eb;
+ int c;
+
+ if(!esrc[Skeyboard].inuse)
+ berror("keyboard events not selected");
+ eb = ebread(&esrc[Skeyboard]);
+ c = BGSHORT(eb->buf);
+ free(eb);
+ return c;
+}
+
+int
+ecanread(unsigned long keys)
+{
+ int i;
+
+ for(;;){
+ for(i=0; i<nsrc; i++){
+ if((keys & (1<<i)) && esrc[i].head)
+ return 1<<i;
+ }
+ if(XtAppPending(app))
+ waitevent();
+ else
+ return 0;
+ }
+}
+
+int
+ecanmouse(void)
+{
+ if(Smouse == -1)
+ berror("mouse events not selected");
+ return ecanread(Emouse);
+}
+
+int
+ecankbd(void)
+{
+ if(Skeyboard == -1)
+ berror("keyboard events not selected");
+ return ecanread(Ekeyboard);
+}
+
+static Ebuf*
+ebread(Esrc *s)
+{
+ Ebuf *eb;
+
+ while(s->head == 0)
+ waitevent();
+ eb = s->head;
+#ifdef COMPRESSMOUSE
+ if(s == &esrc[Smouse]) {
+ while(eb->next) {
+ s->head = eb->next;
+ s->count--;
+ free(eb);
+ eb = s->head;
+ }
+ }
+#endif
+ s->head = s->head->next;
+ if(s->head == 0) {
+ s->tail = 0;
+ s->count = 0;
+ } else
+ s->count--;
+ return eb;
+}
+
+static Ebuf*
+ebadd(Esrc *s)
+{
+ Ebuf *eb;
+ int m;
+
+ m = sizeof(Ebuf);
+ if(s->size > 1)
+ m += (s->size-1); /* overestimate, because of alignment */
+ eb = (Ebuf *)malloc(m);
+ if(eb) {
+ eb->next = 0;
+ eb->n = s->size;
+ if(s->tail){
+ s->tail->next = eb;
+ s->tail = eb;
+ }else
+ s->head = s->tail = eb;
+ }
+ return eb;
+}
+
+void
+berror(char *s)
+{
+ if(onerr)
+ (*onerr)(s);
+ else{
+ fprintf(stderr, "libg error: %s:\n", s);
+ exit(1);
+ }
+}
+
+void
+bflush(void)
+{
+ while(XtAppPending(app) & XtIMXEvent)
+ waitevent();
+}
+
+static void
+waitevent(void)
+{
+ XFlush(_dpy);
+ if (XtAppPending(app) & XtIMXEvent)
+ XtAppProcessEvent(app, XtIMXEvent);
+ else
+ XtAppProcessEvent(app, XtIMAll);
+}
+
+int
+snarfswap(char *s, int n, char **t)
+{
+ *t = GwinSelectionSwap(widg, s);
+ if (*t)
+ return strlen(*t);
+ return 0;
+}
+
+int scrpix(int *w, int *h)
+{
+ if (w)
+ *w = WidthOfScreen(XtScreen(_toplevel));
+ if (h)
+ *h = HeightOfScreen(XtScreen(_toplevel));
+ return 1;
+}
+
+#ifdef DEBUG
+/* for debugging */
+printgc(char *msg, GC g)
+{
+ XGCValues v;
+
+ XGetGCValues(_dpy, g, GCFunction|GCForeground|GCBackground|GCFont|
+ GCTile|GCFillStyle|GCStipple, &v);
+ fprintf(stderr, "%s: gc %x\n", msg, g);
+ fprintf(stderr, " fg %d bg %d func %d fillstyle %d font %x tile %x stippl…
+ v.foreground, v.background, v.function, v.fill_style,
+ v.font, v.tile, v.stipple);
+}
+#endif
+
diff --git a/libframe/Makefile b/libframe/Makefile
@@ -0,0 +1,47 @@
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+#
+# Prototype Makefile for libframe
+#
+# define operating system. ONE of:
+# -DIRIX -DSUNOS -DUMIPS -DSYSVR3 -DAIX -DOSF1
+# -DHPUX -DAPOLLO -DCONVEX -DDYNIX
+#
+# Additionally, -D_POSIX_SOURCE (or its equivalent) may be specified
+# if your compiler supports posix-compatible compilation
+include ../config.mk
+
+OS=-DIRIX5
+
+# add -Iincludedir for any include directories that need to be searched
+# for posix header files (for UMIPS, add -I/usr/include/posix)
+INCS=-I../include -I$(FREETYPEINC)
+
+# add name of library orderer - use ":" if none exists
+RANLIB=:
+
+# add name of library
+AR=ar
+
+CFLAGS=-c $(OS) $(INCS) -D_LIBXG_EXTENSION
+
+LIB=libframe.a
+CC=cc
+
+OBJ=frbox.o frdelete.o frdraw.o frinit.o frinsert.o frptofchar.o\
+ frselect.o frstr.o frutil.o misc.o
+
+all: $(LIB)
+
+$(LIB): $(OBJ)
+ $(AR) rv $(LIB) $(OBJ)
+ $(RANLIB) $(LIB)
+
+clean:
+ rm -f *.o *.a
+
+nuke: clean
+ rm -f $(LIB)
+
+install: $(LIB)
+
+$(OBJ): ../include/u.h ../include/libc.h ../include/frame.h
diff --git a/libframe/frbox.c b/libframe/frbox.c
@@ -0,0 +1,156 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+#define SLOP 25
+
+void
+_fraddbox(Frame *f, int bn, int n) /* add n boxes after bn, shift the r…
+ * box[bn+n]==box[bn] */
+{
+ int i;
+
+ if(bn > f->nbox)
+ berror("_fraddbox");
+ if(f->nbox+n > f->nalloc)
+ _frgrowbox(f, n+SLOP);
+ for(i=f->nbox; --i>=bn; )
+ f->box[i+n] = f->box[i];
+ f->nbox+=n;
+}
+
+void
+_frclosebox(Frame *f, int n0, int n1) /* inclusive */
+{
+ int i;
+
+ if(n0>=f->nbox || n1>=f->nbox || n1<n0)
+ berror("_frclosebox");
+ n1++;
+ for(i=n1; i<f->nbox; i++)
+ f->box[i-(n1-n0)] = f->box[i];
+ f->nbox -= n1-n0;
+}
+
+void
+_frdelbox(Frame *f, int n0, int n1) /* inclusive */
+{
+ if(n0>=f->nbox || n1>=f->nbox || n1<n0)
+ berror("_frdelbox");
+ _frfreebox(f, n0, n1);
+ _frclosebox(f, n0, n1);
+}
+
+void
+_frfreebox(Frame *f, int n0, int n1) /* inclusive */
+{
+ int i;
+
+ if(n1<n0)
+ return;
+ if(n0>=f->nbox || n1>=f->nbox)
+ berror("_frfreebox");
+ n1++;
+ for(i=n0; i<n1; i++)
+ if(f->box[i].nrune >= 0)
+ free(f->box[i].a.ptr);
+}
+
+void
+_frgrowbox(Frame *f, int delta)
+{
+ f->nalloc += delta;
+ f->box = realloc(f->box, f->nalloc*sizeof(Frbox));
+ if(f->box == 0)
+ berror("_frgrowbox");
+}
+
+static
+void
+dupbox(Frame *f, int bn)
+{
+ uchar *p;
+
+ if(f->box[bn].nrune < 0)
+ berror("dupbox");
+ _fraddbox(f, bn, 1);
+ if(f->box[bn].nrune >= 0){
+ p = _frallocstr(NBYTE(&f->box[bn])+1);
+ strcpy((char*)p, (char*)f->box[bn].a.ptr);
+ f->box[bn+1].a.ptr = p;
+ }
+}
+
+static
+uchar*
+runeindex(uchar *p, int n)
+{
+ int i, w;
+ Rune rune;
+
+ for(i=0; i<n; i++,p+=w)
+ if(*p < Runeself)
+ w = 1;
+ else{
+ w = chartorune(&rune, (char*)p);
+ USED(rune);
+ }
+ return p;
+}
+
+static
+void
+truncatebox(Frame *f, Frbox *b, int n) /* drop last n chars; no allocat…
+{
+ if(b->nrune<0 || b->nrune<n)
+ berror("truncatebox");
+ b->nrune -= n;
+ runeindex(b->a.ptr, b->nrune)[0] = 0;
+ b->wid = strwidth(f->font, (char *)b->a.ptr);
+}
+
+static
+void
+chopbox(Frame *f, Frbox *b, int n) /* drop first n chars; no allocation…
+{
+ if(b->nrune<0 || b->nrune<n)
+ berror("chopbox");
+ strcpy((char*)b->a.ptr, (char*)runeindex(b->a.ptr, n));
+ b->nrune -= n;
+ b->wid = strwidth(f->font, (char *)b->a.ptr);
+}
+
+void
+_frsplitbox(Frame *f, int bn, int n)
+{
+ dupbox(f, bn);
+ truncatebox(f, &f->box[bn], f->box[bn].nrune-n);
+ chopbox(f, &f->box[bn+1], n);
+}
+
+void
+_frmergebox(Frame *f, int bn) /* merge bn and bn+1 */
+{
+ Frbox *b;
+
+ b = &f->box[bn];
+ _frinsure(f, bn, NBYTE(&b[0])+NBYTE(&b[1])+1);
+ strcpy((char*)runeindex(b[0].a.ptr, b[0].nrune), (char*)b[1].a.ptr);
+ b[0].wid += b[1].wid;
+ b[0].nrune += b[1].nrune;
+ _frdelbox(f, bn+1, bn+1);
+}
+
+int
+_frfindbox(Frame *f, int bn, ulong p, ulong q) /* find box containing q…
+{
+ Frbox *b;
+
+ for(b = &f->box[bn]; bn<f->nbox && p+NRUNE(b)<=q; bn++, b++)
+ p += NRUNE(b);
+ if(p != q)
+ _frsplitbox(f, bn++, (int)(q-p));
+ return bn;
+}
diff --git a/libframe/frdelete.c b/libframe/frdelete.c
@@ -0,0 +1,104 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libg.h>
+#include <frame.h>
+
+int
+frdelete(Frame *f, ulong p0, ulong p1)
+{
+ Point pt0, pt1, ppt0;
+ Frbox *b;
+ int n0, n1, n;
+ Rectangle r;
+ int nn0;
+
+ if(p0>=f->nchars || p0==p1 || f->b==0)
+ return 0;
+ if(p1 > f->nchars)
+ p1 = f->nchars;
+ n0 = _frfindbox(f, 0, (unsigned long)0, p0);
+ n1 = _frfindbox(f, n0, p0, p1);
+ pt0 = _frptofcharnb(f, p0, n0);
+ pt1 = frptofchar(f, p1);
+ if(f->p0!=p0 || f->p1!=p1) /* likely they ARE equal */
+ frselectp(f, F&~D); /* can do better some day */
+ frselectf(f, pt0, pt1, 0);
+ if(n0 == f->nbox)
+ berror("off end in frdelete");
+ nn0 = n0;
+ ppt0 = pt0;
+ _frfreebox(f, n0, n1-1);
+ f->modified = 1;
+
+ /*
+ * Invariants:
+ * pt0 points to beginning, pt1 points to end
+ * n0 is box containing beginning of stuff being deleted
+ * n1, b are box containing beginning of stuff to be kept after delet…
+ * region between pt0 and pt1 is clear
+ */
+ b = &f->box[n1];
+ while(pt1.x!=pt0.x && n1<f->nbox){
+ _frcklinewrap0(f, &pt0, b);
+ _frcklinewrap(f, &pt1, b);
+ if(b->nrune > 0){
+ n = _frcanfit(f, pt0, b);
+ if(n==0)
+ berror("_frcanfit==0");
+ if(n != b->nrune){
+ _frsplitbox(f, n1, n);
+ b = &f->box[n1];
+ }
+ r.min = pt1;
+ r.max = pt1;
+ r.max.x += b->wid;
+ r.max.y += f->font->height;
+ bitblt(f->b, pt0, f->b, r, S);
+ if(pt0.y == pt1.y)
+ r.min.x = r.max.x-(pt1.x-pt0.x);
+ bitblt(f->b, r.min, f->b, r, 0);
+ }
+ _fradvance(f, &pt1, b);
+ pt0.x += _frnewwid(f, pt0, b);
+ f->box[n0++] = f->box[n1++];
+ b++;
+ }
+ if(pt1.y != pt0.y){
+ Point pt2;
+
+ pt2 = _frptofcharptb(f, 32767, pt1, n1);
+ if(pt2.y > f->r.max.y)
+ berror("frptofchar in frdelete");
+ if(n1 < f->nbox){
+ int q0, q1, q2;
+
+ q0 = pt0.y+f->font->height;
+ q1 = pt1.y+f->font->height;
+ q2 = pt2.y+f->font->height;
+ bitblt(f->b, pt0, f->b, Rect(pt1.x, pt1.y, f->r.max.x,…
+ bitblt(f->b, Pt(f->r.min.x, q0), f->b, Rect(f->r.min.x…
+ frselectf(f, Pt(pt2.x, pt2.y-(pt1.y-pt0.y)), pt2, 0);
+ }else
+ frselectf(f, pt0, pt2, 0);
+ }
+ _frclosebox(f, n0, n1-1);
+ if(nn0>0 && f->box[nn0-1].nrune>=0 && ppt0.x-f->box[nn0-1].wid>=(int)f…
+ --nn0;
+ ppt0.x -= f->box[nn0].wid;
+ }
+ _frclean(f, ppt0, nn0, n0<f->nbox-1? n0+1 : n0);
+ if(f->p1 > p1)
+ f->p1 -= p1-p0;
+ else if(f->p1 > p0)
+ f->p1 = p0;
+ if(f->p0 > p1)
+ f->p0 -= p1-p0;
+ else if(f->p0 > p0)
+ f->p0 = p0;
+ frselectp(f, F&~D);
+ f->nchars -= p1-p0;
+ pt0 = frptofchar(f, f->nchars);
+ n = f->nlines;
+ f->nlines = (pt0.y-f->r.min.y)/f->font->height+(pt0.x>f->left);
+ return n - f->nlines;
+}
diff --git a/libframe/frdraw.c b/libframe/frdraw.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+void
+_frredraw(Frame *f, Point pt)
+{
+ Frbox *b;
+ int nb;
+ for(nb=0,b=f->box; nb<f->nbox; nb++, b++){
+ _frcklinewrap(f, &pt, b);
+ if(b->nrune >= 0)
+ string(f->b, pt, f->font, (char *)b->a.ptr, S^D);
+ pt.x += b->wid;
+ }
+}
+
+Point
+_frdraw(Frame *f, Point pt)
+{
+ Frbox *b;
+ int nb, n;
+
+ for(b=f->box,nb=0; nb<f->nbox; nb++, b++){
+ _frcklinewrap0(f, &pt, b);
+ if(pt.y == f->r.max.y){
+ f->nchars -= _frstrlen(f, nb);
+ _frdelbox(f, nb, f->nbox-1);
+ break;
+ }
+ if(b->nrune > 0){
+ n = _frcanfit(f, pt, b);
+ if(n == 0)
+ berror("draw: _frcanfit==0");
+ if(n != b->nrune){
+ _frsplitbox(f, nb, n);
+ b = &f->box[nb];
+ }
+ pt.x += b->wid;
+ }else{
+ if(b->a.b.bc == '\n')
+ pt.x = f->left, pt.y+=f->font->height;
+ else
+ pt.x += _frnewwid(f, pt, b);
+ }
+ }
+ return pt;
+}
+int
+_frstrlen(Frame *f, int nb)
+{
+ int n;
+
+ for(n=0; nb<f->nbox; nb++)
+ n += NRUNE(&f->box[nb]);
+ return n;
+}
diff --git a/libframe/frinit.c b/libframe/frinit.c
@@ -0,0 +1,42 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+void
+frinit(Frame *f, Rectangle r, XftFont *ft, Bitmap *b)
+{
+ f->font = ft;
+ f->maxtab = 8*charwidth(ft, '0');
+ f->nbox = 0;
+ f->nalloc = 0;
+ f->nchars = 0;
+ f->nlines = 0;
+ f->p0 = 0;
+ f->p1 = 0;
+ f->box = 0;
+ f->lastlinefull = 0;
+ frsetrects(f, r, b);
+}
+
+void
+frsetrects(Frame *f, Rectangle r, Bitmap *b)
+{
+ f->b = b;
+ f->entire = r;
+ f->r = r;
+ f->r.max.y -= (r.max.y-r.min.y)%f->font->height;
+ f->left = r.min.x+1;
+ f->maxlines = (r.max.y-r.min.y)/f->font->height;
+}
+
+void
+frclear(Frame *f)
+{
+ if(f->nbox)
+ _frdelbox(f, 0, f->nbox-1);
+ if(f->box)
+ free(f->box);
+ f->box = 0;
+}
diff --git a/libframe/frinsert.c b/libframe/frinsert.c
@@ -0,0 +1,247 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+#define DELTA 25
+#define TMPSIZE 256
+static Frame frame;
+
+static
+Point
+bxscan(Frame *f, Rune *sp, Rune *ep, Point *ppt)
+{
+ int w, c, nb, delta, nl, nr, rw;
+ Frbox *b;
+ char *s, tmp[TMPSIZE+3]; /* +3 for rune overflow */
+ uchar *p;
+
+ frame.r = f->r;
+ frame.b = f->b;
+ frame.font = f->font;
+ frame.maxtab = f->maxtab;
+ frame.left = f->left;
+ frame.nbox = 0;
+ frame.nchars = 0;
+ delta = DELTA;
+ nl = 0;
+ for(nb=0; sp<ep && nl<=f->maxlines; nb++,frame.nbox++){
+ if(nb == frame.nalloc){
+ _frgrowbox(&frame, delta);
+ if(delta < 10000)
+ delta *= 2;
+ }
+ b = &frame.box[nb];
+ c = *sp;
+ if(c=='\t' || c=='\n'){
+ b->a.b.bc = c;
+ b->wid = 5000;
+ b->a.b.minwid = (c=='\n')? 0 : charwidth(frame.font, '…
+ b->nrune = -1;
+ if(c=='\n')
+ nl++;
+ frame.nchars++;
+ sp++;
+ }else{
+ s = tmp;
+ nr = 0;
+ w = 0;
+ while(sp < ep){
+ c = *sp;
+ if(c=='\t' || c=='\n')
+ break;
+ rw = runetochar(s, sp);
+ if(s+rw >= tmp+TMPSIZE)
+ break;
+ w += charwidth(frame.font, c);
+ sp++;
+ s += rw;
+ nr++;
+ }
+ *s++ = 0;
+ p = _frallocstr(s-tmp);
+ b = &frame.box[nb];
+ b->a.ptr = p;
+ memmove(p, tmp, s-tmp);
+ b->wid = w;
+ b->nrune = nr;
+ frame.nchars += nr;
+ }
+ }
+ _frcklinewrap0(f, ppt, &frame.box[0]);
+ return _frdraw(&frame, *ppt);
+}
+
+static
+void
+chopframe(Frame *f, Point pt, ulong p, int bn)
+{
+ Frbox *b;
+
+ for(b = &f->box[bn]; ; b++){
+ if(b >= &f->box[f->nbox])
+ berror("endofframe");
+ _frcklinewrap(f, &pt, b);
+ if(pt.y >= f->r.max.y)
+ break;
+ p += NRUNE(b);
+ _fradvance(f, &pt, b);
+ }
+ f->nchars = p;
+ f->nlines = f->maxlines;
+ if(b<&f->box[f->nbox]) /* BUG */
+ _frdelbox(f, (int)(b-f->box), f->nbox-1);
+}
+
+void
+frinsert(Frame *f, Rune *sp, Rune *ep, ulong p0)
+{
+ Point pt0, pt1, ppt0, ppt1, pt;
+ Frbox *b;
+ int n, n0, nn0, y;
+ Rectangle r;
+ static struct{
+ Point pt0, pt1;
+ }*pts;
+ static int nalloc=0;
+ int npts;
+
+ if(p0>f->nchars || sp==ep || f->b==0)
+ return;
+ n0 = _frfindbox(f, 0, 0, p0);
+ nn0 = n0;
+ pt0 = _frptofcharnb(f, p0, n0);
+ ppt0 = pt0;
+ pt1 = bxscan(f, sp, ep, &ppt0);
+ ppt1 = pt1;
+ if(n0 < f->nbox){
+ _frcklinewrap(f, &pt0, b = &f->box[n0]); /* for frselec…
+ _frcklinewrap0(f, &ppt1, b);
+ }
+ f->modified = 1;
+ /*
+ * ppt0 and ppt1 are start and end of insertion as they will appear wh…
+ * insertion is complete. pt0 is current location of insertion position
+ * (p0); pt1 is terminal point (without line wrap) of insertion.
+ */
+ if(p0==f->p0 && p0==f->p1) /* quite likely */
+ frselectf(f, pt0, pt0, F&~D);
+ else
+ frselectp(f, F&~D);
+ /*
+ * Find point where old and new x's line up
+ * Invariants:
+ * pt0 is where the next box (b, n0) is now
+ * pt1 is where it will be after then insertion
+ * If pt1 goes off the rectangle, we can toss everything from there on
+ */
+ for(b = &f->box[n0],npts=0;
+ pt1.x!=pt0.x && pt1.y!=f->r.max.y && n0<f->nbox; b++,n0++,npts++){
+ _frcklinewrap(f, &pt0, b);
+ _frcklinewrap0(f, &pt1, b);
+ if(b->nrune > 0){
+ n = _frcanfit(f, pt1, b);
+ if(n == 0)
+ berror("_frcanfit==0");
+ if(n != b->nrune){
+ _frsplitbox(f, n0, n);
+ b = &f->box[n0];
+ }
+ }
+ if(npts == nalloc){
+ pts = realloc(pts, (npts+DELTA)*sizeof(pts[0]));
+ nalloc += DELTA;
+ b = &f->box[n0];
+ }
+ pts[npts].pt0 = pt0;
+ pts[npts].pt1 = pt1;
+ /* has a text box overflowed off the frame? */
+ if(pt1.y == f->r.max.y)
+ break;
+ _fradvance(f, &pt0, b);
+ pt1.x += _frnewwid(f, pt1, b);
+ }
+ if(pt1.y > f->r.max.y)
+ berror("frinsert pt1 too far");
+ if(pt1.y==f->r.max.y && n0<f->nbox){
+ f->nchars -= _frstrlen(f, n0);
+ _frdelbox(f, n0, f->nbox-1);
+ }
+ if(n0 == f->nbox)
+ f->nlines = (pt1.y-f->r.min.y)/f->font->height+(pt1.x>f->left);
+ else if(pt1.y!=pt0.y){
+ int q0, q1;
+
+ y = f->r.max.y;
+ q0 = pt0.y+f->font->height;
+ q1 = pt1.y+f->font->height;
+ f->nlines += (q1-q0)/f->font->height;
+ if(f->nlines > f->maxlines)
+ chopframe(f, ppt1, p0, nn0);
+ if(pt1.y < y){
+ r = f->r;
+ r.min.y = q0;
+ r.max.y = y-(q1-q0);
+ if(q1 < y)
+ bitblt(f->b, Pt(f->r.min.x, q1), f->b, r, S);
+ r.min = pt0;
+ r.max.y = q0;
+ bitblt(f->b, pt1, f->b, r, S);
+ }
+ }
+ /*
+ * Move the old stuff down to make room. The loop will move the stuff
+ * between the insertion and the point where the x's lined up.
+ * The bitblts above moved everything down after the point they lined …
+ */
+ for((y=pt1.y==f->r.max.y?pt1.y:0),b = &f->box[n0-1]; --npts>=0; --b){
+ pt = pts[npts].pt1;
+ if(b->nrune > 0){
+ r.min = pts[npts].pt0;
+ r.max = r.min;
+ r.max.x += b->wid;
+ r.max.y += f->font->height;
+ bitblt(f->b, pt, f->b, r, S);
+ if(pt.y < y){ /* clear bit hanging off right */
+ r.min = pt;
+ r.max = pt;
+ r.min.x += b->wid;
+ r.max.x = f->r.max.x;
+ r.max.y += f->font->height;
+ bitblt(f->b, r.min, f->b, r, 0);
+ }
+ y = pt.y;
+ }else{
+ r.min = pt;
+ r.max = pt;
+ r.max.x += b->wid;
+ r.max.y += f->font->height;
+ if(r.max.x >= f->r.max.x)
+ r.max.x = f->r.max.x;
+ bitblt(f->b, r.min, f->b, r, 0);
+ y = (pt.x == f->left)? pt.y : 0;
+ }
+ }
+ frselectf(f, ppt0, ppt1, 0);
+ _frredraw(&frame, ppt0);
+ _fraddbox(f, nn0, frame.nbox);
+ for(n=0; n<frame.nbox; n++)
+ f->box[nn0+n] = frame.box[n];
+ if(nn0>0 && f->box[nn0-1].nrune>=0 && ppt0.x-f->box[nn0-1].wid>=(int)f…
+ --nn0;
+ ppt0.x -= f->box[nn0].wid;
+ }
+ n0 += frame.nbox;
+ _frclean(f, ppt0, nn0, n0<f->nbox-1? n0+1 : n0);
+ f->nchars += frame.nchars;
+ if(f->p0 >= p0)
+ f->p0 += frame.nchars;
+ if(f->p0 > f->nchars)
+ f->p0 = f->nchars;
+ if(f->p1 >= p0)
+ f->p1 += frame.nchars;
+ if(f->p1 > f->nchars)
+ f->p1 = f->nchars;
+ frselectp(f, F&~D);
+}
diff --git a/libframe/frptofchar.c b/libframe/frptofchar.c
@@ -0,0 +1,116 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+Point
+_frptofcharptb(Frame *f, ulong p, Point pt, int bn)
+{
+ uchar *s;
+ Frbox *b;
+ int w, l;
+ Rune r;
+
+ for(b = &f->box[bn]; bn<f->nbox; bn++,b++){
+ _frcklinewrap(f, &pt, b);
+ if(p < (l=NRUNE(b))){
+ if(b->nrune > 0)
+ for(s=b->a.ptr; p>0; s+=w, p--){
+ if((r = *s) < Runeself)
+ w = 1;
+ else
+ w = chartorune(&r, (char*)s);
+ pt.x += charwidth(f->font, r);
+ if(r==0 || pt.x>f->r.max.x)
+ berror("frptofchar");
+ }
+ break;
+ }
+ p -= l;
+ _fradvance(f, &pt, b);
+ }
+ return pt;
+}
+
+Point
+frptofchar(Frame *f, ulong p)
+{
+ return _frptofcharptb(f, p, Pt(f->left, f->r.min.y), 0);
+}
+
+Point
+_frptofcharnb(Frame *f, ulong p, int nb) /* doesn't do final _fradvance…
+{
+ Point pt;
+ int nbox;
+
+ nbox = f->nbox;
+ f->nbox = nb;
+ pt = _frptofcharptb(f, p, Pt(f->left, f->r.min.y), 0);
+ f->nbox = nbox;
+ return pt;
+}
+
+static
+Point
+_frgrid(Frame *f, Point p)
+{
+ p.y -= f->r.min.y;
+ p.y -= p.y%f->font->height;
+ p.y += f->r.min.y;
+ if(p.x > f->r.max.x)
+ p.x = f->r.max.x;
+ return p;
+}
+
+ulong
+frcharofpt(Frame *f, Point pt)
+{
+ Point qt;
+ int w, bn;
+ uchar *s;
+ Frbox *b;
+ ulong p;
+ Rune r;
+
+ pt = _frgrid(f, pt);
+ qt.x = f->left;
+ qt.y = f->r.min.y;
+ for(b=f->box,bn=0,p=0; bn<f->nbox && qt.y<pt.y; bn++,b++){
+ _frcklinewrap(f, &qt, b);
+ if(qt.y >= pt.y)
+ break;
+ _fradvance(f, &qt, b);
+ p += NRUNE(b);
+ }
+ for(; bn<f->nbox && qt.x<=pt.x; bn++,b++){
+ _frcklinewrap(f, &qt, b);
+ if(qt.y > pt.y)
+ break;
+ if(qt.x+b->wid > pt.x){
+ if(b->nrune < 0)
+ _fradvance(f, &qt, b);
+ else{
+ s = b->a.ptr;
+ for(;;){
+ if((r = *s) < Runeself)
+ w = 1;
+ else
+ w = chartorune(&r, (char*)s);
+ if(r == 0)
+ berror("end of string in frcha…
+ s += w;
+ qt.x += charwidth(f->font, r);
+ if(qt.x > pt.x)
+ break;
+ p++;
+ }
+ }
+ }else{
+ p += NRUNE(b);
+ _fradvance(f, &qt, b);
+ }
+ }
+ return p;
+}
diff --git a/libframe/frselect.c b/libframe/frselect.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+void
+frselect(Frame *f, Mouse *m) /* when called, button 1 is down */
+{
+ ulong p0, p1, q;
+ Point mp, pt0, pt1, qt;
+
+ mp = m->xy;
+
+ Again:
+ f->modified = 0;
+ frselectp(f, F&~D);
+ p0 = p1 = frcharofpt(f, mp);
+ pt0 = frptofchar(f, p0);
+ pt1 = frptofchar(f, p1);
+ frselectf(f, pt0, pt1, F&~D);
+ do{
+ if(f->modified) /* special hack so 8½ can frselect in …
+ goto Again;
+ q = frcharofpt(f, m->xy);
+ if(p1 != q){
+ if(p0 == p1)
+ frselectf(f, pt0, pt1, F&~D);
+ qt = frptofchar(f, q);
+ if(p1 < q)
+ frselectf(f, pt1, qt, F&~D);
+ else
+ frselectf(f, qt, pt1, F&~D);
+ p1 = q;
+ pt1 = qt;
+ if(p0 == p1)
+ frselectf(f, pt0, pt1, F&~D);
+ }
+ f->modified = 0;
+ if(p0 < p1)
+ f->p0 = p0, f->p1 = p1;
+ else
+ f->p0 = p1, f->p1 = p0;
+ frgetmouse();
+ }while(m->buttons & 1);
+}
+/* it is assumed p0<=p1 and both were generated by frptofchar() */
+void
+frselectf(Frame *f, Point p0, Point p1, Fcode c)
+{
+ int n;
+ Point q0, q1;
+
+ if(p0.x == f->left)
+ p0.x = f->r.min.x;
+ if(p1.x == f->left)
+ p1.x = f->r.min.x;
+ q0 = p0;
+ q1 = p1;
+ q0.y += f->font->height;
+ q1.y += f->font->height;
+ n = (p1.y-p0.y)/f->font->height;
+ if(f->b == 0)
+ berror("frselectf b==0");
+ if(p0.y == f->r.max.y)
+ return;
+ if(n == 0){
+ if(p0.x == p1.x)
+ if(p0.x == f->r.min.x)
+ q1.x++;
+ else
+ p0.x--;
+ bitblt(f->b, p0, f->b, Rpt(p0, q1), c);
+ }else{
+ if(p0.x >= f->r.max.x)
+ p0.x = f->r.max.x-1;
+ bitblt(f->b, p0, f->b, Rect(p0.x, p0.y, f->r.max.x, q0.y), c);
+ if(n > 1)
+ bitblt(f->b, Pt(f->r.min.x, q0.y),
+ f->b, Rect(f->r.min.x, q0.y, f->r.max.x, p1.y)…
+ bitblt(f->b, Pt(f->r.min.x, p1.y),
+ f->b, Rect(f->r.min.x, p1.y, q1.x, q1.y), c);
+ }
+}
+
+void
+frselectp(Frame *f, Fcode c)
+{
+ Point pt0, pt1;
+
+ pt0 = frptofchar(f, f->p0);
+ pt1 = (f->p0==f->p1)? pt0 : frptofchar(f, f->p1);
+ frselectf(f, pt0, pt1, c);
+}
diff --git a/libframe/frstr.c b/libframe/frstr.c
@@ -0,0 +1,41 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+/*
+ * The code here and elsewhere requires that strings not be gcalloc()ed
+ */
+
+#define CHUNK 16
+#define ROUNDUP(n) ((n+CHUNK)&~(CHUNK-1))
+
+uchar *
+_frallocstr(unsigned n)
+{
+ uchar *p;
+
+ p = malloc(ROUNDUP(n));
+ if(p == 0)
+ berror("out of memory");
+ return p;
+}
+
+void
+_frinsure(Frame *f, int bn, unsigned n)
+{
+ Frbox *b;
+ uchar *p;
+
+ b = &f->box[bn];
+ if(b->nrune < 0)
+ berror("_frinsure");
+ if(ROUNDUP(b->nrune) > n) /* > guarantees room for terminal NUL…
+ return;
+ p = _frallocstr(n);
+ b = &f->box[bn];
+ memmove(p, b->a.ptr, NBYTE(b)+1);
+ free(b->a.ptr);
+ b->a.ptr = p;
+}
diff --git a/libframe/frutil.c b/libframe/frutil.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+
+int
+_frcanfit(Frame *f, Point pt, Frbox *b)
+{
+ int left, w, nr;
+ uchar *p;
+ Rune r;
+
+ left = f->r.max.x-pt.x;
+ if(b->nrune < 0)
+ return b->a.b.minwid <= left;
+ if(left >= b->wid)
+ return b->nrune;
+ for(nr=0,p=b->a.ptr; *p; p+=w,nr++){
+ r = *p;
+ if(r < Runeself)
+ w = 1;
+ else
+ w = chartorune(&r, (char*)p);
+ left -= charwidth(f->font, r);
+ if(left < 0)
+ return nr;
+ }
+ berror("_frcanfit can't");
+ return 0;
+}
+
+void
+_frcklinewrap(Frame *f, Point *p, Frbox *b)
+{
+ if((b->nrune<0? b->a.b.minwid : b->wid) > f->r.max.x-p->x){
+ p->x = f->left;
+ p->y += f->font->height;
+ }
+}
+
+void
+_frcklinewrap0(Frame *f, Point *p, Frbox *b)
+{
+ if(_frcanfit(f, *p, b) == 0){
+ p->x = f->left;
+ p->y += f->font->height;
+ }
+}
+
+void
+_fradvance(Frame *f, Point *p, Frbox *b)
+{
+ if(b->nrune<0 && b->a.b.bc=='\n'){
+ p->x = f->left;
+ p->y += f->font->height;
+ }else
+ p->x += b->wid;
+}
+
+int
+_frnewwid(Frame *f, Point pt, Frbox *b)
+{
+ int c, x;
+
+ c = f->r.max.x;
+ x = pt.x;
+ if(b->nrune >= 0)
+ return b->wid;
+ if(b->a.b.bc == '\t'){
+ if(x+b->a.b.minwid > c)
+ x = pt.x = f->left;
+ x += f->maxtab;
+ x -= (x-f->left)%f->maxtab;
+ if(x-pt.x<b->a.b.minwid || x>c)
+ x = pt.x+b->a.b.minwid;
+ b->wid = x-pt.x;
+ }
+ return b->wid;
+}
+
+void
+_frclean(Frame *f, Point pt, int n0, int n1) /* look for mergeable boxe…
+{
+ Frbox *b;
+ int nb, c;
+
+ c = f->r.max.x;
+ for(nb=n0; nb<n1-1; nb++){
+ b = &f->box[nb];
+ _frcklinewrap(f, &pt, b);
+ while(b[0].nrune>=0 && nb<n1-1 && b[1].nrune>=0 && pt.x+b[0].w…
+ _frmergebox(f, nb);
+ n1--;
+ b = &f->box[nb];
+ }
+ _fradvance(f, &pt, &f->box[nb]);
+ }
+ for(; nb<f->nbox; nb++){
+ b = &f->box[nb];
+ _frcklinewrap(f, &pt, b);
+ _fradvance(f, &pt, &f->box[nb]);
+ }
+ f->lastlinefull = 0;
+ if(pt.y >= f->r.max.y)
+ f->lastlinefull = 1;
+}
diff --git a/libframe/misc.c b/libframe/misc.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <pwd.h>
+#ifdef NEEDVARARG
+#include <varargs.h>
+#else
+#include <stdarg.h>
+#endif
+#include <errno.h>
+
+void
+fprint(int fd, char *z, ...)
+{
+ va_list args;
+ char buf[2048]; /* pick reasonable blocksize */
+
+ va_start(args, z);
+ vsprintf(buf, z, args);
+ write(fd, buf, strlen(buf));
+ va_end(args);
+}
+
+int errstr(char *buf)
+{
+
+ strncpy(buf, strerror(errno), ERRLEN);
+ return 1;
+}
+
+char*
+getuser(void)
+{
+ struct passwd *p;
+
+ static char *user = 0;
+
+ if (!user) {
+ p = getpwuid(getuid());
+ if (p && p->pw_name) {
+ user = malloc(strlen(p->pw_name)+1);
+ if (user)
+ strcpy(user, p->pw_name);
+ }
+ }
+ if(!user)
+ user = "unknown";
+ return user;
+}
+
+#ifdef NEEDSTRERROR
+char *
+strerror(int n)
+{
+ extern char *sys_errlist[];
+ return sys_errlist[n];
+}
+#endif /* NEEDSTRERROR */
+
+#ifdef NEEDMEMMOVE
+/*
+ * memcpy is probably fast, but may not work with overlap
+ */
+void*
+memmove(void *a1, const void *a2, size_t n)
+{
+ char *s1;
+ const char *s2;
+
+ s1 = a1;
+ s2 = a2;
+ if(s1 > s2)
+ goto back;
+ if(s1 + n <= s2)
+ return memcpy(a1, a2, n);
+ while(n > 0) {
+ *s1++ = *s2++;
+ n--;
+ }
+ return a1;
+
+back:
+ s2 += n;
+ if(s2 <= s1)
+ return memcpy(a1, a2, n);
+ s1 += n;
+ while(n > 0) {
+ *--s1 = *--s2;
+ n--;
+ }
+ return a1;
+}
+#endif /* NEEDMEMMOVE */
diff --git a/rsam/Makefile b/rsam/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2013-2015 Rob King <[email protected]
+# This file may be redistributed and modified for any purpose.
+# No warranty is expressed or implied; use at your own risk.
+
+include ../config.mk
+
+LDFLAGS=
+CFLAGS=-DSAMBIN=\"$(BINDIR)/sam\"
+
+all: rsam
+
+rsam: rsam.o
+
+clean:
+ rm -f *.o rsam
+
+install: rsam
+ cp rsam $(BINDIR)
diff --git a/rsam/rsam.c b/rsam/rsam.c
@@ -0,0 +1,147 @@
+/* Copyright 2013-2015 Rob King <[email protected]>
+ * This file may be freely redistributed in source or binary form with or with…
+ * No warranty is expressed or implied; use at your own risk.
+ */
+
+#define _POSIX_C_SOURCE 200112L
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define PARENT_READ readpipe[0]
+#define CHILD_WRITE readpipe[1]
+#define CHILD_READ writepipe[0]
+#define PARENT_WRITE writepipe[1]
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+char *fifopath = NULL;
+
+void
+cleanup(void)
+{
+ if (fifopath)
+ {
+ unlink(fifopath);
+ free(fifopath);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *home = getenv("HOME") ? getenv("HOME") : "/tmp";
+ long pathmax = pathconf(home, _PC_PATH_MAX) != -1 ? pathco…
+ int writepipe[2] = {-1};
+ int readpipe[2] = {-1};
+
+ fifopath = calloc(pathmax, sizeof(char));
+ if (fifopath == NULL)
+ {
+ perror("fifopath");
+ return EXIT_FAILURE;
+ }
+
+ if (pipe(writepipe) != 0 || pipe(readpipe) != 0)
+ {
+ perror("pipe");
+ return EXIT_FAILURE;
+ }
+
+ snprintf(fifopath, pathmax, "%s/.sam.fifo", home);
+ unlink(fifopath);
+ if (mkfifo(fifopath, 0600) != 0)
+ {
+ perror("mkfifo");
+ return EXIT_FAILURE;
+ }
+
+ fifopath = fifopath;
+ atexit(cleanup);
+
+ int fifofd = open(fifopath, O_RDWR);
+ if (fifofd < 0)
+ {
+ perror("open");
+ return EXIT_FAILURE;
+ }
+
+ pid_t child = fork();
+ if (child == 0)
+ {
+ close(PARENT_WRITE);
+ close(PARENT_READ);
+
+ dup2(CHILD_READ, STDIN_FILENO); close(CHILD_READ);
+ dup2(CHILD_WRITE, STDOUT_FILENO); close(CHILD_WRITE);
+
+ execl(SAMBIN, "sam", "-R", NULL);
+ return EXIT_FAILURE;
+ }
+ else if (child < 0)
+ {
+ perror("fork");
+ return EXIT_FAILURE;
+ }
+
+ close(CHILD_READ);
+ close(CHILD_WRITE);
+
+ fd_set readfds;
+ fd_set writefds;
+
+ FD_ZERO(&readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+ FD_SET(fifofd, &readfds);
+ FD_SET(PARENT_READ, &readfds);
+
+ while (select(MAX(STDIN_FILENO, MAX(PARENT_READ, fifofd)) + 1, &readfd…
+ {
+ ssize_t count = 0;
+ char buf[8192];
+
+ if (FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ count = read(STDIN_FILENO, buf, 8192);
+ if (count <= 0)
+ {
+ exit(EXIT_SUCCESS);
+ }
+ write(PARENT_WRITE, buf, count);
+ }
+
+ if (FD_ISSET(fifofd, &readfds))
+ {
+ memset(buf, 0, 256);
+ count = read(fifofd, buf, 253);
+ if (count <= 0)
+ {
+ exit(EXIT_SUCCESS);
+ }
+ write(STDOUT_FILENO, "\x19\xff\x00", 3);
+ write(STDOUT_FILENO, buf, 255);
+ }
+
+ if (FD_ISSET(PARENT_READ, &readfds))
+ {
+ count = read(PARENT_READ, buf, 8192);
+ if (count <= 0)
+ {
+ exit(EXIT_SUCCESS);
+ }
+ write(STDOUT_FILENO, buf, count);
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+ FD_SET(fifofd, &readfds);
+ FD_SET(PARENT_READ, &readfds);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/sam/B.rc b/sam/B.rc
@@ -0,0 +1,51 @@
+#!/bin/rc
+
+files=()
+line=''
+
+if (~ $#* 0) {
+ echo 'usage: B [-nnn] files...' >[1=2]
+ exit 1
+}
+
+dir=`{/bin/pwd}
+
+if (~ $#USER 0)
+ USER=$LOGNAME
+pipe=/tmp/.sam.$USER
+
+switch($DISPLAY) {
+ case *:[0-9]*.[0-9]*
+ pipe=$pipe.$DISPLAY
+ if (! test -r $pipe)
+ pipe=`{echo $pipe | sed 's/\.[0-9]*$//'}
+ case *:[0-9]*
+ pipe=$pipe.$DISPLAY
+ if (! test -r $pipe)
+ pipe=$pipe.0
+ case ""
+ case *
+ pipe=$pipe.$DISPLAY
+}
+
+if (! test -r $pipe) {
+ echo `{basename $0}^': No pipe "'$pipe'" to sam.' >[1=2]
+ exit 1
+}
+
+for (i) {
+ switch($i) {
+ case /*
+ files = ( $files $i )
+ case -*
+ line = `{echo $i | sed 's/.//'}
+ case *
+ files = ( $files $dir/$i )
+ }
+}
+
+if (! ~ $#files 0)
+ echo B $files >> $pipe
+
+if (! ~ $line '')
+ echo $line >> $pipe
diff --git a/sam/B.sh b/sam/B.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+files=
+line="none"
+
+if [ $# = 0 ]; then
+ echo 'usage: B [-nnnn] files...' 1>&2
+ exit 1
+fi
+
+dir=`/bin/pwd`
+if [ "$USER" = "" ]; then
+ USER=$LOGNAME
+fi
+pipe=/tmp/.sam.$USER
+
+case "$DISPLAY" in
+ *:[0-9]*\.[0-9]*)
+ pipe=$pipe.$DISPLAY
+ if [ ! -r $pipe ]; then
+ pipe=`echo $pipe | sed 's/\.[0-9]*$//'`
+ fi
+ ;;
+ *:[0-9]*)
+ pipe=$pipe.$DISPLAY
+ if [ ! -r $pipe ]; then
+ pipe=$pipe.0
+ fi
+ ;;
+ "")
+ ;;
+ *) pipe=$pipe.$DISPLAY
+ ;;
+esac
+if [ ! -r $pipe ]; then
+ echo `basename $0`": No pipe \""$pipe"\" to sam." 1>&2
+ exit 1
+fi
+
+for i in $*
+do
+ case "$i" in
+ /*) files="$files $i"
+ ;;
+ -*) line=`echo $i | sed 's/.//'`
+ ;;
+ *) files="$files $dir/$i"
+ ;;
+ esac
+done
+
+echo "B $files" >> $pipe
+if [ $line != "none" ]; then
+ echo $line >> $pipe
+fi
+
diff --git a/sam/Makefile b/sam/Makefile
@@ -0,0 +1,89 @@
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+#
+# Prototype Makefile for sam
+#
+# define operating system. ONE of:
+# -DIRIX -DSUNOS -DUMIPS -DSYSVR3 -DAIX -DOSF1
+# -DHPUX -DAPOLLO -DCONVEX -DDYNIX
+#
+# -DIRIX is the default and should actually work on any modern system.
+# Additionally, -D_POSIX_SOURCE (or its equivalent) may be specified
+# if your compiler supports posix-compatible compilation.
+#
+include ../config.mk
+
+# If your system has 64-bit addresses, add -DUSE64BITS to $(OS).
+OS=-DIRIX5 -DUSE64BITS=$(USE64BITS)
+
+# add -Iincludedir for any include directories that need to be searched
+# for posix header files (for UMIPS, add -I/usr/include/posix)
+INCS=-I../include
+
+# Set the name of the environment variable containing the user's home d…
+HOMEDIR=HOME
+
+# RSAMNAME and TERMNAME contain the names of the files containing the
+# sam and samterm executables, respectively. SAMDIR is the directory
+# where sam is to be installed. SAMSAVEDIR is the name of the directory
+# where the samsave file restoration script is stored.
+RSAMNAME=sam
+TERMNAME=$(BINDIR)/samterm
+SAMDIR=$(BINDIR)
+SAMSAVEDIR=$(BINDIR)
+
+# Set TMP to a good place for tmp files (with lots of room)
+TMP=$(TMPDIR)
+
+# Set SHELLNAME and SHELLPATH to the name of a shell and the pathname
+# of its executable
+SHELLNAME=sh
+SHELLPATH=/bin/sh
+
+# Set RXNAME and RXPATHNAME to the name of the remote execution command
+# and the pathname of its executable
+RXNAME=ssh
+RXPATHNAME=/usr/bin/ssh
+
+# Set RXSAMNAME to the name of the command to run on the remote host.
+RXSAMNAME=/usr/local/bin/rsam
+
+SAMSAVE=/bin/sh\\n$(SAMSAVEDIR)/samsave
+
+CFLAGS=$(OS) -D_LIBXG_EXTENSION $(INCS)
+
+SYSFLAGS= -DHOMEDIR=\"$(HOMEDIR)\" -DRSAMNAME=\"$(RSAMNAME)\" \
+ -DTERMNAME=\"$(TERMNAME)\" -DTMP=\"$(TMP)\" \
+ -DSHELLNAME=\"$(SHELLNAME)\" -DSHELLPATH=\"$(SHELLPATH)\" \
+ -DRXNAME=\"$(RXNAME)\" -DRXPATHNAME=\"$(RXPATHNAME)\" \
+ -DRXSAMNAME=\"$(RXSAMNAME)\" -DSAMSAVE=\"$(SAMSAVE)\"
+
+LIB=../libframe/libframe.a ../libXg/libXg.a
+CC=cc $(SYSFLAGS)
+
+OBJ=sam.o address.o buffer.o cmd.o disc.o error.o file.o io.o \
+ list.o mesg.o moveto.o multi.o rasp.o regexp.o shell.o \
+ string.o sys.o unix.o xec.o
+
+all: sam
+
+sam: $(OBJ) $(LIB)
+ $(CC) -o sam $(OBJ) $(LIB)
+
+clean:
+ rm -f *.o core sam
+
+nuke: clean
+ rm -f sam
+
+install: sam
+ cp sam $(SAMDIR)/$(RSAMNAME)
+ cp samsave $(SAMSAVEDIR)/samsave
+ chmod +x $(SAMSAVEDIR)/samsave
+
+$(OBJ): sam.h ../include/u.h ../include/libc.h errors.h mesg.h
+
+cmd.o: parse.h
+xec.o: parse.h
+
+unix.o: sam.h ../include/u.h ../include/libc.h errors.h mesg.h
+ $(CC) -c $(CFLAGS) $(SYSFLAGS) unix.c
diff --git a/sam/address.c b/sam/address.c
@@ -0,0 +1,241 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+#include "parse.h"
+
+Address addr;
+String lastpat;
+int patset;
+File *menu;
+
+File *matchfile(String*);
+Address charaddr(Posn, Address, int);
+
+Address
+address(Addr *ap, Address a, int sign)
+{
+ File *f = a.f;
+ Address a1, a2;
+
+ do{
+ switch(ap->type){
+ case 'l':
+ case '#':
+ a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, s…
+ break;
+
+ case '.':
+ a = f->dot;
+ break;
+
+ case '$':
+ a.r.p1 = a.r.p2 = f->nrunes;
+ break;
+
+ case '\'':
+ a.r = f->mark;
+ break;
+
+ case '?':
+ sign = -sign;
+ if(sign == 0)
+ sign = -1;
+ /* fall through */
+ case '/':
+ nextmatch(f, ap->are, sign>=0? a.r.p2 : a.r.p1, sign);
+ a.r = sel.p[0];
+ break;
+
+ case '"':
+ a = matchfile(ap->are)->dot;
+ f = a.f;
+ if(f->state == Unread)
+ load(f);
+ break;
+
+ case '*':
+ a.r.p1 = 0, a.r.p2 = f->nrunes;
+ return a;
+
+ case ',':
+ case ';':
+ if(ap->left)
+ a1 = address(ap->left, a, 0);
+ else
+ a1.f = a.f, a1.r.p1 = a1.r.p2 = 0;
+ if(ap->type == ';'){
+ f = a1.f;
+ f->dot = a = a1;
+ }
+ if(ap->next)
+ a2 = address(ap->next, a, 0);
+ else
+ a2.f = a.f, a2.r.p1 = a2.r.p2 = f->nrunes;
+ if(a1.f != a2.f)
+ error(Eorder);
+ a.f = a1.f, a.r.p1 = a1.r.p1, a.r.p2 = a2.r.p2;
+ if(a.r.p2 < a.r.p1)
+ error(Eorder);
+ return a;
+
+ case '+':
+ case '-':
+ sign = 1;
+ if(ap->type == '-')
+ sign = -1;
+ if(ap->next==0 || ap->next->type=='+' || ap->next->typ…
+ a = lineaddr(1L, a, sign);
+ break;
+ default:
+ panic("address");
+ return a;
+ }
+ }while(ap = ap->next); /* assign = */
+ return a;
+}
+
+void
+nextmatch(File *f, String *r, Posn p, int sign)
+{
+ compile(r);
+ if(sign >= 0){
+ if(!execute(f, p, INFINITY))
+ error(Esearch);
+ if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p1==p){
+ if(++p>f->nrunes)
+ p = 0;
+ if(!execute(f, p, INFINITY))
+ panic("address");
+ }
+ }else{
+ if(!bexecute(f, p))
+ error(Esearch);
+ if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p2==p){
+ if(--p<0)
+ p = f->nrunes;
+ if(!bexecute(f, p))
+ panic("address");
+ }
+ }
+}
+
+File *
+matchfile(String *r)
+{
+ File *f;
+ File *match = 0;
+ int i;
+
+ for(i = 0; i<file.nused; i++){
+ f = file.filepptr[i];
+ if(f == cmd)
+ continue;
+ if(filematch(f, r)){
+ if(match)
+ error(Emanyfiles);
+ match = f;
+ }
+ }
+ if(!match)
+ error(Efsearch);
+ return match;
+}
+
+int
+filematch(File *f, String *r)
+{
+ char *c, buf[STRSIZE+100];
+ String *t;
+
+ c = Strtoc(&f->name);
+ sprint(buf, "%c%c%c %s\n", " '"[f->state==Dirty],
+ "-+"[f->rasp!=0], " ."[f==curfile], c);
+ free(c);
+ t = tmpcstr(buf);
+ Strduplstr(&genstr, t);
+ freetmpstr(t);
+ /* A little dirty... */
+ if(menu == 0)
+ (menu=Fopen())->state=Clean;
+ Bdelete(menu->buf, 0, menu->buf->nrunes);
+ Binsert(menu->buf, &genstr, 0);
+ menu->nrunes = menu->buf->nrunes;
+ compile(r);
+ return execute(menu, 0, menu->nrunes);
+}
+
+Address
+charaddr(Posn l, Address addr, int sign)
+{
+ if(sign == 0)
+ addr.r.p1 = addr.r.p2 = l;
+ else if(sign < 0)
+ addr.r.p2 = addr.r.p1-=l;
+ else if(sign > 0)
+ addr.r.p1 = addr.r.p2+=l;
+ if(addr.r.p1<0 || addr.r.p2>addr.f->nrunes)
+ error(Erange);
+ return addr;
+}
+
+Address
+lineaddr(Posn l, Address addr, int sign)
+{
+ int n;
+ int c;
+ File *f = addr.f;
+ Address a;
+
+ SET(c);
+ a.f = f;
+ if(sign >= 0){
+ if(l == 0){
+ if(sign==0 || addr.r.p2==0){
+ a.r.p1 = a.r.p2 = 0;
+ return a;
+ }
+ a.r.p1 = addr.r.p2;
+ Fgetcset(f, addr.r.p2-1);
+ }else{
+ if(sign==0 || addr.r.p2==0){
+ Fgetcset(f, (Posn)0);
+ n = 1;
+ }else{
+ Fgetcset(f, addr.r.p2-1);
+ n = Fgetc(f)=='\n';
+ }
+ for(; n<l; ){
+ c = Fgetc(f);
+ if(c == -1)
+ error(Erange);
+ else if(c == '\n')
+ n++;
+ }
+ a.r.p1 = f->getcp;
+ }
+ do; while((c=Fgetc(f))!='\n' && c!=-1);
+ a.r.p2 = f->getcp;
+ }else{
+ Fbgetcset(f, addr.r.p1);
+ if(l == 0)
+ a.r.p2 = addr.r.p1;
+ else{
+ for(n = 0; n<l; ){ /* always runs once */
+ c = Fbgetc(f);
+ if(c == '\n')
+ n++;
+ else if(c == -1){
+ if(++n != l)
+ error(Erange);
+ }
+ }
+ a.r.p2 = f->getcp;
+ if(c == '\n')
+ a.r.p2++; /* lines start after a newlin…
+ }
+ do; while((c=Fbgetc(f))!='\n' && c!=-1);
+ a.r.p1 = f->getcp;
+ if(c == '\n')
+ a.r.p1++; /* lines start after a newline */
+ }
+ return a;
+}
diff --git a/sam/buffer.c b/sam/buffer.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+int incache(Buffer*, Posn, Posn);
+
+Buffer *
+Bopen(Discdesc *dd)
+{
+ Buffer *b;
+
+ b = emalloc(sizeof(Buffer));
+ b->disc = Dopen(dd);
+ Strinit(&b->cache);
+ return b;
+}
+
+void
+Bterm(Buffer *b)
+{
+ Dclose(b->disc);
+ Strclose(&b->cache);
+ free(b);
+}
+
+int
+Bread(Buffer *b, Rune *addr, int n, Posn p0)
+{
+ int m;
+
+ if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
+ panic("bread cache");
+ if(p0 < 0)
+ panic("Bread p0<0");
+ if(p0+n > b->nrunes){
+ n = b->nrunes-p0;
+ if(n < 0)
+ panic("Bread<0");
+ }
+ if(!incache(b, p0, p0+n)){
+ Bflush(b);
+ if(n>=BLOCKSIZE/2)
+ return Dread(b->disc, addr, n, p0);
+ else{
+ Posn minp;
+ if(b->nrunes-p0>BLOCKSIZE/2)
+ m = BLOCKSIZE/2;
+ else
+ m = b->nrunes-p0;
+ if(m<n)
+ m = n;
+ minp = p0-BLOCKSIZE/2;
+ if(minp<0)
+ minp = 0;
+ m += p0-minp;
+ Strinsure(&b->cache, m);
+ if(Dread(b->disc, b->cache.s, m, minp)!=m)
+ panic("Bread");
+ b->cache.n = m;
+ b->c1 = minp;
+ b->c2 = minp+m;
+ b->dirty = FALSE;
+ }
+ }
+ memmove(addr, &b->cache.s[p0-b->c1], n*RUNESIZE);
+ return n;
+}
+
+void
+Binsert(Buffer *b, String *s, Posn p0)
+{
+ if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
+ panic("binsert cache");
+ if(p0<0)
+ panic("Binsert p0<0");
+ if(s->n == 0)
+ return;
+ if(incache(b, p0, p0) && b->cache.n+s->n<=STRSIZE){
+ Strinsert(&b->cache, s, p0-b->c1);
+ b->dirty = TRUE;
+ if(b->cache.n > BLOCKSIZE*2){
+ b->nrunes += s->n;
+ Bflush(b);
+ /* try to leave some cache around p0 */
+ if(p0 >= b->c1+BLOCKSIZE){
+ /* first BLOCKSIZE can go */
+ Strdelete(&b->cache, 0, BLOCKSIZE);
+ b->c1 += BLOCKSIZE;
+ }else if(p0 <= b->c2-BLOCKSIZE){
+ /* last BLOCKSIZE can go */
+ b->cache.n -= BLOCKSIZE;
+ b->c2 -= BLOCKSIZE;
+ }else{
+ /* too hard; negate the cache and pick up next…
+ Strzero(&b->cache);
+ b->c1 = b->c2 = 0;
+ }
+ return;
+ }
+ }else{
+ Bflush(b);
+ if(s->n >= BLOCKSIZE/2){
+ b->cache.n = 0;
+ b->c1 = b->c2 = 0;
+ Dinsert(b->disc, s->s, s->n, p0);
+ }else{
+ int m;
+ Posn minp;
+ if(b->nrunes-p0 > BLOCKSIZE/2)
+ m = BLOCKSIZE/2;
+ else
+ m = b->nrunes-p0;
+ minp = p0-BLOCKSIZE/2;
+ if(minp < 0)
+ minp = 0;
+ m += p0-minp;
+ Strinsure(&b->cache, m);
+ if(Dread(b->disc, b->cache.s, m, minp)!=m)
+ panic("Bread");
+ b->cache.n = m;
+ b->c1 = minp;
+ b->c2 = minp+m;
+ Strinsert(&b->cache, s, p0-b->c1);
+ b->dirty = TRUE;
+ }
+ }
+ b->nrunes += s->n;
+}
+
+void
+Bdelete(Buffer *b, Posn p1, Posn p2)
+{
+ if(p1<0 || p2<0)
+ panic("Bdelete p<0");
+ if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
+ panic("bdelete cache");
+ if(p1 == p2)
+ return;
+ if(incache(b, p1, p2)){
+ Strdelete(&b->cache, p1-b->c1, p2-b->c1);
+ b->dirty = TRUE;
+ }else{
+ Bflush(b);
+ Ddelete(b->disc, p1, p2);
+ b->cache.n = 0;
+ b->c1 = b->c2 = 0;
+ }
+ b->nrunes -= p2-p1;
+}
+
+void
+Bflush(Buffer *b)
+{
+ if(b->dirty){
+ Dreplace(b->disc, b->c1, b->c2, b->cache.s, b->cache.n);
+ b->c2 = b->c1+b->cache.n;
+ b->dirty = FALSE;
+ if(b->nrunes != b->disc->nrunes)
+ panic("Bflush");
+ }
+}
+
+void
+Bclean(Buffer *b)
+{
+ if(b->dirty){
+ Bflush(b);
+ b->c1 = b->c2 = 0;
+ Strzero(&b->cache);
+ }
+}
+
+/*int hits, misses; /**/
+
+int
+incache(Buffer *b, Posn p1, Posn p2)
+{
+ /*if(b->c1<=p1 && p2<=b->c1+b->cache.n)hits++; else misses++;/**/
+ return b->c1<=p1 && p2<=b->c1+b->cache.n;
+}
diff --git a/sam/cmd.c b/sam/cmd.c
@@ -0,0 +1,591 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+#include "parse.h"
+
+static char linex[]="\n";
+static char wordx[]=" \t\n";
+struct cmdtab cmdtab[]={
+/* cmdc text regexp addr defcmd defa…
+ '\n', 0, 0, 0, 0, aDot, 0, …
+ 'a', 1, 0, 0, 0, aDot, 0, …
+ 'b', 0, 0, 0, 0, aNo, 0, …
+ 'B', 0, 0, 0, 0, aNo, 0, …
+ 'c', 1, 0, 0, 0, aDot, 0, …
+ 'd', 0, 0, 0, 0, aDot, 0, …
+ 'D', 0, 0, 0, 0, aNo, 0, …
+ 'e', 0, 0, 0, 0, aNo, 0, …
+ 'f', 0, 0, 0, 0, aNo, 0, …
+ 'g', 0, 1, 0, 'p', aDot, 0, …
+ 'i', 1, 0, 0, 0, aDot, 0, …
+ 'k', 0, 0, 0, 0, aDot, 0, …
+ 'm', 0, 0, 1, 0, aDot, 0, …
+ 'n', 0, 0, 0, 0, aNo, 0, …
+ 'p', 0, 0, 0, 0, aDot, 0, …
+ 'q', 0, 0, 0, 0, aNo, 0, …
+ 'r', 0, 0, 0, 0, aDot, 0, …
+ 's', 0, 1, 0, 0, aDot, 1, …
+ 't', 0, 0, 1, 0, aDot, 0, …
+ 'u', 0, 0, 0, 0, aNo, 1, …
+ 'v', 0, 1, 0, 'p', aDot, 0, …
+ 'w', 0, 0, 0, 0, aAll, 0, …
+ 'x', 0, 1, 0, 'p', aDot, 0, …
+ 'y', 0, 1, 0, 'p', aDot, 0, …
+ 'X', 0, 1, 0, 'f', aNo, 0, …
+ 'Y', 0, 1, 0, 'f', aNo, 0, …
+ '!', 0, 0, 0, 0, aNo, 0, …
+ '>', 0, 0, 0, 0, aDot, 0, …
+ '<', 0, 0, 0, 0, aDot, 0, …
+ '|', 0, 0, 0, 0, aDot, 0, …
+ '=', 0, 0, 0, 0, aDot, 0, …
+ 'c'|0x100,0, 0, 0, 0, aNo, 0, …
+ 0, 0, 0, 0, 0, 0, 0, …
+};
+Cmd *parsecmd(int);
+Addr *compoundaddr(void);
+Addr *simpleaddr(void);
+void freecmd(void);
+void okdelim(int);
+
+Rune line[BLOCKSIZE];
+Rune termline[BLOCKSIZE];
+Rune *linep = line;
+Rune *terminp = termline;
+Rune *termoutp = termline;
+List cmdlist;
+List addrlist;
+List relist;
+List stringlist;
+int eof;
+
+void
+resetcmd(void)
+{
+ linep = line;
+ *linep = 0;
+ terminp = termoutp = termline;
+ freecmd();
+}
+
+int
+inputc(void)
+{
+ int n, nbuf;
+ char buf[3];
+ Rune r;
+
+ Again:
+ nbuf = 0;
+ if(downloaded){
+ while(termoutp == terminp){
+ cmdupdate();
+ if(patset)
+ tellpat();
+ while(termlocked > 0){
+ outT0(Hunlock);
+ termlocked--;
+ }
+ if(rcv() == 0)
+ return -1;
+ }
+ r = *termoutp++;
+ if(termoutp == terminp)
+ terminp = termoutp = termline;
+ }else{
+ do{
+ n = read(0, buf+nbuf, 1);
+ if(n <= 0)
+ return -1;
+ nbuf += n;
+ }while(!fullrune(buf, nbuf));
+ chartorune(&r, buf);
+ }
+ if(r == 0){
+ warn(Wnulls);
+ goto Again;
+ }
+ return r;
+}
+
+int
+inputline(void)
+{
+ int i, c;
+
+ linep = line;
+ i = 0;
+ do{
+ if((c = inputc())<=0)
+ return -1;
+ if(i == (sizeof line)/RUNESIZE-1)
+ error(Etoolong);
+ }while((line[i++]=c) != '\n');
+ line[i] = 0;
+ return 1;
+}
+
+int
+getch(void)
+{
+ if(eof)
+ return -1;
+ if(*linep==0 && inputline()<0){
+ eof = TRUE;
+ return -1;
+ }
+ return *linep++;
+}
+
+int
+nextc(void)
+{
+ if(*linep == 0)
+ return -1;
+ return *linep;
+}
+
+void
+ungetch(void)
+{
+ if(--linep < line)
+ panic("ungetch");
+}
+
+Posn
+getnum(void)
+{
+ Posn n=0;
+ int c;
+
+ if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */
+ return 1;
+ while('0'<=(c=getch()) && c<='9')
+ n = n*10 + (c-'0');
+ ungetch();
+ return n;
+}
+
+int
+skipbl(void)
+{
+ int c;
+ do
+ c = getch();
+ while(c==' ' || c=='\t');
+ if(c >= 0)
+ ungetch();
+ return c;
+}
+
+void
+termcommand(void)
+{
+ Posn p;
+
+ Fgetcset(cmd, cmdpt);
+ for(p=cmdpt; p<cmd->nrunes; p++){
+ if(terminp >= &termline[BLOCKSIZE]){
+ cmdpt = cmd->nrunes;
+ error(Etoolong);
+ }
+ *terminp++ = Fgetc(cmd);
+ }
+ cmdpt = cmd->nrunes;
+}
+
+void
+cmdloop(void)
+{
+ Cmd *cmdp;
+ File *ocurfile;
+ int loaded;
+
+ for(;;){
+ if(!downloaded && curfile && curfile->state==Unread)
+ load(curfile);
+ if((cmdp = parsecmd(0))==0){
+ if(downloaded){
+ rescue();
+ exits("eof");
+ }
+ break;
+ }
+ ocurfile = curfile;
+ loaded = curfile && curfile->state!=Unread;
+ if(cmdexec(curfile, cmdp) == 0)
+ break;
+ freecmd();
+ cmdupdate();
+ update();
+ if(downloaded && curfile &&
+ (ocurfile!=curfile || (!loaded && curfile->state!=Unread)))
+ outTs(Hcurrent, curfile->tag);
+ /* don't allow type ahead on files that aren't bound */
+ if(downloaded && curfile && curfile->rasp == 0)
+ terminp = termoutp;
+ }
+}
+
+Cmd *
+newcmd(void){
+ Cmd *p;
+
+ p = emalloc(sizeof(Cmd));
+ inslist(&cmdlist, cmdlist.nused, (long)p);
+ return p;
+}
+
+Addr*
+newaddr(void)
+{
+ Addr *p;
+
+ p = emalloc(sizeof(Addr));
+ inslist(&addrlist, addrlist.nused, (long)p);
+ return p;
+}
+
+String*
+newre(void)
+{
+ String *p;
+
+ p = emalloc(sizeof(String));
+ inslist(&relist, relist.nused, (long)p);
+ Strinit(p);
+ return p;
+}
+
+String*
+newstring(void)
+{
+ String *p;
+
+ p = emalloc(sizeof(String));
+ inslist(&stringlist, stringlist.nused, (long)p);
+ Strinit(p);
+ return p;
+}
+
+void
+freecmd(void)
+{
+ int i;
+
+ while(cmdlist.nused > 0)
+ free(cmdlist.ucharpptr[--cmdlist.nused]);
+ while(addrlist.nused > 0)
+ free(addrlist.ucharpptr[--addrlist.nused]);
+ while(relist.nused > 0){
+ i = --relist.nused;
+ Strclose(relist.stringpptr[i]);
+ free(relist.stringpptr[i]);
+ }
+ while(stringlist.nused>0){
+ i = --stringlist.nused;
+ Strclose(stringlist.stringpptr[i]);
+ free(stringlist.stringpptr[i]);
+ }
+}
+
+int
+lookup(int c)
+{
+ int i;
+
+ for(i=0; cmdtab[i].cmdc; i++)
+ if(cmdtab[i].cmdc == c)
+ return i;
+ return -1;
+}
+
+void
+okdelim(int c)
+{
+ if(c=='\\' || ('a'<=c && c<='z')
+ || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
+ error_c(Edelim, c);
+}
+
+void
+atnl(void)
+{
+ skipbl();
+ if(getch() != '\n')
+ error(Enewline);
+}
+
+void
+getrhs(String *s, int delim, int cmd)
+{
+ int c;
+
+ while((c = getch())>0 && c!=delim && c!='\n'){
+ if(c == '\\'){
+ if((c=getch()) <= 0)
+ error(Ebadrhs);
+ if(c == '\n'){
+ ungetch();
+ c='\\';
+ }else if(c == 'n')
+ c='\n';
+ else if(c!=delim && (cmd=='s' || c!='\\')) /* s…
+ Straddc(s, '\\');
+ }
+ Straddc(s, c);
+ }
+ ungetch(); /* let client read whether delimeter, '\n' or whatev…
+}
+
+String *
+collecttoken(char *end)
+{
+ String *s = newstring();
+ int c;
+
+ while((c=nextc())==' ' || c=='\t')
+ Straddc(s, getch()); /* blanks significant for getname() */
+ while((c=getch())>0 && utfrune(end, c)==0)
+ Straddc(s, c);
+ Straddc(s, 0);
+ if(c != '\n')
+ atnl();
+ return s;
+}
+
+String *
+collecttext(void)
+{
+ String *s = newstring();
+ int begline, i, c, delim;
+
+ if(skipbl()=='\n'){
+ getch();
+ i = 0;
+ do{
+ begline = i;
+ while((c = getch())>0 && c!='\n')
+ i++, Straddc(s, c);
+ i++, Straddc(s, '\n');
+ if(c < 0)
+ goto Return;
+ }while(s->s[begline]!='.' || s->s[begline+1]!='\n');
+ Strdelete(s, s->n-2, s->n);
+ }else{
+ okdelim(delim = getch());
+ getrhs(s, delim, 'a');
+ if(nextc()==delim)
+ getch();
+ atnl();
+ }
+ Return:
+ Straddc(s, 0); /* JUST FOR CMDPRINT() */
+ return s;
+}
+
+Cmd *
+parsecmd(int nest)
+{
+ int i, c;
+ struct cmdtab *ct;
+ Cmd *cp, *ncp;
+ Cmd cmd;
+
+ cmd.next = cmd.ccmd = 0;
+ cmd.re = 0;
+ cmd.flag = cmd.num = 0;
+ cmd.addr = compoundaddr();
+ if(skipbl() == -1)
+ return 0;
+ if((c=getch())==-1)
+ return 0;
+ cmd.cmdc = c;
+ if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case…
+ getch(); /* the 'd' */
+ cmd.cmdc='c'|0x100;
+ }
+ i = lookup(cmd.cmdc);
+ if(i >= 0){
+ if(cmd.cmdc == '\n')
+ goto Return; /* let nl_cmd work it all out */
+ ct = &cmdtab[i];
+ if(ct->defaddr==aNo && cmd.addr)
+ error(Enoaddr);
+ if(ct->count)
+ cmd.num = getnum();
+ if(ct->regexp){
+ /* x without pattern -> .*\n, indicated by cmd.re==0 */
+ /* X without pattern is all files */
+ if((ct->cmdc!='x' && ct->cmdc!='X') ||
+ ((c = nextc())!=' ' && c!='\t' && c!='\n')){
+ skipbl();
+ if((c = getch())=='\n' || c<0)
+ error(Enopattern);
+ okdelim(c);
+ cmd.re = getregexp(c);
+ if(ct->cmdc == 's'){
+ cmd.ctext = newstring();
+ getrhs(cmd.ctext, c, 's');
+ if(nextc() == c){
+ getch();
+ if(nextc() == 'g')
+ cmd.flag = getch();
+ }
+
+ }
+ }
+ }
+ if(ct->addr && (cmd.caddr=simpleaddr())==0)
+ error(Eaddress);
+ if(ct->defcmd){
+ if(skipbl() == '\n'){
+ getch();
+ cmd.ccmd = newcmd();
+ cmd.ccmd->cmdc = ct->defcmd;
+ }else if((cmd.ccmd = parsecmd(nest))==0)
+ panic("defcmd");
+ }else if(ct->text)
+ cmd.ctext = collecttext();
+ else if(ct->token)
+ cmd.ctext = collecttoken(ct->token);
+ else
+ atnl();
+ }else
+ switch(cmd.cmdc){
+ case '{':
+ cp = 0;
+ do{
+ if(skipbl()=='\n')
+ getch();
+ ncp = parsecmd(nest+1);
+ if(cp)
+ cp->next = ncp;
+ else
+ cmd.ccmd = ncp;
+ }while(cp = ncp);
+ break;
+ case '}':
+ atnl();
+ if(nest==0)
+ error(Enolbrace);
+ return 0;
+ default:
+ error_c(Eunk, cmd.cmdc);
+ }
+ Return:
+ cp = newcmd();
+ *cp = cmd;
+ return cp;
+}
+
+String* /* BUGGERED */
+getregexp(int delim)
+{
+ String *r = newre();
+ int c;
+
+ for(Strzero(&genstr); ; Straddc(&genstr, c))
+ if((c = getch())=='\\'){
+ if(nextc()==delim)
+ c = getch();
+ else if(nextc()=='\\'){
+ Straddc(&genstr, c);
+ c = getch();
+ }
+ }else if(c==delim || c=='\n')
+ break;
+ if(c!=delim && c)
+ ungetch();
+ if(genstr.n > 0){
+ patset = TRUE;
+ Strduplstr(&lastpat, &genstr);
+ Straddc(&lastpat, '\0');
+ }
+ if(lastpat.n <= 1)
+ error(Epattern);
+ Strduplstr(r, &lastpat);
+ return r;
+}
+
+Addr *
+simpleaddr(void)
+{
+ Addr addr;
+ Addr *ap, *nap;
+
+ addr.next = 0;
+ addr.left = 0;
+ switch(skipbl()){
+ case '#':
+ addr.type = getch();
+ addr.num = getnum();
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ addr.num = getnum();
+ addr.type='l';
+ break;
+ case '/': case '?': case '"':
+ addr.are = getregexp(addr.type = getch());
+ break;
+ case '.':
+ case '$':
+ case '+':
+ case '-':
+ case '\'':
+ addr.type = getch();
+ break;
+ default:
+ return 0;
+ }
+ if(addr.next = simpleaddr())
+ switch(addr.next->type){
+ case '.':
+ case '$':
+ case '\'':
+ if(addr.type!='"')
+ case '"':
+ error(Eaddress);
+ break;
+ case 'l':
+ case '#':
+ if(addr.type=='"')
+ break;
+ /* fall through */
+ case '/':
+ case '?':
+ if(addr.type!='+' && addr.type!='-'){
+ /* insert the missing '+' */
+ nap = newaddr();
+ nap->type='+';
+ nap->next = addr.next;
+ addr.next = nap;
+ }
+ break;
+ case '+':
+ case '-':
+ break;
+ default:
+ panic("simpleaddr");
+ }
+ ap = newaddr();
+ *ap = addr;
+ return ap;
+}
+
+Addr *
+compoundaddr(void)
+{
+ Addr addr;
+ Addr *ap, *next;
+
+ addr.left = simpleaddr();
+ if((addr.type = skipbl())!=',' && addr.type!=';')
+ return addr.left;
+ getch();
+ next = addr.next = compoundaddr();
+ if(next && (next->type==',' || next->type==';') && next->left==0)
+ error(Eaddress);
+ ap = newaddr();
+ *ap = addr;
+ return ap;
+}
diff --git a/sam/disc.c b/sam/disc.c
@@ -0,0 +1,335 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+#define BLOCKFILL (BLOCKSIZE/2)
+
+static Discdesc desc[NBUFFILES];
+
+void bkalloc(Disc*, int);
+void bkfree(Disc*, int);
+void bkwrite(Disc*, Rune*, int, int, int);
+void bkread(Disc*, Rune*, int, int, int);
+
+
+Discdesc *
+Dstart(void)
+{
+ int i, fd;
+ Discdesc *dd;
+
+ for(i=0, dd=desc; dd->fd; i++, dd++)
+ if(i == NBUFFILES-1)
+ panic("too many buffer files");
+ fd = newtmp(i);
+ if(fd < 0)
+ panic("can't create buffer file");
+ dd->fd = fd;
+ return dd;
+}
+
+Disc *
+Dopen(Discdesc *dd)
+{
+ Disc *d;
+
+ d = emalloc(sizeof(Disc));
+ d->desc = dd;
+ return d;
+}
+
+void
+Dclose(Disc *d)
+{
+ int i;
+
+ for(i=d->block.nused; --i>=0; ) /* backwards because bkfree() s…
+ bkfree(d, i);
+ free(d->block.listptr);
+ free(d);
+}
+
+int
+Dread(Disc *d, Rune *addr, int n, Posn p1)
+{
+ int i, nb, nr;
+ Posn p = 0, p2 = p1+n;
+
+ for(i=0; i<d->block.nused; i++){
+ if((p+=d->block.blkptr[i].nrunes) > p1){
+ p -= d->block.blkptr[i].nrunes;
+ goto out;
+ }
+ }
+ if(p == p1)
+ return 0; /* eof */
+ return -1; /* past eof */
+
+ out:
+ n = 0;
+ if(p != p1){ /* trailing partial block */
+ nb = d->block.blkptr[i].nrunes;
+ if(p2 > p+nb)
+ nr = nb-(p1-p);
+ else
+ nr = p2-p1;
+ bkread(d, addr, nr, i, p1-p);
+ /* advance to next block */
+ p += nb;
+ addr += nr;
+ n += nr;
+ i++;
+ }
+ /* whole blocks */
+ while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
+ if(i >= d->block.nused)
+ return n; /* eof */
+ bkread(d, addr, nb, i, 0);
+ p += nb;
+ addr += nb;
+ n += nb;
+ i++;
+ }
+ if(p < p2){ /* any initial partial block left? */
+ nr = p2-p;
+ nb = d->block.blkptr[i].nrunes;
+ if(nr>nb)
+ nr = nb; /* eof */
+ /* just read in the part that survives */
+ bkread(d, addr, nr, i, 0);
+ n += nr;
+ }
+ return n;
+}
+
+void
+Dinsert(Disc *d, Rune *addr, int n, Posn p0) /* if addr null, just make space …
+{
+ int i, nb, ni;
+ Posn p = 0;
+ Rune hold[BLOCKSIZE];
+ int nhold;
+
+ for(i=0; i<d->block.nused; i++){
+ if((p+=d->block.blkptr[i].nrunes) >= p0){
+ p -= d->block.blkptr[i].nrunes;
+ goto out;
+ }
+ }
+ if(p != p0)
+ panic("Dinsert"); /* beyond eof */
+
+ out:
+ d->nrunes += n;
+ nhold = 0;
+ if(i<d->block.nused && (nb=d->block.blkptr[i].nrunes)>p0-p){
+ nhold = nb-(p0-p);
+ bkread(d, hold, nhold, i, p0-p);
+ d->block.blkptr[i].nrunes -= nhold; /* no write necessa…
+ }
+ /* insertion point is now at end of block i (which may not exist) */
+ while(n > 0){
+ if(i < d->block.nused
+ && (nb=d->block.blkptr[i].nrunes) < BLOCKFILL){
+ /* fill this block */
+ if(nb+n > BLOCKSIZE)
+ ni = BLOCKFILL-nb;
+ else
+ ni = n;
+ if(addr)
+ bkwrite(d, addr, ni, i, nb);
+ nb += ni;
+ }else{ /* make new block */
+ if(i < d->block.nused)
+ i++; /* put after this block, if it exi…
+ bkalloc(d, i);
+ if(n > BLOCKSIZE)
+ ni = BLOCKFILL;
+ else
+ ni = n;
+ if(addr)
+ bkwrite(d, addr, ni, i, 0);
+ nb = ni;
+ }
+ d->block.blkptr[i].nrunes = nb;
+ if(addr)
+ addr += ni;
+ n -= ni;
+ }
+ if(nhold){
+ if(i < d->block.nused
+ && (nb=d->block.blkptr[i].nrunes)+nhold < BLOCKSIZE){
+ /* fill this block */
+ bkwrite(d, hold, nhold, i, nb);
+ nb += nhold;
+ }else{ /* make new block */
+ if(i < d->block.nused)
+ i++; /* put after this block, if it exi…
+ bkalloc(d, i);
+ bkwrite(d, hold, nhold, i, 0);
+ nb = nhold;
+ }
+ d->block.blkptr[i].nrunes = nb;
+ }
+}
+
+void
+Ddelete(Disc *d, Posn p1, Posn p2)
+{
+ int i, nb, nd;
+ Posn p = 0;
+ Rune buf[BLOCKSIZE];
+
+ for(i = 0; i<d->block.nused; i++){
+ if((p+=d->block.blkptr[i].nrunes) > p1){
+ p -= d->block.blkptr[i].nrunes;
+ goto out;
+ }
+ }
+ if(p1!=d->nrunes || p2!=p1)
+ panic("Ddelete");
+ return; /* beyond eof */
+
+ out:
+ d->nrunes -= p2-p1;
+ if(p != p1){ /* throw away partial block */
+ nb = d->block.blkptr[i].nrunes;
+ bkread(d, buf, nb, i, 0);
+ if(p2 >= p+nb)
+ nd = nb-(p1-p);
+ else{
+ nd = p2-p1;
+ memmove(buf+(p1-p), buf+(p1-p)+nd, RUNESIZE*(nb-((p1-p…
+ }
+ nb -= nd;
+ bkwrite(d, buf, nb, i, 0);
+ d->block.blkptr[i].nrunes = nb;
+ p2 -= nd;
+ /* advance to next block */
+ p += nb;
+ i++;
+ }
+ /* throw away whole blocks */
+ while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
+ if(i >= d->block.nused)
+ panic("Ddelete 2");
+ bkfree(d, i);
+ p2 -= nb;
+ }
+ if(p >= p2) /* any initial partial block left to delete? */
+ return; /* no */
+ nd = p2-p;
+ nb = d->block.blkptr[i].nrunes;
+ /* just read in the part that survives */
+ bkread(d, buf, nb-=nd, i, nd);
+ /* a little block merging */
+ if(nb<BLOCKSIZE/2 && i>0 && (nd = d->block.blkptr[i-1].nrunes)<BLOCKSI…
+ memmove(buf+nd, buf, RUNESIZE*nb);
+ bkread(d, buf, nd, --i, 0);
+ bkfree(d, i);
+ nb += nd;
+ }
+ bkwrite(d, buf, nb, i, 0);
+ d->block.blkptr[i].nrunes = nb;
+}
+
+void
+Dreplace(Disc *d, Posn p1, Posn p2, Rune *addr, int n)
+{
+ int i, nb, nr;
+ Posn p = 0;
+ Rune buf[BLOCKSIZE];
+
+ if(p2-p1 > n)
+ Ddelete(d, p1+n, p2);
+ else if(p2-p1 < n)
+ Dinsert(d, 0, n-(p2-p1), p2);
+ if(n == 0)
+ return;
+ p2 = p1+n;
+ /* they're now conformal; replace in place */
+ for(i=0; i<d->block.nused; i++){
+ if((p+=d->block.blkptr[i].nrunes) > p1){
+ p -= d->block.blkptr[i].nrunes;
+ goto out;
+ }
+ }
+ panic("Dreplace");
+
+ out:
+ if(p != p1){ /* trailing partial block */
+ nb = d->block.blkptr[i].nrunes;
+ bkread(d, buf, nb, i, 0);
+ if(p2 > p+nb)
+ nr = nb-(p1-p);
+ else
+ nr = p2-p1;
+ memmove(buf+p1-p, addr, RUNESIZE*nr);
+ bkwrite(d, buf, nb, i, 0);
+ /* advance to next block */
+ p += nb;
+ addr += nr;
+ i++;
+ }
+ /* whole blocks */
+ while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
+ if(i >= d->block.nused)
+ panic("Dreplace 2");
+ bkwrite(d, addr, nb, i, 0);
+ p += nb;
+ addr += nb;
+ i++;
+ }
+ if(p < p2){ /* any initial partial block left? */
+ nr = p2-p;
+ nb = d->block.blkptr[i].nrunes;
+ /* just read in the part that survives */
+ bkread(d, buf+nr, nb-nr, i, nr);
+ memmove(buf, addr, RUNESIZE*nr);
+ bkwrite(d, buf, nb, i, 0);
+ }
+}
+
+void
+bkread(Disc *d, Rune *loc, int n, int bk, int off)
+{
+ Seek(d->desc->fd, RUNESIZE*(BLOCKSIZE*d->block.blkptr[bk].bnum+off), 0…
+ Read(d->desc->fd, loc, n*RUNESIZE);
+}
+
+void
+bkwrite(Disc *d, Rune *loc, int n, int bk, int off)
+{
+ Seek(d->desc->fd, RUNESIZE*(BLOCKSIZE*d->block.blkptr[bk].bnum+off), 0…
+ Write(d->desc->fd, loc, n*RUNESIZE);
+ /*
+ * sleazy hack to avoid silly SGI kernel bug
+ * fill partial block with garbage
+ */
+ if (off+n >= d->block.blkptr[bk].nrunes && off+n < BLOCKSIZE)
+ Write(d->desc->fd, genbuf, (BLOCKSIZE-(off+n))*RUNESIZE);
+}
+
+void
+bkalloc(Disc *d, int n)
+{
+ Discdesc *dd = d->desc;
+ ulong bnum;
+
+ if(dd->free.nused)
+ bnum = dd->free.longptr[--dd->free.nused];
+ else
+ bnum = dd->nbk++;
+ if(bnum >= 1<<(8*(sizeof(((Block*)0)->bnum))))
+ error(Etmpovfl);
+ inslist(&d->block, n, 0L);
+ d->block.blkptr[n].bnum = bnum;
+}
+
+void
+bkfree(Disc *d, int n)
+{
+ Discdesc *dd = d->desc;
+
+ inslist(&dd->free, dd->free.nused, d->block.blkptr[n].bnum);
+ dellist(&d->block, n);
+}
diff --git a/sam/error.c b/sam/error.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+static char *emsg[]={
+ /* error_s */
+ "can't open",
+ "can't create",
+ "not in menu:",
+ "changes to",
+ "I/O error:",
+ /* error_c */
+ "unknown command",
+ "no operand for",
+ "bad delimiter",
+ /* error */
+ "can't fork",
+ "interrupt",
+ "address",
+ "search",
+ "pattern",
+ "newline expected",
+ "blank expected",
+ "pattern expected",
+ "can't nest X or Y",
+ "unmatched `}'",
+ "command takes no address",
+ "addresses overlap",
+ "substitution",
+ "& match too long",
+ "bad \\ in rhs",
+ "address range",
+ "changes not in sequence",
+ "addresses out of order",
+ "no file name",
+ "unmatched `('",
+ "unmatched `)'",
+ "malformed `[]'",
+ "malformed regexp",
+ "reg. exp. list overflow",
+ "plan 9 command",
+ "can't pipe",
+ "no current file",
+ "string too long",
+ "changed files",
+ "empty string",
+ "file search",
+ "non-unique match for \"\"",
+ "tag match too long",
+ "too many subexpressions",
+ "temporary file too large",
+ "file is append-only",
+};
+static char *wmsg[]={
+ /* warn_s */
+ "duplicate file name",
+ "no such file",
+ "write might change good version of",
+ /* warn_S */
+ "files might be aliased",
+ /* warn */
+ "null characters elided",
+ "can't run pwd",
+ "last char not newline",
+ "exit status not 0",
+};
+
+void
+error(Err s)
+{
+ char buf[512];
+
+ sprint(buf, "?%s", emsg[s]);
+ hiccough(buf);
+}
+
+void
+error_s(Err s, char *a)
+{
+ char buf[512];
+
+ sprint(buf, "?%s \"%s\"", emsg[s], a);
+ hiccough(buf);
+}
+
+void
+error_c(Err s, int c)
+{
+ char buf[512];
+
+ sprint(buf, "?%s `%c'", emsg[s], c);
+ hiccough(buf);
+}
+
+void
+warn(Warn s)
+{
+ dprint("?warning: %s\n", wmsg[s]);
+}
+
+void
+warn_S(Warn s, String *a)
+{
+ print_s(wmsg[s], a);
+}
+
+void
+warn_SS(Warn s, String *a, String *b)
+{
+ print_ss(wmsg[s], a, b);
+}
+
+void
+warn_s(Warn s, char *a)
+{
+ dprint("?warning: %s `%s'\n", wmsg[s], a);
+}
+
+void
+termwrite(char *s)
+{
+ String *p;
+
+ if(downloaded){
+ p = tmpcstr(s);
+ if(cmd)
+ Finsert(cmd, p, cmdpt);
+ else
+ Strinsert(&cmdstr, p, cmdstr.n);
+ cmdptadv += p->n;
+ }else
+ Write(2, s, strlen(s));
+}
diff --git a/sam/errors.h b/sam/errors.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+typedef enum Err{
+ /* error_s */
+ Eopen,
+ Ecreate,
+ Emenu,
+ Emodified,
+ Eio,
+ /* error_c */
+ Eunk,
+ Emissop,
+ Edelim,
+ /* error */
+ Efork,
+ Eintr,
+ Eaddress,
+ Esearch,
+ Epattern,
+ Enewline,
+ Eblank,
+ Enopattern,
+ EnestXY,
+ Enolbrace,
+ Enoaddr,
+ Eoverlap,
+ Enosub,
+ Elongrhs,
+ Ebadrhs,
+ Erange,
+ Esequence,
+ Eorder,
+ Enoname,
+ Eleftpar,
+ Erightpar,
+ Ebadclass,
+ Ebadregexp,
+ Eoverflow,
+ Enocmd,
+ Epipe,
+ Enofile,
+ Etoolong,
+ Echanges,
+ Eempty,
+ Efsearch,
+ Emanyfiles,
+ Elongtag,
+ Esubexp,
+ Etmpovfl,
+ Eappend
+}Err;
+typedef enum Warn{
+ /* warn_s */
+ Wdupname,
+ Wfile,
+ Wdate,
+ /* warn_ss */
+ Wdupfile,
+ /* warn */
+ Wnulls,
+ Wpwd,
+ Wnotnewline,
+ Wbadstatus
+}Warn;
diff --git a/sam/file.c b/sam/file.c
@@ -0,0 +1,465 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+/*
+ * Files are splayed out a factor of NDISC to reduce indirect block access
+ */
+Discdesc *files[NDISC];
+Discdesc *transcripts[NDISC];
+Buffer *undobuf;
+static String *ftempstr(Rune*, int);
+int fcount;
+File *lastfile;
+
+void puthdr_csl(Buffer*, char, short, Posn);
+void puthdr_cs(Buffer*, char, short);
+void puthdr_M(Buffer*, Posn, Range, Range, Mod, short);
+void puthdr_cll(Buffer*, char, Posn, Posn);
+void Fflush(File*);
+
+enum{
+ SKIP=50, /* max dist between file changes folded togeth…
+ MAXCACHE=STRSIZE /* max length of cache. must be < 32K-BLOCKSIZ…
+};
+
+void
+Fstart(void)
+{
+ undobuf = Bopen(Dstart());
+ snarfbuf = Bopen(Dstart());
+ plan9buf = Bopen(Dstart());
+}
+
+void
+Fmark(File *f, Mod m)
+{
+ Buffer *t = f->transcript;
+ Posn p;
+
+ if(f->state == Readerr)
+ return;
+ if(f->state == Unread) /* this is implicit 'e' of a file */
+ return;
+ p = m==0? -1 : f->markp;
+ f->markp = t->nrunes;
+ puthdr_M(t, p, f->dot.r, f->mark, f->mod, f->state);
+ f->ndot = f->dot;
+ f->marked = TRUE;
+ f->mod = m;
+ f->hiposn = -1;
+ /* Safety first */
+ f->cp1 = f->cp2 = 0;
+}
+
+File *
+Fopen(void)
+{
+ File *f;
+
+ f = emalloc(sizeof(File));
+ if(files[fcount] == 0){
+ files[fcount] = Dstart();
+ transcripts[fcount] = Dstart();
+ }
+ f->buf = Bopen(files[fcount]);
+ f->transcript = Bopen(transcripts[fcount]);
+ if(++fcount == NDISC)
+ fcount = 0;
+ f->nrunes = 0;
+ f->markp = 0;
+ f->mod = 0;
+ f->dot.f = f;
+ f->ndot.f = f;
+ f->dev = ~0;
+ f->qid = ~0;
+ Strinit0(&f->name);
+ Strinit(&f->cache);
+ f->state = Unread;
+ Fmark(f, (Mod)0);
+ return f;
+}
+
+void
+Fclose(File *f)
+{
+ if(f == lastfile)
+ lastfile = 0;
+ Bterm(f->buf);
+ Bterm(f->transcript);
+ Strclose(&f->name);
+ Strclose(&f->cache);
+ if(f->rasp)
+ listfree(f->rasp);
+ free(f);
+}
+
+void
+Finsert(File *f, String *str, Posn p1)
+{
+ Buffer *t = f->transcript;
+
+ if(f->state == Readerr)
+ return;
+ if(str->n == 0)
+ return;
+ if(str->n<0 || str->n>STRSIZE)
+ panic("Finsert");
+ if(f->mod < modnum)
+ Fmark(f, modnum);
+ if(p1 < f->hiposn)
+ error(Esequence);
+ if(str->n >= BLOCKSIZE){ /* don't bother with the cache */
+ Fflush(f);
+ puthdr_csl(t, 'i', str->n, p1);
+ Binsert(t, str, t->nrunes);
+ }else{ /* insert into the cache instead of the transcript */
+ if(f->cp2==0 && f->cp1==0 && f->cache.n==0) /* empty ca…
+ f->cp1 = f->cp2 = p1;
+ if(p1-f->cp2>SKIP || f->cache.n+str->n>MAXCACHE-SKIP){
+ Fflush(f);
+ f->cp1 = f->cp2 = p1;
+ }
+ if(f->cp2 != p1){ /* grab the piece in between */
+ Rune buf[SKIP];
+ String s;
+ Fchars(f, buf, f->cp2, p1);
+ s.s = buf;
+ s.n = p1-f->cp2;
+ Strinsert(&f->cache, &s, f->cache.n);
+ f->cp2 = p1;
+ }
+ Strinsert(&f->cache, str, f->cache.n);
+ }
+ if(f != cmd)
+ quitok = FALSE;
+ f->closeok = FALSE;
+ if(f->state == Clean)
+ state(f, Dirty);
+ f->hiposn = p1;
+}
+
+void
+Fdelete(File *f, Posn p1, Posn p2)
+{
+ if(f->state == Readerr)
+ return;
+ if(p1==p2)
+ return;
+ if(f->mod<modnum)
+ Fmark(f, modnum);
+ if(p1<f->hiposn)
+ error(Esequence);
+ if(p1-f->cp2>SKIP)
+ Fflush(f);
+ if(f->cp2==0 && f->cp1==0 && f->cache.n==0) /* empty cache */
+ f->cp1 = f->cp2 = p1;
+ if(f->cp2 != p1){ /* grab the piece in between */
+ if(f->cache.n+(p1-f->cp2)>MAXCACHE){
+ Fflush(f);
+ f->cp1 = f->cp2 = p1;
+ }else{
+ Rune buf[SKIP];
+ String s;
+ Fchars(f, buf, f->cp2, p1);
+ s.s = buf;
+ s.n = p1-f->cp2;
+ Strinsert(&f->cache, &s, f->cache.n);
+ }
+ }
+ f->cp2 = p2;
+ if(f!=cmd)
+ quitok = FALSE;
+ f->closeok = FALSE;
+ if(f->state==Clean)
+ state(f, Dirty);
+ f->hiposn = p2;
+}
+
+void
+Fflush(File *f)
+{
+ Buffer *t = f->transcript;
+ Posn p1 = f->cp1, p2 = f->cp2;
+
+ if(f->state == Readerr)
+ return;
+ if(p1 != p2)
+ puthdr_cll(t, 'd', p1, p2);
+ if(f->cache.n){
+ puthdr_csl(t, 'i', f->cache.n, p2);
+ Binsert(t, &f->cache, t->nrunes);
+ Strzero(&f->cache);
+ }
+ f->cp1 = f->cp2 = 0;
+}
+
+void
+Fsetname(File *f, String *s)
+{
+ Buffer *t = f->transcript;
+
+ if(f->state == Readerr)
+ return;
+ if(f->state == Unread){ /* This is setting initial file name */
+ Strduplstr(&f->name, s);
+ sortname(f);
+ }else{
+ if(f->mod < modnum)
+ Fmark(f, modnum);
+ puthdr_cs(t, 'f', s->n);
+ Binsert(t, s, t->nrunes);
+ }
+}
+
+/*
+ * The heart of it all. Fupdate will run along the transcript list, executing
+ * the commands and converting them into their inverses for a later undo pass.
+ * The pass runs top to bottom, so addresses in the transcript are tracked
+ * (by the var. delta) so they stay valid during the operation. This causes
+ * all operations to appear to happen simultaneously, which is why the address…
+ * passed to Fdelete and Finsert never take into account other changes occurri…
+ * in this command (and is why things are done this way).
+ */
+int
+Fupdate(File *f, int mktrans, int toterm)
+{
+ Buffer *t = f->transcript;
+ Buffer *u = undobuf;
+ int n, ni;
+ Posn p0, p1, p2, p, deltadot = 0, deltamark = 0, delta = 0;
+ int changes = FALSE;
+ union Hdr buf;
+ Rune tmp[BLOCKSIZE+1]; /* +1 for NUL in 'f' case */
+
+ if(f->state == Readerr)
+ return FALSE;
+ if(lastfile && f!=lastfile)
+ Bclean(lastfile->transcript); /* save memory when multi…
+ lastfile = f;
+ Fflush(f);
+ if(f->marked)
+ p0 = f->markp+sizeof(Mark)/RUNESIZE;
+ else
+ p0 = 0;
+ f->dot = f->ndot;
+ while((n=Bread(t, (Rune*)&buf, sizeof buf/RUNESIZE, p0)) > 0){
+ switch(buf.cs.c){
+ default:
+ panic("unknown in Fupdate");
+ case 'd':
+ p1 = buf.cll.l;
+ p2 = buf.cll.l1;
+ p0 += sizeof(struct _cll)/RUNESIZE;
+ if(p2 <= f->dot.r.p1)
+ deltadot -= p2-p1;
+ if(p2 <= f->mark.p1)
+ deltamark -= p2-p1;
+ p1 += delta, p2+=delta;
+ delta -= p2-p1;
+ if(!mktrans)
+ for(p = p1; p<p2; p+=ni){
+ if(p2-p>BLOCKSIZE)
+ ni = BLOCKSIZE;
+ else
+ ni = p2-p;
+ puthdr_csl(u, 'i', ni, p1);
+ Bread(f->buf, tmp, ni, p);
+ Binsert(u, ftempstr(tmp, ni), u->nrune…
+ }
+ f->nrunes -= p2-p1;
+ Bdelete(f->buf, p1, p2);
+ changes = TRUE;
+ break;
+
+ case 'f':
+ n = buf.cs.s;
+ p0 += sizeof(struct _cs)/RUNESIZE;
+ Strinsure(&genstr, n+1);
+ Bread(t, tmp, n, p0);
+ tmp[n] = 0;
+ p0 += n;
+ Strdupl(&genstr, tmp);
+ if(!mktrans){
+ puthdr_cs(u, 'f', f->name.n);
+ Binsert(u, &f->name, u->nrunes);
+ }
+ Strduplstr(&f->name, &genstr);
+ sortname(f);
+ changes = TRUE;
+ break;
+
+ case 'i':
+ n = buf.csl.s;
+ p1 = buf.csl.l;
+ p0 += sizeof(struct _csl)/RUNESIZE;
+ if(p1 < f->dot.r.p1)
+ deltadot += n;
+ if(p1 < f->mark.p1)
+ deltamark += n;
+ p1 += delta;
+ delta += n;
+ if(!mktrans)
+ puthdr_cll(u, 'd', p1, p1+n);
+ changes = TRUE;
+ f->nrunes += n;
+ while(n > 0){
+ if(n > BLOCKSIZE)
+ ni = BLOCKSIZE;
+ else
+ ni = n;
+ Bread(t, tmp, ni, p0);
+ Binsert(f->buf, ftempstr(tmp, ni), p1);
+ n -= ni;
+ p1 += ni;
+ p0 += ni;
+ }
+ break;
+ }
+ }
+ toterminal(f, toterm);
+ f->dot.r.p1 += deltadot;
+ f->dot.r.p2 += deltadot;
+ if(f->dot.r.p1 > f->nrunes)
+ f->dot.r.p1 = f->nrunes;
+ if(f->dot.r.p2 > f->nrunes)
+ f->dot.r.p2 = f->nrunes;
+ f->mark.p1 += deltamark;
+ f->mark.p2 += deltamark;
+ if(f->mark.p1 > f->nrunes)
+ f->mark.p1 = f->nrunes;
+ if(f->mark.p2 > f->nrunes)
+ f->mark.p2 = f->nrunes;
+ if(n < 0)
+ panic("Fupdate read");
+ if(f == cmd)
+ f->mod = 0; /* can't undo command file */
+ if(p0 > f->markp+sizeof(Posn)/RUNESIZE){ /* for undo, this thro…
+ if(f->mod > 0){ /* can't undo the dawn of time */
+ Bdelete(t, f->markp+sizeof(Mark)/RUNESIZE, t->nrunes);
+ /* copy the undo list back into the transcript */
+ for(p = 0; p<u->nrunes; p+=ni){
+ if(u->nrunes-p>BLOCKSIZE)
+ ni = BLOCKSIZE;
+ else
+ ni = u->nrunes-p;
+ Bread(u, tmp, ni, p);
+ Binsert(t, ftempstr(tmp, ni), t->nrunes);
+ }
+ }
+ Bdelete(u, (Posn)0, u->nrunes);
+ }
+ return f==cmd? FALSE : changes;
+}
+
+void
+puthdr_csl(Buffer *b, char c, short s, Posn p)
+{
+ struct _csl buf;
+
+ if(p < 0)
+ panic("puthdr_csP");
+ buf.c = c;
+ buf.s = s;
+ buf.l = p;
+ Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
+}
+
+void
+puthdr_cs(Buffer *b, char c, short s)
+{
+ struct _cs buf;
+
+ buf.c = c;
+ buf.s = s;
+ Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
+}
+
+void
+puthdr_M(Buffer *b, Posn p, Range dot, Range mk, Mod m, short s1)
+{
+ Mark mark;
+ static first = 1;
+
+ if(!first && p<0)
+ panic("puthdr_M");
+ mark.p = p;
+ mark.dot = dot;
+ mark.mark = mk;
+ mark.m = m;
+ mark.s1 = s1;
+ Binsert(b, ftempstr((Rune *)&mark, sizeof mark/RUNESIZE), b->nrunes);
+}
+
+void
+puthdr_cll(Buffer *b, char c, Posn p1, Posn p2)
+{
+ struct _cll buf;
+
+ if(p1<0 || p2<0)
+ panic("puthdr_cll");
+ buf.c = c;
+ buf.l = p1;
+ buf.l1 = p2;
+ Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
+}
+
+long
+Fchars(File *f, Rune *addr, Posn p1, Posn p2)
+{
+ return Bread(f->buf, addr, p2-p1, p1);
+}
+
+int
+Fgetcset(File *f, Posn p)
+{
+ if(p<0 || p>f->nrunes)
+ panic("Fgetcset out of range");
+ if((f->ngetc = Fchars(f, f->getcbuf, p, p+NGETC))<0)
+ panic("Fgetcset Bread fail");
+ f->getcp = p;
+ f->getci = 0;
+ return f->ngetc;
+}
+
+int
+Fbgetcset(File *f, Posn p)
+{
+ if(p<0 || p>f->nrunes)
+ panic("Fbgetcset out of range");
+ if((f->ngetc = Fchars(f, f->getcbuf, p<NGETC? (Posn)0 : p-NGETC, p))<0)
+ panic("Fbgetcset Bread fail");
+ f->getcp = p;
+ f->getci = f->ngetc;
+ return f->ngetc;
+}
+
+int
+Fgetcload(File *f, Posn p)
+{
+ if(Fgetcset(f, p)){
+ --f->ngetc;
+ f->getcp++;
+ return f->getcbuf[f->getci++];
+ }
+ return -1;
+}
+
+int
+Fbgetcload(File *f, Posn p)
+{
+ if(Fbgetcset(f, p)){
+ --f->getcp;
+ return f->getcbuf[--f->getci];
+ }
+ return -1;
+}
+
+static String*
+ftempstr(Rune *s, int n)
+{
+ static String p;
+
+ p.s = s;
+ p.n = n;
+ p.size = n;
+ return &p;
+}
diff --git a/sam/io.c b/sam/io.c
@@ -0,0 +1,257 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+#define NSYSFILE 3
+#define NOFILE 128
+
+void
+checkqid(File *f)
+{
+ int i, w;
+ File *g;
+
+ w = whichmenu(f);
+ for(i=1; i<file.nused; i++){
+ g = file.filepptr[i];
+ if(w == i)
+ continue;
+ if(f->dev==g->dev && f->qid==g->qid)
+ warn_SS(Wdupfile, &f->name, &g->name);
+ }
+}
+
+void
+writef(File *f)
+{
+ Rune c;
+ Posn n;
+ char *name;
+ int i, samename, newfile;
+ ulong dev, qid;
+ long mtime, appendonly, length;
+
+ newfile = 0;
+ samename = Strcmp(&genstr, &f->name) == 0;
+ name = Strtoc(&f->name);
+ i = statfile(name, &dev, &qid, &mtime, 0, 0);
+ if(i == -1)
+ newfile++;
+ else if(samename &&
+ (f->dev!=dev || f->qid!=qid || f->date<mtime)){
+ f->dev = dev;
+ f->qid = qid;
+ f->date = mtime;
+ warn_S(Wdate, &genstr);
+ return;
+ }
+ if(genc)
+ free(genc);
+ genc = Strtoc(&genstr);
+ if((io=create(genc, 1, 0666L)) < 0)
+ error_s(Ecreate, genc);
+ dprint("%s: ", genc);
+ if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && leng…
+ error(Eappend);
+ n = writeio(f);
+ if(f->name.s[0]==0 || samename)
+ state(f, addr.r.p1==0 && addr.r.p2==f->nrunes? Clean : Dirty);
+ if(newfile)
+ dprint("(new file) ");
+ if(addr.r.p2>0 && Fchars(f, &c, addr.r.p2-1, addr.r.p2) && c!='\n')
+ warn(Wnotnewline);
+ closeio(n);
+ if(f->name.s[0]==0 || samename){
+ if(statfile(name, &dev, &qid, &mtime, 0, 0) > 0){
+ f->dev = dev;
+ f->qid = qid;
+ f->date = mtime;
+ checkqid(f);
+ }
+ }
+}
+
+Posn
+readio(File *f, int *nulls, int setdate)
+{
+ int n, b, w;
+ Rune *r;
+ Posn nt;
+ Posn p = addr.r.p2;
+ ulong dev, qid;
+ long mtime;
+ char buf[BLOCKSIZE+1], *s;
+
+ *nulls = FALSE;
+ b = 0;
+ for(nt = 0; (n = read(io, buf+b, BLOCKSIZE-b))>0; nt+=(r-genbuf)){
+ n += b;
+ b = 0;
+ r = genbuf;
+ s = buf;
+ while(n > 0){
+ if((*r = *(uchar*)s) < Runeself){
+ if(*r)
+ r++;
+ else
+ *nulls = TRUE;
+ --n;
+ s++;
+ continue;
+ }
+ if(fullrune(s, n)){
+ w = chartorune(r, s);
+ if(*r)
+ r++;
+ else
+ *nulls = TRUE;
+ n -= w;
+ s += w;
+ continue;
+ }
+ b = n;
+ memmove(buf, s, b);
+ break;
+ }
+ Finsert(f, tmprstr(genbuf, r-genbuf), p);
+ }
+ if(b)
+ *nulls = TRUE;
+ if(*nulls)
+ warn(Wnulls);
+ if(setdate){
+ if(statfd(io, &dev, &qid, &mtime, 0, 0) > 0){
+ f->dev = dev;
+ f->qid = qid;
+ f->date = mtime;
+ checkqid(f);
+ }
+ }
+ return nt;
+}
+
+Posn
+writeio(File *f)
+{
+ int m, n;
+ Posn p = addr.r.p1;
+ char *c;
+
+ while(p < addr.r.p2){
+ if(addr.r.p2-p>BLOCKSIZE)
+ n = BLOCKSIZE;
+ else
+ n = addr.r.p2-p;
+ if(Fchars(f, genbuf, p, p+n)!=n)
+ panic("writef read");
+ c = Strtoc(tmprstr(genbuf, n));
+ m = strlen(c);
+ if (m < n)
+ panic("corrupted file");
+ if(Write(io, c, m) != m){
+ free(c);
+ if(p > 0)
+ p += n;
+ break;
+ }
+ free(c);
+ p += n;
+ }
+ return p-addr.r.p1;
+}
+void
+closeio(Posn p)
+{
+ close(io);
+ io = 0;
+ if(p >= 0)
+ dprint("#%lu\n", p);
+}
+
+int remotefd0 = 0;
+int remotefd1 = 1;
+
+void
+bootterm(char *machine, char **argv, char **end)
+{
+ int ph2t[2], pt2h[2];
+
+ if(machine){
+ dup(remotefd0, 0);
+ dup(remotefd1, 1);
+ close(remotefd0);
+ close(remotefd1);
+ argv[0] = "samterm";
+ *end = 0;
+ exec(samterm, argv);
+ fprint(2, "can't exec: ");
+ perror(samterm);
+ _exits("damn");
+ }
+ if(pipe(ph2t)==-1 || pipe(pt2h)==-1)
+ panic("pipe");
+ switch(fork()){
+ case 0:
+ dup(ph2t[0], 0);
+ dup(pt2h[1], 1);
+ close(ph2t[0]);
+ close(ph2t[1]);
+ close(pt2h[0]);
+ close(pt2h[1]);
+ argv[0] = "samterm";
+ *end = 0;
+ exec(samterm, argv);
+ fprint(2, "can't exec: ");
+ perror(samterm);
+ _exits("damn");
+ case -1:
+ panic("can't fork samterm");
+ }
+ dup(pt2h[0], 0);
+ dup(ph2t[1], 1);
+ close(ph2t[0]);
+ close(ph2t[1]);
+ close(pt2h[0]);
+ close(pt2h[1]);
+}
+
+void
+connectto(char *machine)
+{
+ int p1[2], p2[2];
+
+ if(pipe(p1)<0 || pipe(p2)<0){
+ dprint("can't pipe\n");
+ exits("pipe");
+ }
+ remotefd0 = p1[0];
+ remotefd1 = p2[1];
+ switch(fork()){
+ case 0:
+ dup(p2[0], 0);
+ dup(p1[1], 1);
+ close(p1[0]);
+ close(p1[1]);
+ close(p2[0]);
+ close(p2[1]);
+ execl(getenv("RSH") ? getenv("RSH") : RXPATH, getenv("RSH") ? …
+ dprint("can't exec %s\n", RXPATH);
+ exits("exec");
+
+ case -1:
+ dprint("can't fork\n");
+ exits("fork");
+ }
+ close(p1[1]);
+ close(p2[0]);
+}
+
+void
+startup(char *machine, int Rflag, char **argv, char **end)
+{
+ if(machine)
+ connectto(machine);
+ if(!Rflag)
+ bootterm(machine, argv, end);
+ downloaded = 1;
+ outTs(Hversion, VERSION);
+}
diff --git a/sam/list.c b/sam/list.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+/*
+ * Check that list has room for one more element.
+ */
+void
+growlist(List *l)
+{
+ if(l->listptr==0 || l->nalloc==0){
+ l->nalloc = INCR;
+ l->listptr = emalloc(INCR*sizeof(long));
+ l->nused = 0;
+ }else if(l->nused == l->nalloc){
+ l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(long…
+ memset((void*)(l->longptr+l->nalloc), 0, INCR*sizeof(long));
+ l->nalloc += INCR;
+ }
+}
+
+/*
+ * Remove the ith element from the list
+ */
+void
+dellist(List *l, int i)
+{
+ memmove(&l->longptr[i], &l->longptr[i+1], (l->nused-(i+1))*sizeof(long…
+ l->nused--;
+}
+
+/*
+ * Add a new element, whose position is i, to the list
+ */
+void
+inslist(List *l, int i, long val)
+{
+ growlist(l);
+ memmove(&l->longptr[i+1], &l->longptr[i], (l->nused-i)*sizeof(long));
+ l->longptr[i] = val;
+ l->nused++;
+}
+
+void
+listfree(List *l)
+{
+ free(l->listptr);
+ free(l);
+}
diff --git a/sam/mesg.c b/sam/mesg.c
@@ -0,0 +1,762 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+Header h;
+uchar indata[DATASIZE];
+uchar outdata[2*DATASIZE+3]; /* room for overflow message */
+uchar *inp;
+uchar *outp;
+uchar *outmsg = outdata;
+Posn cmdpt;
+Posn cmdptadv;
+Buffer *snarfbuf;
+int waitack;
+int noflush;
+int tversion;
+
+long inlong(void);
+long invlong(void);
+int inshort(void);
+int inmesg(Tmesg);
+void setgenstr(File*, Posn, Posn);
+#ifdef DEBUG
+char *hname[] = {
+ [Hversion] "Hversion",
+ [Hbindname] "Hbindname",
+ [Hcurrent] "Hcurrent",
+ [Hnewname] "Hnewname",
+ [Hmovname] "Hmovname",
+ [Hgrow] "Hgrow",
+ [Hcheck0] "Hcheck0",
+ [Hcheck] "Hcheck",
+ [Hunlock] "Hunlock",
+ [Hdata] "Hdata",
+ [Horigin] "Horigin",
+ [Hunlockfile] "Hunlockfile",
+ [Hsetdot] "Hsetdot",
+ [Hgrowdata] "Hgrowdata",
+ [Hmoveto] "Hmoveto",
+ [Hclean] "Hclean",
+ [Hdirty] "Hdirty",
+ [Hcut] "Hcut",
+ [Hsetpat] "Hsetpat",
+ [Hdelname] "Hdelname",
+ [Hclose] "Hclose",
+ [Hsetsnarf] "Hsetsnarf",
+ [Hsnarflen] "Hsnarflen",
+ [Hack] "Hack",
+ [Hextcmd] "Hextcmd",
+ [Hexit] "Hexit",
+};
+
+char *tname[] = {
+ [Tversion] "Tversion",
+ [Tstartcmdfile] "Tstartcmdfile",
+ [Tcheck] "Tcheck",
+ [Trequest] "Trequest",
+ [Torigin] "Torigin",
+ [Tstartfile] "Tstartfile",
+ [Tworkfile] "Tworkfile",
+ [Ttype] "Ttype",
+ [Tcut] "Tcut",
+ [Tpaste] "Tpaste",
+ [Tsnarf] "Tsnarf",
+ [Tstartnewfile] "Tstartnewfile",
+ [Twrite] "Twrite",
+ [Tclose] "Tclose",
+ [Tlook] "Tlook",
+ [Tsearch] "Tsearch",
+ [Tsend] "Tsend",
+ [Tdclick] "Tdclick",
+ [Tstartsnarf] "Tstartsnarf",
+ [Tsetsnarf] "Tsetsnarf",
+ [Tack] "Tack",
+ [Texit] "Texit",
+};
+
+void
+journal(int out, char *s)
+{
+ static int fd = 0;
+
+ if(fd <= 0)
+ fd = create("/tmp/sam.out", 1, 0666L);
+ fprint(fd, "%s%s\n", out? "out: " : "in: ", s);
+}
+
+void
+journaln(int out, long n)
+{
+ char buf[32];
+ sprint(buf, sizeof (long) > 4 ? "%ld" : "%d", n);
+ journal(out, buf);
+}
+#else
+#define journal(a, b)
+#define journaln(a, b)
+#endif
+
+int
+rcvchar(void){
+ static uchar buf[64];
+ static i, nleft = 0;
+
+ if(nleft <= 0){
+ nleft = read(0, (char *)buf, sizeof buf);
+ if(nleft <= 0)
+ return -1;
+ i = 0;
+ }
+ --nleft;
+ return buf[i++];
+}
+
+int
+rcv(void){
+ int c;
+ static state = 0;
+ static count = 0;
+ static i = 0;
+
+ while((c=rcvchar()) != -1)
+ switch(state){
+ case 0:
+ h.type = c;
+ state++;
+ break;
+
+ case 1:
+ h.count0 = c;
+ state++;
+ break;
+
+ case 2:
+ h.count1 = c;
+ count = h.count0|(h.count1<<8);
+ i = 0;
+ if(count > DATASIZE)
+ panic("count>DATASIZE");
+ if(count == 0)
+ goto zerocount;
+ state++;
+ break;
+
+ case 3:
+ indata[i++] = c;
+ if(i == count){
+ zerocount:
+ indata[i] = 0;
+ state = count = 0;
+ return inmesg(h.type);
+ }
+ break;
+ }
+ return 0;
+}
+
+File *
+whichfile(int tag)
+{
+ int i;
+
+ for(i = 0; i<file.nused; i++)
+ if(file.filepptr[i]->tag==tag)
+ return file.filepptr[i];
+ hiccough((char *)0);
+ return 0;
+}
+
+int
+inmesg(Tmesg type)
+{
+ Rune buf[1025];
+ int i, m;
+ short s;
+ long l, l1;
+ File *f;
+ Posn p0, p1;
+ Range r;
+ String *str;
+ char *c;
+ Rune *rp;
+
+ if(type > TMAX)
+ panic("inmesg");
+
+ journal(0, tname[type]);
+
+ inp = indata;
+ switch(type){
+ case -1:
+ panic("rcv error");
+
+ default:
+ fprint(2, "unknown type %d\n", type);
+ panic("rcv unknown");
+
+ case Tversion:
+ tversion = inshort();
+ journaln(0, tversion);
+ break;
+
+ case Tstartcmdfile:
+ l = invlong(); /* for 64-bit pointers */
+ journaln(0, l);
+ Strdupl(&genstr, samname);
+ cmd = newfile();
+ outTsv(Hbindname, cmd->tag, l);
+ outTs(Hcurrent, cmd->tag);
+ Fsetname(cmd, &genstr);
+ cmd->rasp = emalloc(sizeof(List));
+ cmd->state = Clean;
+ if(cmdstr.n){
+ Finsert(cmd, &cmdstr, 0L);
+ Strdelete(&cmdstr, 0L, (Posn)cmdstr.n);
+ }
+ Fupdate(cmd, FALSE, TRUE);
+ outT0(Hunlock);
+ break;
+
+ case Tcheck:
+ /* go through whichfile to check the tag */
+ outTs(Hcheck, whichfile(inshort())->tag);
+ break;
+
+ case Trequest:
+ f = whichfile(inshort());
+ p0 = inlong();
+ p1 = p0+inshort();
+ journaln(0, p0);
+ journaln(0, p1-p0);
+ if(f->state == Unread)
+ panic("Trequest: unread");
+ if(p1>f->nrunes)
+ p1 = f->nrunes;
+ if(p0>f->nrunes) /* can happen e.g. scrolling during command */
+ p0 = f->nrunes;
+ if(p0 == p1){
+ i = 0;
+ r.p1 = r.p2 = p0;
+ }else{
+ r = rdata(f->rasp, p0, p1-p0);
+ i = r.p2-r.p1;
+ if(Fchars(f, buf, r.p1, r.p2)!=i)
+ panic("Trequest 2");
+ }
+ buf[i]=0;
+ outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1));
+ break;
+
+ case Torigin:
+ s = inshort();
+ l = inlong();
+ l1 = inlong();
+ journaln(0, l1);
+ lookorigin(whichfile(s), l, l1);
+ break;
+
+ case Tstartfile:
+ termlocked++;
+ f = whichfile(inshort());
+ if(!f->rasp) /* this might be a duplicate message */
+ f->rasp = emalloc(sizeof(List));
+ current(f);
+ outTsv(Hbindname, f->tag, invlong()); /* for 64-bit poi…
+ outTs(Hcurrent, f->tag);
+ journaln(0, f->tag);
+ if(f->state == Unread)
+ load(f);
+ else{
+ if(f->nrunes>0){
+ rgrow(f->rasp, 0L, f->nrunes);
+ outTsll(Hgrow, f->tag, 0L, f->nrunes);
+ }
+ outTs(Hcheck0, f->tag);
+ moveto(f, f->dot.r);
+ }
+ break;
+
+ case Tworkfile:
+ i = inshort();
+ f = whichfile(i);
+ current(f);
+ f->dot.r.p1 = inlong();
+ f->dot.r.p2 = inlong();
+ f->tdot = f->dot.r;
+ journaln(0, i);
+ journaln(0, f->dot.r.p1);
+ journaln(0, f->dot.r.p2);
+ break;
+
+ case Ttype:
+ f = whichfile(inshort());
+ p0 = inlong();
+ journaln(0, p0);
+ journal(0, (char*)inp);
+ str = tmpcstr((char*)inp);
+ i = str->n;
+ Finsert(f, str, p0);
+ if(Fupdate(f, FALSE, FALSE))
+ modnum++;
+ if(f==cmd && p0==f->nrunes-i && i>0 && str->s[i-1]=='\n'){
+ freetmpstr(str);
+ termlocked++;
+ termcommand();
+ }else
+ freetmpstr(str);
+ f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this alrea…
+ f->tdot = f->dot.r;
+ break;
+
+ case Tcut:
+ f = whichfile(inshort());
+ p0 = inlong();
+ p1 = inlong();
+ journaln(0, p0);
+ journaln(0, p1);
+ Fdelete(f, p0, p1);
+ if(Fupdate(f, FALSE, FALSE))
+ modnum++;
+ f->dot.r.p1 = f->dot.r.p2 = p0;
+ f->tdot = f->dot.r; /* terminal knows the value of dot alrea…
+ break;
+
+ case Tpaste:
+ f = whichfile(inshort());
+ p0 = inlong();
+ journaln(0, p0);
+ for(l=0; l<snarfbuf->nrunes; l+=m){
+ m = snarfbuf->nrunes-l;
+ if(m>BLOCKSIZE)
+ m = BLOCKSIZE;
+ Bread(snarfbuf, genbuf, m, l);
+ Finsert(f, tmprstr(genbuf, m), p0);
+ }
+ if(Fupdate(f, FALSE, TRUE))
+ modnum++;
+ f->dot.r.p1 = p0;
+ f->dot.r.p2 = p0+snarfbuf->nrunes;
+ f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */
+ telldot(f);
+ outTs(Hunlockfile, f->tag);
+ break;
+
+ case Tsnarf:
+ i = inshort();
+ p0 = inlong();
+ p1 = inlong();
+ snarf(whichfile(i), p0, p1, snarfbuf, 0);
+ break;
+
+ case Tstartnewfile:
+ l = invlong();
+ Strdupl(&genstr, empty);
+ f = newfile();
+ f->rasp = emalloc(sizeof(List));
+ outTsv(Hbindname, f->tag, l);
+ Fsetname(f, &genstr);
+ outTs(Hcurrent, f->tag);
+ current(f);
+ load(f);
+ break;
+
+ case Twrite:
+ termlocked++;
+ i = inshort();
+ journaln(0, i);
+ f = whichfile(i);
+ addr.r.p1 = 0;
+ addr.r.p2 = f->nrunes;
+ if(f->name.s[0] == 0)
+ error(Enoname);
+ Strduplstr(&genstr, &f->name);
+ writef(f);
+ break;
+
+ case Tclose:
+ termlocked++;
+ i = inshort();
+ journaln(0, i);
+ f = whichfile(i);
+ current(f);
+ trytoclose(f);
+ /* if trytoclose fails, will error out */
+ delete(f);
+ break;
+
+ case Tlook:
+ f = whichfile(inshort());
+ termlocked++;
+ p0 = inlong();
+ p1 = inlong();
+ journaln(0, p0);
+ journaln(0, p1);
+ setgenstr(f, p0, p1);
+ for(l = 0; l<genstr.n; l++){
+ i = genstr.s[l];
+ if(utfrune(".*+?(|)\\[]^$", i))
+ Strinsert(&genstr, tmpcstr("\\"), l++);
+ }
+ Straddc(&genstr, '\0');
+ nextmatch(f, &genstr, p1, 1);
+ moveto(f, sel.p[0]);
+ break;
+
+ case Tsearch:
+ termlocked++;
+ if(curfile == 0)
+ error(Enofile);
+ if(lastpat.s[0] == 0)
+ panic("Tsearch");
+ nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1);
+ moveto(curfile, sel.p[0]);
+ break;
+
+ case Tsend:
+ termlocked++;
+ inshort(); /* ignored */
+ p0 = inlong();
+ p1 = inlong();
+ setgenstr(cmd, p0, p1);
+ Bdelete(snarfbuf, (Posn)0, snarfbuf->nrunes);
+ Binsert(snarfbuf, &genstr, (Posn)0);
+ outTl(Hsnarflen, genstr.n);
+ if(genstr.s[genstr.n-1] != '\n')
+ Straddc(&genstr, '\n');
+ Finsert(cmd, &genstr, cmd->nrunes);
+ Fupdate(cmd, FALSE, TRUE);
+ cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nrunes;
+ telldot(cmd);
+ termcommand();
+ break;
+
+ case Tdclick:
+ f = whichfile(inshort());
+ p1 = inlong();
+ doubleclick(f, p1);
+ f->tdot.p1 = f->tdot.p2 = p1;
+ telldot(f);
+ outTs(Hunlockfile, f->tag);
+ break;
+
+ case Tstartsnarf:
+ if (snarfbuf->nrunes <= 0) { /* nothing to export */
+ outTs(Hsetsnarf, 0);
+ break;
+ }
+ c = 0;
+ i = 0;
+ m = snarfbuf->nrunes;
+ if(m > 32000) { /* tmprstr stores len in a shor…
+ m = 32000;
+ dprint("?warning: snarf buffer truncated\n");
+ }
+ rp = malloc(m*sizeof(Rune));
+ if(rp){
+ Bread(snarfbuf, rp, m, 0);
+ c = Strtoc(tmprstr(rp, m));
+ free(rp);
+ i = strlen(c);
+ }
+ outTs(Hsetsnarf, i);
+ if(c){
+ Write(1, c, i);
+ free(c);
+ } else
+ dprint("snarf buffer too long\n");
+ break;
+
+ case Tsetsnarf:
+ m = inshort();
+ if(m > SNARFSIZE)
+ error(Etoolong);
+ c = malloc(m+1);
+ if(c){
+ for(i=0; i<m; i++)
+ c[i] = rcvchar();
+ c[m] = 0;
+ str = tmpcstr(c);
+ free(c);
+ Bdelete(snarfbuf, (Posn)0, snarfbuf->nrunes);
+ Binsert(snarfbuf, str, (Posn)0);
+ freetmpstr(str);
+ outT0(Hunlock);
+ }
+ break;
+
+ case Tack:
+ waitack = 0;
+ break;
+
+ case Texit:
+ exits(0);
+ }
+ return TRUE;
+}
+
+void
+snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok)
+{
+ Posn l;
+ int i;
+
+ if(!emptyok && p1==p2)
+ return;
+ Bdelete(buf, (Posn)0, buf->nrunes);
+ /* Stage through genbuf to avoid compaction problems (vestigial) */
+ for(l=p1; l<p2; l+=i){
+ i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l;
+ Fchars(f, genbuf, l, l+i);
+ Binsert(buf, tmprstr(genbuf, i), buf->nrunes);
+ }
+}
+
+int
+inshort(void)
+{
+ ushort n;
+
+ n = inp[0] | (inp[1]<<8);
+ inp += 2;
+ return n;
+}
+
+long
+inlong(void)
+{
+ ulong n;
+
+ n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24);
+ inp += 4;
+ return n;
+}
+
+long
+invlong(void)
+{
+ ulong n;
+
+ n = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4];
+ n = (n<<16) | (inp[3]<<8) | inp[2];
+ n = (n<<16) | (inp[1]<<8) | inp[0];
+ inp += 8;
+ return n;
+}
+
+void
+setgenstr(File *f, Posn p0, Posn p1)
+{
+ if(p0 != p1){
+ if(p1-p0 >= TBLOCKSIZE)
+ error(Etoolong);
+ Strinsure(&genstr, p1-p0);
+ Fchars(f, genbuf, p0, p1);
+ memmove(genstr.s, genbuf, RUNESIZE*(p1-p0));
+ genstr.n = p1-p0;
+ }else{
+ if(snarfbuf->nrunes == 0)
+ error(Eempty);
+ if(snarfbuf->nrunes > TBLOCKSIZE)
+ error(Etoolong);
+ Bread(snarfbuf, genbuf, snarfbuf->nrunes, (Posn)0);
+ Strinsure(&genstr, snarfbuf->nrunes);
+ memmove(genstr.s, genbuf, RUNESIZE*snarfbuf->nrunes);
+ genstr.n = snarfbuf->nrunes;
+ }
+}
+
+void
+outT0(Hmesg type)
+{
+ outstart(type);
+ outsend();
+}
+
+void
+outTl(Hmesg type, long l)
+{
+ outstart(type);
+ outlong(l);
+ outsend();
+}
+
+void
+outTs(Hmesg type, int s)
+{
+ outstart(type);
+ journaln(1, s);
+ outshort(s);
+ outsend();
+}
+
+void
+outS(String *s)
+{
+ char *c;
+ int i;
+
+ c = Strtoc(s);
+ i = strlen(c);
+ outcopy(i, c);
+ if(i > 99)
+ c[99] = 0;
+ journaln(1, i);
+ journal(1, c);
+ free(c);
+}
+
+void
+outTsS(Hmesg type, int s1, String *s)
+{
+ outstart(type);
+ outshort(s1);
+ outS(s);
+ outsend();
+}
+
+void
+outTslS(Hmesg type, int s1, Posn l1, String *s)
+{
+ outstart(type);
+ outshort(s1);
+ journaln(1, s1);
+ outlong(l1);
+ journaln(1, l1);
+ outS(s);
+ outsend();
+}
+
+void
+outTS(Hmesg type, String *s)
+{
+ outstart(type);
+ outS(s);
+ outsend();
+}
+
+void
+outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s)
+{
+ outstart(type);
+ outshort(s1);
+ outlong(l1);
+ outlong(l2);
+ journaln(1, l1);
+ journaln(1, l2);
+ outS(s);
+ outsend();
+}
+
+void
+outTsll(Hmesg type, int s, Posn l1, Posn l2)
+{
+ outstart(type);
+ outshort(s);
+ outlong(l1);
+ outlong(l2);
+ journaln(1, l1);
+ journaln(1, l2);
+ outsend();
+}
+
+void
+outTsl(Hmesg type, int s, Posn l)
+{
+ outstart(type);
+ outshort(s);
+ outlong(l);
+ journaln(1, l);
+ outsend();
+}
+
+void
+outTsv(Hmesg type, int s, Posn l)
+{
+ outstart(type);
+ outshort(s);
+ outvlong((void*)l);
+ journaln(1, l);
+ outsend();
+}
+
+void
+outstart(Hmesg type)
+{
+ journal(1, hname[type]);
+ outmsg[0] = type;
+ outp = outmsg+3;
+}
+
+void
+outcopy(int count, void *data)
+{
+ memmove(outp, data, count);
+ outp += count;
+}
+
+void
+outshort(int s)
+{
+ *outp++ = s;
+ *outp++ = s>>8;
+}
+
+void
+outlong(long l)
+{
+ *outp++ = l;
+ *outp++ = l>>8;
+ *outp++ = l>>16;
+ *outp++ = l>>24;
+}
+
+void
+outvlong(void *v)
+{
+ int i;
+ ulong l;
+
+ l = (ulong) v;
+ for(i = 0; i < 8; i++, l >>= 8)
+ *outp++ = l;
+}
+
+void
+outsend(void)
+{
+ int outcount;
+
+ outcount = outp-outmsg;
+ outcount -= 3;
+ outmsg[1] = outcount;
+ outmsg[2] = outcount>>8;
+ outmsg = outp;
+ if(!noflush){
+ outcount = outmsg-outdata;
+ if (write(1, (char*) outdata, outcount) != outcount)
+ rescue();
+ outmsg = outdata;
+ return;
+ }
+ if(outmsg < outdata+DATASIZE)
+ return;
+ outflush();
+}
+
+void
+outflush(void)
+{
+ if(outmsg == outdata)
+ return;
+ noflush = 0;
+ outT0(Hack);
+ waitack = 1;
+ do
+ if(rcv() == 0){
+ rescue();
+ exits("eof");
+ }
+ while(waitack);
+ outmsg = outdata;
+ noflush = 1;
+}
diff --git a/sam/mesg.h b/sam/mesg.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#define VERSION 0
+
+#define TBLOCKSIZE 512 /* largest piece of text sent t…
+#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol head…
+#define SNARFSIZE 16384 /* maximum length of exchanged s…
+/*
+ * Messages originating at the terminal
+ */
+typedef enum Tmesg
+{
+ Tversion, /* version */
+ Tstartcmdfile, /* terminal just opened command frame */
+ Tcheck, /* ask host to poke with Hcheck */
+ Trequest, /* request data to fill a hole */
+ Torigin, /* gimme an Horigin near here */
+ Tstartfile, /* terminal just opened a file's frame */
+ Tworkfile, /* set file to which commands apply */
+ Ttype, /* add some characters, but terminal already kno…
+ Tcut,
+ Tpaste,
+ Tsnarf,
+ Tstartnewfile, /* terminal just opened a new frame */
+ Twrite, /* write file */
+ Tclose, /* terminal requests file close; check mod. sta…
+ Tlook, /* search for literal current text */
+ Tsearch, /* search for last regular expression */
+ Tsend, /* pretend he typed stuff */
+ Tdclick, /* double click */
+ Tstartsnarf, /* initiate snarf buffer exchange */
+ Tsetsnarf, /* remember string in snarf buffer */
+ Tack, /* acknowledge Hack */
+ Texit, /* exit */
+ TMAX
+}Tmesg;
+/*
+ * Messages originating at the host
+ */
+typedef enum Hmesg
+{
+ Hversion, /* version */
+ Hbindname, /* attach name[0] to text in terminal */
+ Hcurrent, /* make named file the typing file */
+ Hnewname, /* create "" name in menu */
+ Hmovname, /* move file name in menu */
+ Hgrow, /* insert space in rasp */
+ Hcheck0, /* see below */
+ Hcheck, /* ask terminal to check whether it needs more …
+ Hunlock, /* command is finished; user can do things */
+ Hdata, /* store this data in previously allocated space…
+ Horigin, /* set origin of file/frame in terminal */
+ Hunlockfile, /* unlock file in terminal */
+ Hsetdot, /* set dot in terminal */
+ Hgrowdata, /* Hgrow + Hdata folded together */
+ Hmoveto, /* scrolling, context search, etc. */
+ Hclean, /* named file is now 'clean' */
+ Hdirty, /* named file is now 'dirty' */
+ Hcut, /* remove space from rasp */
+ Hsetpat, /* set remembered regular expression */
+ Hdelname, /* delete file name from menu */
+ Hclose, /* close file and remove from menu */
+ Hsetsnarf, /* remember string in snarf buffer */
+ Hsnarflen, /* report length of implicit snarf */
+ Hack, /* request acknowledgement */
+ Hexit,
+ Hextcmd, /* execute a external command */
+ HMAX
+}Hmesg;
+typedef struct Header{
+ uchar type; /* one of the above */
+ uchar count0; /* low bits of data size */
+ uchar count1; /* high bits of data size */
+ uchar data[1]; /* variable size */
+}Header;
+/*
+ * File transfer protocol schematic, a la Holzmann
+ *
+ * proc h
+ * { pvar n = 0;
+ * queue h[4];
+ *
+ * do
+ * :: (n < N) -> n++; t!Hgrow
+ * :: (n == N) -> n++; t!Hcheck0
+ * :: h?Trequest -> t!Hdata
+ * :: h?Tcheck -> t!Hcheck
+ * od
+ * }
+ * proc t
+ * { queue t[4];
+ * do
+ * :: t?Hgrow -> h!Trequest
+ * :: t?Hdata -> skip
+ * :: t?Hcheck0 -> h!Tcheck
+ * :: t?Hcheck ->
+ * if
+ * :: break
+ * :: h!Trequest; h!Tcheck
+ * fi
+ * od
+ * }
+ */
diff --git a/sam/moveto.c b/sam/moveto.c
@@ -0,0 +1,168 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+void
+moveto(File *f, Range r)
+{
+ Posn p1 = r.p1, p2 = r.p2;
+
+ f->dot.r.p1 = p1;
+ f->dot.r.p2 = p2;
+ if(f->rasp){
+ telldot(f);
+ outTsl(Hmoveto, f->tag, f->dot.r.p1);
+ }
+}
+
+void
+telldot(File *f)
+{
+ if(f->rasp == 0)
+ panic("telldot");
+ if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2)
+ return;
+ outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2);
+ f->tdot = f->dot.r;
+}
+
+void
+tellpat(void)
+{
+ outTS(Hsetpat, &lastpat);
+ patset = FALSE;
+}
+
+#define CHARSHIFT 128
+
+void
+lookorigin(File *f, Posn p0, Posn ls)
+{
+ int nl, nc, c;
+ Posn oldp0;
+
+ if(p0 > f->nrunes)
+ p0 = f->nrunes;
+ oldp0 = p0;
+ Fgetcset(f, p0);
+ for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++)
+ if((c=Fbgetc(f)) == '\n'){
+ nl++;
+ oldp0 = p0-nc;
+ }
+ if(c == -1)
+ p0 = 0;
+ else if(nl==0){
+ if(p0>=CHARSHIFT/2)
+ p0-=CHARSHIFT/2;
+ else
+ p0 = 0;
+ }else
+ p0 = oldp0;
+ outTsl(Horigin, f->tag, p0);
+}
+
+int
+alnum(int c)
+{
+ /*
+ * Hard to get absolutely right. Use what we know about ASCII
+ * and assume anything above the Latin control characters is
+ * potentially an alphanumeric.
+ */
+ if(c<=' ')
+ return 0;
+ if(0x7F<=c && c<=0xA0)
+ return 0;
+ if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
+ return 0;
+ return 1;
+}
+
+int
+clickmatch(File *f, int cl, int cr, int dir)
+{
+ int c;
+ int nest = 1;
+
+ while((c=(dir>0? Fgetc(f) : Fbgetc(f))) > 0)
+ if(c == cr){
+ if(--nest==0)
+ return 1;
+ }else if(c == cl)
+ nest++;
+ return cl=='\n' && nest==1;
+}
+
+Rune*
+strrune(Rune *s, Rune c)
+{
+ Rune c1;
+
+ if(c == 0) {
+ while(*s++)
+ ;
+ return s-1;
+ }
+
+ while(c1 = *s++)
+ if(c1 == c)
+ return s-1;
+ return 0;
+}
+
+void
+doubleclick(File *f, Posn p1)
+{
+ int c, i;
+ Rune *r, *l;
+
+ if(p1 > f->nrunes)
+ return;
+ f->dot.r.p1 = f->dot.r.p2 = p1;
+ for(i=0; left[i]; i++){
+ l = left[i];
+ r = right[i];
+ /* try left match */
+ if(p1 == 0){
+ Fgetcset(f, p1);
+ c = '\n';
+ }else{
+ Fgetcset(f, p1-1);
+ c = Fgetc(f);
+ }
+ if(c!=-1 && strrune(l, c)){
+ if(clickmatch(f, c, r[strrune(l, c)-l], 1)){
+ f->dot.r.p1 = p1;
+ f->dot.r.p2 = f->getcp-(c!='\n');
+ }
+ return;
+ }
+ /* try right match */
+ if(p1 == f->nrunes){
+ Fbgetcset(f, p1);
+ c = '\n';
+ }else{
+ Fbgetcset(f, p1+1);
+ c = Fbgetc(f);
+ }
+ if(c!=-1 && strrune(r, c)){
+ if(clickmatch(f, c, l[strrune(r, c)-r], -1)){
+ f->dot.r.p1 = f->getcp;
+ if(c!='\n' || f->getcp!=0 ||
+ (Fgetcset(f, (Posn)0),Fgetc(f))=='\n')
+ f->dot.r.p1++;
+ f->dot.r.p2 = p1+(p1<f->nrunes && c=='\n');
+ }
+ return;
+ }
+ }
+ /* try filling out word to right */
+ Fgetcset(f, p1);
+ while((c=Fgetc(f))!=-1 && alnum(c))
+ f->dot.r.p2++;
+ /* try filling out word to left */
+ Fbgetcset(f, p1);
+ while((c=Fbgetc(f))!=-1 && alnum(c))
+ f->dot.r.p1--;
+}
+
diff --git a/sam/multi.c b/sam/multi.c
@@ -0,0 +1,91 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+List file;
+ushort tag;
+
+File *
+newfile(void)
+{
+ File *f;
+
+ inslist(&file, 0, (long)(f = Fopen()));
+ f->tag = tag++;
+ if(downloaded)
+ outTs(Hnewname, f->tag);
+ /* already sorted; file name is "" */
+ return f;
+}
+
+int
+whichmenu(File *f)
+{
+ int i;
+
+ for(i=0; i<file.nused; i++)
+ if(file.filepptr[i]==f)
+ return i;
+ return -1;
+}
+
+void
+delfile(File *f)
+{
+ int w = whichmenu(f);
+
+ if(w < 0) /* e.g. x/./D */
+ return;
+ if(downloaded)
+ outTs(Hdelname, f->tag);
+ dellist(&file, w);
+ Fclose(f);
+}
+
+void
+sortname(File *f)
+{
+ int i, cmp, w;
+ int dupwarned;
+
+ w = whichmenu(f);
+ dupwarned = FALSE;
+ dellist(&file, w);
+ if(f == cmd)
+ i = 0;
+ else for(i=0; i<file.nused; i++){
+ cmp = Strcmp(&f->name, &file.filepptr[i]->name);
+ if(cmp==0 && !dupwarned){
+ dupwarned = TRUE;
+ warn_S(Wdupname, &f->name);
+ }else if(cmp<0 && (i>0 || cmd==0))
+ break;
+ }
+ inslist(&file, i, (long)f);
+ if(downloaded)
+ outTsS(Hmovname, f->tag, &f->name);
+}
+
+void
+state(File *f, int cleandirty)
+{
+ if(f == cmd)
+ return;
+ if(downloaded && whichmenu(f)>=0){ /* else flist or menu */
+ if(f->state==Dirty && cleandirty!=Dirty)
+ outTs(Hclean, f->tag);
+ else if(f->state!=Dirty && cleandirty==Dirty)
+ outTs(Hdirty, f->tag);
+ }
+ f->state = cleandirty;
+}
+
+File *
+lookfile(String *s)
+{
+ int i;
+
+ for(i=0; i<file.nused; i++)
+ if(Strcmp(&file.filepptr[i]->name, s) == 0)
+ return file.filepptr[i];
+ return 0;
+}
diff --git a/sam/parse.h b/sam/parse.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+typedef struct Addr Addr;
+typedef struct Cmd Cmd;
+struct Addr
+{
+ char type; /* # (char addr), l (line addr), / ? . $ + - …
+ union{
+ String *re;
+ Addr *aleft; /* left side of , and ; */
+ } g;
+ Posn num;
+ Addr *next; /* or right side of , and ; …
+};
+
+#define are g.re
+#define left g.aleft
+
+struct Cmd
+{
+ Addr *addr; /* address (range of text) */
+ String *re; /* regular expression for e.…
+ union{
+ Cmd *cmd; /* target of x, g, {, etc. */
+ String *text; /* text of a, c, i; rhs of…
+ Addr *addr; /* address for m, t */
+ } g;
+ Cmd *next; /* pointer to next element in…
+ short num;
+ ushort flag; /* whatever */
+ ushort cmdc; /* command character; 'x' e…
+};
+
+#define ccmd g.cmd
+#define ctext g.text
+#define caddr g.addr
+
+extern struct cmdtab{
+ ushort cmdc; /* command character */
+ uchar text; /* takes a textual argument? */
+ uchar regexp; /* takes a regular expression? */
+ uchar addr; /* takes an address (m or t)? */
+ uchar defcmd; /* default command; 0==>none */
+ uchar defaddr; /* default address */
+ uchar count; /* takes a count e.g. s2/// */
+ char *token; /* takes text terminated by one of …
+ int (*fn)(File*, Cmd*); /* function to call with parse t…
+}cmdtab[];
+
+enum Defaddr{ /* default addresses */
+ aNo,
+ aDot,
+ aAll
+};
+
+int nl_cmd(File*, Cmd*), a_cmd(File*, Cmd*), b_cmd(File*, Cmd*);
+int c_cmd(File*, Cmd*), cd_cmd(File*, Cmd*), d_cmd(File*, Cmd*);
+int D_cmd(File*, Cmd*), e_cmd(File*, Cmd*);
+int f_cmd(File*, Cmd*), g_cmd(File*, Cmd*), i_cmd(File*, Cmd*);
+int k_cmd(File*, Cmd*), m_cmd(File*, Cmd*), n_cmd(File*, Cmd*);
+int p_cmd(File*, Cmd*), q_cmd(File*, Cmd*);
+int s_cmd(File*, Cmd*), u_cmd(File*, Cmd*), w_cmd(File*, Cmd*);
+int x_cmd(File*, Cmd*), X_cmd(File*, Cmd*), plan9_cmd(File*, Cmd*);
+int eq_cmd(File*, Cmd*);
+
+
+String *getregexp(int);
+Addr *newaddr(void);
+Address address(Addr*, Address, int);
+int cmdexec(File*, Cmd*);
diff --git a/sam/plan9.c b/sam/plan9.c
@@ -0,0 +1,174 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+Rune samname[] = L"~~sam~~";
+
+Rune *left[]= {
+ L"{[(<«",
+ L"\n",
+ L"'\"`",
+ 0
+};
+Rune *right[]= {
+ L"}])>»",
+ L"\n",
+ L"'\"`",
+ 0
+};
+
+char RSAM[] = "sam";
+char SAMTERM[] = "/bin/aux/samterm";
+char HOME[] = "home";
+char TMPDIR[] = "/tmp";
+char SH[] = "rc";
+char SHPATH[] = "/bin/rc";
+char RX[] = "rx";
+char RXPATH[] = "/bin/rx";
+char SAMSAVECMD[] = "/bin/rc\n/sys/lib/samsave";
+
+void
+dprint(char *z, ...)
+{
+ char buf[BLOCKSIZE];
+ va_list arg;
+
+ va_start(arg, z);
+ doprint(buf, &buf[BLOCKSIZE], z, arg);
+ va_end(arg);
+ termwrite(buf);
+}
+
+void
+print_ss(char *s, String *a, String *b)
+{
+ dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s);
+}
+
+void
+print_s(char *s, String *a)
+{
+ dprint("?warning: %s `%.*S'\n", s, a->n, a->s);
+}
+
+char*
+getuser(void)
+{
+ static char user[NAMELEN];
+ int fd;
+
+ if(user[0] == 0){
+ fd = open("/dev/user", 0);
+ if(fd<0 || read(fd, user, sizeof user)<=0)
+ strcpy(user, "none");
+ close(fd);
+ }
+ return user;
+}
+
+int
+statfile(char *name, ulong *dev, ulong *id, long *time, long *length, long *ap…
+{
+ Dir dirb;
+
+ if(dirstat(name, &dirb) == -1)
+ return -1;
+ if(dev)
+ *dev = dirb.type|(dirb.dev<<16);
+ if(id)
+ *id = dirb.qid.path;
+ if(time)
+ *time = dirb.mtime;
+ if(length)
+ *length = dirb.length;
+ if(appendonly)
+ *appendonly = dirb.mode & CHAPPEND;
+ return 1;
+}
+
+int
+statfd(int fd, ulong *dev, ulong *id, long *time, long *length, long *appendon…
+{
+ Dir dirb;
+
+ if(dirfstat(fd, &dirb) == -1)
+ return -1;
+ if(dev)
+ *dev = dirb.type|(dirb.dev<<16);
+ if(id)
+ *id = dirb.qid.path;
+ if(time)
+ *time = dirb.mtime;
+ if(length)
+ *length = dirb.length;
+ if(appendonly)
+ *appendonly = dirb.mode & CHAPPEND;
+ return 1;
+}
+
+void
+notifyf(void *a, char *s)
+{
+ USED(a);
+ if(bpipeok && strcmp(s, "sys: write on closed pipe") == 0)
+ noted(NCONT);
+ if(strcmp(s, "interrupt") == 0)
+ noted(NCONT);
+ panicking = 1;
+ rescue();
+ noted(NDFLT);
+}
+
+int
+newtmp(int num)
+{
+ int i, fd;
+ static char tempnam[30];
+
+ i = getpid();
+ do
+ sprint(tempnam, "%s/%d%.4s%dsam", TMPDIR, num, getuser(), i++);
+ while(access(tempnam, 0) == 0);
+ fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000);
+ if(fd < 0){
+ remove(tempnam);
+ fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000);
+ }
+ return fd;
+}
+
+int
+waitfor(int pid)
+{
+ int rpid;
+ Waitmsg wm;
+
+ do; while((rpid = wait(&wm)) != pid && rpid != -1);
+ return wm.msg[0];
+}
+
+void
+samerr(char *buf)
+{
+ sprint(buf, "%s/sam.err", TMPDIR);
+}
+
+void*
+emalloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == 0)
+ panic("malloc fails");
+ memset(p, 0, n);
+ return p;
+}
+
+void*
+erealloc(void *p, ulong n)
+{
+ p = realloc(p, n);
+ if(p == 0)
+ panic("realloc fails");
+ return p;
+}
diff --git a/sam/rasp.c b/sam/rasp.c
@@ -0,0 +1,276 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+/*
+ * GROWDATASIZE must be big enough that all errors go out as Hgrowdata's,
+ * so they will be scrolled into visibility in the ~~sam~~ window (yuck!).
+ */
+#define GROWDATASIZE 50 /* if size is > this, send data w…
+
+void rcut(List*, Posn, Posn);
+int rterm(List*, Posn);
+void rgrow(List*, Posn, Posn);
+
+void
+toterminal(File *f, int toterm)
+{
+ Buffer *t = f->transcript;
+ Posn n, p0, p1, p2, delta = 0, deltacmd = 0;
+ Range r;
+ union{
+ union Hdr g;
+ Rune buf[8+GROWDATASIZE];
+ }hdr;
+ Posn growpos, grown;
+
+ if(f->rasp == 0)
+ return;
+ if(f->marked)
+ p0 = f->markp+sizeof(Mark)/RUNESIZE;
+ else
+ p0 = 0;
+ grown = 0;
+ noflush = 1;
+ SET(growpos);
+ while(Bread(t, (Rune*)&hdr, sizeof(hdr)/RUNESIZE, p0) > 0){
+ switch(hdr.g.cs.c){
+ default:
+ fprint(2, "char %c %.2x\n", hdr.g.cs.c, hdr.g.cs.c);
+ panic("unknown in toterminal");
+
+ case 'd':
+ if(grown){
+ outTsll(Hgrow, f->tag, growpos, grown);
+ grown = 0;
+ }
+ p1 = hdr.g.cll.l;
+ p2 = hdr.g.cll.l1;
+ if(p2 <= p1)
+ panic("toterminal delete 0");
+ if(f==cmd && p1<cmdpt){
+ if(p2 <= cmdpt)
+ deltacmd -= (p2-p1);
+ else
+ deltacmd -= cmdpt-p1;
+ }
+ p1 += delta;
+ p2 += delta;
+ p0 += sizeof(struct _cll)/RUNESIZE;
+ if(toterm)
+ outTsll(Hcut, f->tag, p1, p2-p1);
+ rcut(f->rasp, p1, p2);
+ delta -= p2-p1;
+ break;
+
+ case 'f':
+ if(grown){
+ outTsll(Hgrow, f->tag, growpos, grown);
+ grown = 0;
+ }
+ n = hdr.g.cs.s;
+ p0 += sizeof(struct _cs)/RUNESIZE + n;
+ break;
+
+ case 'i':
+ n = hdr.g.csl.s;
+ p1 = hdr.g.csl.l;
+ p0 += sizeof(struct _csl)/RUNESIZE + n;
+ if(n <= 0)
+ panic("toterminal insert 0");
+ if(f==cmd && p1<cmdpt)
+ deltacmd += n;
+ p1 += delta;
+ if(toterm){
+ if(n>GROWDATASIZE || !rterm(f->rasp, p1)){
+ rgrow(f->rasp, p1, n);
+ if(grown && growpos+grown!=p1){
+ outTsll(Hgrow, f->tag, growpos…
+ grown = 0;
+ }
+ if(grown)
+ grown += n;
+ else{
+ growpos = p1;
+ grown = n;
+ }
+ }else{
+ Rune *rp;
+ if(grown){
+ outTsll(Hgrow, f->tag, growpos…
+ grown = 0;
+ }
+ rp = hdr.buf+sizeof(hdr.g.csl)/RUNESIZ…
+ rgrow(f->rasp, p1, n);
+ r = rdata(f->rasp, p1, n);
+ if(r.p1!=p1 || r.p2!=p1+n)
+ panic("rdata in toterminal");
+ outTsllS(Hgrowdata, f->tag, p1, n, tmp…
+ }
+ }else{
+ rgrow(f->rasp, p1, n);
+ r = rdata(f->rasp, p1, n);
+ if(r.p1!=p1 || r.p2!=p1+n)
+ panic("rdata in toterminal");
+ }
+ delta += n;
+ break;
+ }
+ }
+ if(grown)
+ outTsll(Hgrow, f->tag, growpos, grown);
+ if(toterm)
+ outTs(Hcheck0, f->tag);
+ outflush();
+ noflush = 0;
+ if(f == cmd){
+ cmdpt += deltacmd+cmdptadv;
+ cmdptadv = 0;
+ }
+}
+
+#define M 0x80000000L
+#define P(i) r->longptr[i]
+#define T(i) (P(i)&M) /* in terminal */
+#define L(i) (P(i)&~M) /* length of this piece */
+
+void
+rcut(List *r, Posn p1, Posn p2)
+{
+ Posn p, x;
+ int i;
+
+ if(p1 == p2)
+ panic("rcut 0");
+ for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
+ ;
+ if(i == r->nused)
+ panic("rcut 1");
+ if(p<p1){ /* chop this piece */
+ if(p+L(i) < p2){
+ x = p1-p;
+ p += L(i);
+ }else{
+ x = L(i)-(p2-p1);
+ p = p2;
+ }
+ if(T(i))
+ P(i) = x|M;
+ else
+ P(i) = x;
+ i++;
+ }
+ while(i<r->nused && p+L(i)<=p2){
+ p += L(i);
+ dellist(r, i);
+ }
+ if(p<p2){
+ if(i == r->nused)
+ panic("rcut 2");
+ x = L(i)-(p2-p);
+ if(T(i))
+ P(i) = x|M;
+ else
+ P(i) = x;
+ }
+ /* can we merge i and i-1 ? */
+ if(i>0 && i<r->nused && T(i-1)==T(i)){
+ x = L(i-1)+L(i);
+ dellist(r, i--);
+ if(T(i))
+ P(i)=x|M;
+ else
+ P(i)=x;
+ }
+}
+
+void
+rgrow(List *r, Posn p1, Posn n)
+{
+ Posn p;
+ int i;
+
+ if(n == 0)
+ panic("rgrow 0");
+ for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
+ ;
+ if(i == r->nused){ /* stick on end of file */
+ if(p!=p1)
+ panic("rgrow 1");
+ if(i>0 && !T(i-1))
+ P(i-1)+=n;
+ else
+ inslist(r, i, n);
+ }else if(!T(i)) /* goes in this empty piece */
+ P(i)+=n;
+ else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies li…
+ P(i-1)+=n;
+ else if(p==p1)
+ inslist(r, i, n);
+ else{ /* must break piece in terminal */
+ inslist(r, i+1, (L(i)-(p1-p))|M);
+ inslist(r, i+1, n);
+ P(i) = (p1-p)|M;
+ }
+}
+
+int
+rterm(List *r, Posn p1)
+{
+ Posn p;
+ int i;
+
+ for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
+ ;
+ if(i==r->nused && (i==0 || !T(i-1)))
+ return 0;
+ return T(i);
+}
+
+Range
+rdata(List *r, Posn p1, Posn n)
+{
+ Posn p;
+ int i;
+ Range rg;
+
+ if(n==0)
+ panic("rdata 0");
+ for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
+ ;
+ if(i==r->nused)
+ panic("rdata 1");
+ if(T(i)){
+ n-=L(i)-(p1-p);
+ if(n<=0){
+ rg.p1 = rg.p2 = p1;
+ return rg;
+ }
+ p+=L(i++);
+ p1 = p;
+ }
+ if(T(i) || i==r->nused)
+ panic("rdata 2");
+ if(p+L(i)<p1+n)
+ n = L(i)-(p1-p);
+ rg.p1 = p1;
+ rg.p2 = p1+n;
+ if(p!=p1){
+ inslist(r, i+1, L(i)-(p1-p));
+ P(i)=p1-p;
+ i++;
+ }
+ if(L(i)!=n){
+ inslist(r, i+1, L(i)-n);
+ P(i)=n;
+ }
+ P(i)|=M;
+ /* now i is set; can we merge? */
+ if(i<r->nused-1 && T(i+1)){
+ P(i)=(n+=L(i+1))|M;
+ dellist(r, i+1);
+ }
+ if(i>0 && T(i-1)){
+ P(i)=(n+L(i-1))|M;
+ dellist(r, i-1);
+ }
+ return rg;
+}
diff --git a/sam/regexp.c b/sam/regexp.c
@@ -0,0 +1,820 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+Rangeset sel;
+String lastregexp;
+/*
+ * Machine Information
+ */
+typedef struct Inst Inst;
+
+struct Inst
+{
+ long type; /* < 0x10000 ==> literal, otherwise action */
+ union {
+ int rsid;
+ int rsubid;
+ int class;
+ struct Inst *rother;
+ struct Inst *rright;
+ } r;
+ union{
+ struct Inst *lleft;
+ struct Inst *lnext;
+ } l;
+};
+#define sid r.rsid
+#define subid r.rsubid
+#define rclass r.class
+#define other r.rother
+#define right r.rright
+#define left l.lleft
+#define next l.lnext
+
+#define NPROG 1024
+Inst program[NPROG];
+Inst *progp;
+Inst *startinst; /* First inst. of program; might not be program…
+Inst *bstartinst; /* same for backwards machine */
+
+typedef struct Ilist Ilist;
+struct Ilist
+{
+ Inst *inst; /* Instruction of the thread */
+ Rangeset se;
+ Posn startp; /* first char of match */
+};
+
+#define NLIST 128
+
+Ilist *tl, *nl; /* This list, next list */
+Ilist list[2][NLIST];
+static Rangeset sempty;
+
+/*
+ * Actions and Tokens
+ *
+ * 0x100xx are operators, value == precedence
+ * 0x200xx are tokens, i.e. operands for operators
+ */
+#define OPERATOR 0x10000 /* Bitmask of all operators */
+#define START 0x10000 /* Start, used for marker o…
+#define RBRA 0x10001 /* Right bracket, ) */
+#define LBRA 0x10002 /* Left bracket, ( */
+#define OR 0x10003 /* Alternation, | */
+#define CAT 0x10004 /* Concatentation, implicit o…
+#define STAR 0x10005 /* Closure, * */
+#define PLUS 0x10006 /* a+ == aa* */
+#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 …
+#define ANY 0x20000 /* Any character but newline,…
+#define NOP 0x20001 /* No operation, internal use…
+#define BOL 0x20002 /* Beginning of line, ^ */
+#define EOL 0x20003 /* End of line, $ */
+#define CCLASS 0x20004 /* Character class, [] */
+#define NCCLASS 0x20005 /* Negated character clas…
+#define END 0x20077 /* Terminate: match found */
+
+#define ISATOR 0x10000
+#define ISAND 0x20000
+
+/*
+ * Parser Information
+ */
+typedef struct Node Node;
+struct Node
+{
+ Inst *first;
+ Inst *last;
+};
+
+#define NSTACK 20
+Node andstack[NSTACK];
+Node *andp;
+int atorstack[NSTACK];
+int *atorp;
+int lastwasand; /* Last token was operand */
+int cursubid;
+int subidstack[NSTACK];
+int *subidp;
+int backwards;
+int nbra;
+Rune *exprp; /* pointer to next character in source expr…
+#define DCLASS 10 /* allocation increment */
+int nclass; /* number active */
+int Nclass; /* high water mark */
+Rune **class;
+int negateclass;
+
+void addinst(Ilist *l, Inst *inst, Rangeset *sep);
+void newmatch(Rangeset*);
+void bnewmatch(Rangeset*);
+void pushand(Inst*, Inst*);
+void pushator(int);
+Node *popand(int);
+int popator(void);
+void startlex(Rune*);
+int lex(void);
+void operator(int);
+void operand(int);
+void evaluntil(int);
+void optimize(Inst*);
+void bldcclass(void);
+
+void
+regerror(Err e)
+{
+ Strzero(&lastregexp);
+ error(e);
+}
+
+void
+regerror_c(Err e, int c)
+{
+ Strzero(&lastregexp);
+ error_c(e, c);
+}
+
+Inst *
+newinst(int t)
+{
+ if(progp >= &program[NPROG])
+ regerror(Etoolong);
+ progp->type = t;
+ progp->left = 0;
+ progp->right = 0;
+ return progp++;
+}
+
+Inst *
+realcompile(Rune *s)
+{
+ int token;
+
+ startlex(s);
+ atorp = atorstack;
+ andp = andstack;
+ subidp = subidstack;
+ cursubid = 0;
+ lastwasand = FALSE;
+ /* Start with a low priority operator to prime parser */
+ pushator(START-1);
+ while((token=lex()) != END){
+ if((token&ISATOR) == OPERATOR)
+ operator(token);
+ else
+ operand(token);
+ }
+ /* Close with a low priority operator */
+ evaluntil(START);
+ /* Force END */
+ operand(END);
+ evaluntil(START);
+ if(nbra)
+ regerror(Eleftpar);
+ --andp; /* points to first and only operand */
+ return andp->first;
+}
+
+void
+compile(String *s)
+{
+ int i;
+ Inst *oprogp;
+
+ if(Strcmp(s, &lastregexp)==0)
+ return;
+ for(i=0; i<nclass; i++)
+ free(class[i]);
+ nclass = 0;
+ progp = program;
+ backwards = FALSE;
+ startinst = realcompile(s->s);
+ optimize(program);
+ oprogp = progp;
+ backwards = TRUE;
+ bstartinst = realcompile(s->s);
+ optimize(oprogp);
+ Strduplstr(&lastregexp, s);
+}
+
+void
+operand(int t)
+{
+ Inst *i;
+ if(lastwasand)
+ operator(CAT); /* catenate is implicit */
+ i = newinst(t);
+ if(t == CCLASS){
+ if(negateclass)
+ i->type = NCCLASS; /* UGH */
+ i->rclass = nclass-1; /* UGH */
+ }
+ pushand(i, i);
+ lastwasand = TRUE;
+}
+
+void
+operator(int t)
+{
+ if(t==RBRA && --nbra<0)
+ regerror(Erightpar);
+ if(t==LBRA){
+/*
+ * if(++cursubid >= NSUBEXP)
+ * regerror(Esubexp);
+ */
+ cursubid++; /* silently ignored */
+ nbra++;
+ if(lastwasand)
+ operator(CAT);
+ }else
+ evaluntil(t);
+ if(t!=RBRA)
+ pushator(t);
+ lastwasand = FALSE;
+ if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
+ lastwasand = TRUE; /* these look like operands */
+}
+
+void
+cant(char *s)
+{
+ char buf[100];
+
+ sprint(buf, "regexp: can't happen: %s", s);
+ panic(buf);
+}
+
+void
+pushand(Inst *f, Inst *l)
+{
+ if(andp >= &andstack[NSTACK])
+ cant("operand stack overflow");
+ andp->first = f;
+ andp->last = l;
+ andp++;
+}
+
+void
+pushator(int t)
+{
+ if(atorp >= &atorstack[NSTACK])
+ cant("operator stack overflow");
+ *atorp++=t;
+ if(cursubid >= NSUBEXP)
+ *subidp++= -1;
+ else
+ *subidp++=cursubid;
+}
+
+Node *
+popand(int op)
+{
+ if(andp <= &andstack[0])
+ if(op)
+ regerror_c(Emissop, op);
+ else
+ regerror(Ebadregexp);
+ return --andp;
+}
+
+int
+popator(void)
+{
+ if(atorp <= &atorstack[0])
+ cant("operator stack underflow");
+ --subidp;
+ return *--atorp;
+}
+
+void
+evaluntil(int pri)
+{
+ Node *op1, *op2, *t;
+ Inst *inst1, *inst2;
+
+ while(pri==RBRA || atorp[-1]>=pri){
+ switch(popator()){
+ case LBRA:
+ op1 = popand('(');
+ inst2 = newinst(RBRA);
+ inst2->subid = *subidp;
+ op1->last->next = inst2;
+ inst1 = newinst(LBRA);
+ inst1->subid = *subidp;
+ inst1->next = op1->first;
+ pushand(inst1, inst2);
+ return; /* must have been RBRA */
+ default:
+ panic("unknown regexp operator");
+ break;
+ case OR:
+ op2 = popand('|');
+ op1 = popand('|');
+ inst2 = newinst(NOP);
+ op2->last->next = inst2;
+ op1->last->next = inst2;
+ inst1 = newinst(OR);
+ inst1->right = op1->first;
+ inst1->left = op2->first;
+ pushand(inst1, inst2);
+ break;
+ case CAT:
+ op2 = popand(0);
+ op1 = popand(0);
+ if(backwards && op2->first->type!=END)
+ t = op1, op1 = op2, op2 = t;
+ op1->last->next = op2->first;
+ pushand(op1->first, op2->last);
+ break;
+ case STAR:
+ op2 = popand('*');
+ inst1 = newinst(OR);
+ op2->last->next = inst1;
+ inst1->right = op2->first;
+ pushand(inst1, inst1);
+ break;
+ case PLUS:
+ op2 = popand('+');
+ inst1 = newinst(OR);
+ op2->last->next = inst1;
+ inst1->right = op2->first;
+ pushand(op2->first, inst1);
+ break;
+ case QUEST:
+ op2 = popand('?');
+ inst1 = newinst(OR);
+ inst2 = newinst(NOP);
+ inst1->left = inst2;
+ inst1->right = op2->first;
+ op2->last->next = inst2;
+ pushand(inst1, inst2);
+ break;
+ }
+ }
+}
+
+
+void
+optimize(Inst *start)
+{
+ Inst *inst, *target;
+
+ for(inst=start; inst->type!=END; inst++){
+ target = inst->next;
+ while(target->type == NOP)
+ target = target->next;
+ inst->next = target;
+ }
+}
+
+#ifdef DEBUG
+void
+dumpstack(void){
+ Node *stk;
+ int *ip;
+
+ dprint("operators\n");
+ for(ip = atorstack; ip<atorp; ip++)
+ dprint("0%o\n", *ip);
+ dprint("operands\n");
+ for(stk = andstack; stk<andp; stk++)
+ dprint("0%o\t0%o\n", stk->first->type, stk->last->type);
+}
+void
+dump(void){
+ Inst *l;
+
+ l = program;
+ do{
+ dprint("%d:\t0%o\t%d\t%d\n", l-program, l->type,
+ l->left-program, l->right-program);
+ }while(l++->type);
+}
+#endif
+
+void
+startlex(Rune *s)
+{
+ exprp = s;
+ nbra = 0;
+}
+
+
+int
+lex(void){
+ int c= *exprp++;
+
+ switch(c){
+ case '\\':
+ if(*exprp)
+ if((c= *exprp++)=='n')
+ c='\n';
+ break;
+ case 0:
+ c = END;
+ --exprp; /* In case we come here again */
+ break;
+ case '*':
+ c = STAR;
+ break;
+ case '?':
+ c = QUEST;
+ break;
+ case '+':
+ c = PLUS;
+ break;
+ case '|':
+ c = OR;
+ break;
+ case '.':
+ c = ANY;
+ break;
+ case '(':
+ c = LBRA;
+ break;
+ case ')':
+ c = RBRA;
+ break;
+ case '^':
+ c = BOL;
+ break;
+ case '$':
+ c = EOL;
+ break;
+ case '[':
+ c = CCLASS;
+ bldcclass();
+ break;
+ }
+ return c;
+}
+
+long
+nextrec(void){
+ if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0))
+ regerror(Ebadclass);
+ if(exprp[0] == '\\'){
+ exprp++;
+ if(*exprp=='n'){
+ exprp++;
+ return '\n';
+ }
+ return *exprp++|0x10000;
+ }
+ return *exprp++;
+}
+
+void
+bldcclass(void)
+{
+ long c1, c2, n, na;
+ Rune *classp;
+
+ classp = emalloc(DCLASS*RUNESIZE);
+ n = 0;
+ na = DCLASS;
+ /* we have already seen the '[' */
+ if(*exprp == '^'){
+ classp[n++] = '\n'; /* don't match newline in negate ca…
+ negateclass = TRUE;
+ exprp++;
+ }else
+ negateclass = FALSE;
+ while((c1 = nextrec()) != ']'){
+ if(c1 == '-'){
+ Error:
+ free(classp);
+ regerror(Ebadclass);
+ }
+ if(n+4 >= na){ /* 3 runes plus NUL */
+ na += DCLASS;
+ classp = erealloc(classp, na*RUNESIZE);
+ }
+ if(*exprp == '-'){
+ exprp++; /* eat '-' */
+ if((c2 = nextrec()) == ']')
+ goto Error;
+ classp[n+0] = 0xFFFF;
+ classp[n+1] = c1;
+ classp[n+2] = c2;
+ n += 3;
+ }else
+ classp[n++] = c1;
+ }
+ classp[n] = 0;
+ if(nclass == Nclass){
+ Nclass += DCLASS;
+ class = erealloc(class, Nclass*sizeof(Rune*));
+ }
+ class[nclass++] = classp;
+}
+
+int
+classmatch(int classno, int c, int negate)
+{
+ Rune *p;
+
+ p = class[classno];
+ while(*p){
+ if(*p == 0xFFFF){
+ if(p[1]<=c && c<=p[2])
+ return !negate;
+ p += 3;
+ }else if(*p++ == c)
+ return !negate;
+ }
+ return negate;
+}
+
+/*
+ * Note optimization in addinst:
+ * *l must be pending when addinst called; if *l has been looked
+ * at already, the optimization is a bug.
+ */
+void
+addinst(Ilist *l, Inst *inst, Rangeset *sep)
+{
+ Ilist *p;
+
+ for(p = l; p->inst; p++){
+ if(p->inst==inst){
+ if((sep)->p[0].p1 < p->se.p[0].p1)
+ p->se= *sep; /* this would be bug */
+ return; /* It's already there */
+ }
+ }
+ p->inst = inst;
+ p->se= *sep;
+ (p+1)->inst = 0;
+}
+
+int
+execute(File *f, Posn startp, Posn eof)
+{
+ int flag = 0;
+ Inst *inst;
+ Ilist *tlp;
+ Posn p = startp;
+ int nnl = 0, ntl;
+ int c;
+ int wrapped = 0;
+ int startchar = startinst->type<OPERATOR? startinst->type : 0;
+
+ list[0][0].inst = list[1][0].inst = 0;
+ sel.p[0].p1 = -1;
+ Fgetcset(f, startp);
+ /* Execute machine once for each character */
+ for(;;p++){
+ doloop:
+ c = Fgetc(f);
+ if(p>=eof || c<0){
+ switch(wrapped++){
+ case 0: /* let loop run one more click …
+ case 2:
+ break;
+ case 1: /* expired; wrap to beginning */
+ if(sel.p[0].p1>=0 || eof!=INFINITY)
+ goto Return;
+ list[0][0].inst = list[1][0].inst = 0;
+ Fgetcset(f, (Posn)0);
+ p = 0;
+ goto doloop;
+ default:
+ goto Return;
+ }
+ }else if(((wrapped && p>=startp) || sel.p[0].p1>0) && nnl==0)
+ break;
+ /* fast check for first char */
+ if(startchar && nnl==0 && c!=startchar)
+ continue;
+ tl = list[flag];
+ nl = list[flag^=1];
+ nl->inst = 0;
+ ntl = nnl;
+ nnl = 0;
+ if(sel.p[0].p1<0 && (!wrapped || p<startp || startp==eof)){
+ /* Add first instruction to this list */
+ if(++ntl >= NLIST)
+ Overflow:
+ error(Eoverflow);
+ sempty.p[0].p1 = p;
+ addinst(tl, startinst, &sempty);
+ }
+ /* Execute machine until this list is empty */
+ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment =…
+ Switchstmt:
+ switch(inst->type){
+ default: /* regular character */
+ if(inst->type==c){
+ Addinst:
+ if(++nnl >= NLIST)
+ goto Overflow;
+ addinst(nl, inst->next, &tlp->se);
+ }
+ break;
+ case LBRA:
+ if(inst->subid>=0)
+ tlp->se.p[inst->subid].p1 = p;
+ inst = inst->next;
+ goto Switchstmt;
+ case RBRA:
+ if(inst->subid>=0)
+ tlp->se.p[inst->subid].p2 = p;
+ inst = inst->next;
+ goto Switchstmt;
+ case ANY:
+ if(c!='\n')
+ goto Addinst;
+ break;
+ case BOL:
+ if(p == 0){
+ Step:
+ inst = inst->next;
+ goto Switchstmt;
+ }
+ if(f->getci > 1){
+ if(f->getcbuf[f->getci-2]=='\n')
+ goto Step;
+ }else{
+ Rune c;
+ if(Fchars(f, &c, p-1, p)==1 && c=='\n')
+ goto Step;
+ }
+ break;
+ case EOL:
+ if(c == '\n')
+ goto Step;
+ break;
+ case CCLASS:
+ if(c>=0 && classmatch(inst->rclass, c, 0))
+ goto Addinst;
+ break;
+ case NCCLASS:
+ if(c>=0 && classmatch(inst->rclass, c, 1))
+ goto Addinst;
+ break;
+ case OR:
+ /* evaluate right choice later */
+ if(++ntl >= NLIST)
+ goto Overflow;
+ addinst(tlp, inst->right, &tlp->se);
+ /* efficiency: advance and re-evaluate */
+ inst = inst->left;
+ goto Switchstmt;
+ case END: /* Match! */
+ tlp->se.p[0].p2 = p;
+ newmatch(&tlp->se);
+ break;
+ }
+ }
+ }
+ Return:
+ return sel.p[0].p1>=0;
+}
+
+void
+newmatch(Rangeset *sp)
+{
+ int i;
+
+ if(sel.p[0].p1<0 || sp->p[0].p1<sel.p[0].p1 ||
+ (sp->p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2))
+ for(i = 0; i<NSUBEXP; i++)
+ sel.p[i] = sp->p[i];
+}
+
+int
+bexecute(File *f, Posn startp)
+{
+ int flag = 0;
+ Inst *inst;
+ Ilist *tlp;
+ Posn p = startp;
+ int nnl = 0, ntl;
+ int c;
+ int wrapped = 0;
+ int startchar = bstartinst->type<OPERATOR? bstartinst->type : 0;
+
+ list[0][0].inst = list[1][0].inst = 0;
+ sel.p[0].p1= -1;
+ Fgetcset(f, startp);
+ /* Execute machine once for each character, including terminal NUL */
+ for(;;--p){
+ doloop:
+ if((c = Fbgetc(f))==-1){
+ switch(wrapped++){
+ case 0: /* let loop run one more click …
+ case 2:
+ break;
+ case 1: /* expired; wrap to end */
+ if(sel.p[0].p1>=0)
+ case 3:
+ goto Return;
+ list[0][0].inst = list[1][0].inst = 0;
+ Fgetcset(f, f->nrunes);
+ p = f->nrunes;
+ goto doloop;
+ default:
+ goto Return;
+ }
+ }else if(((wrapped && p<=startp) || sel.p[0].p1>0) && nnl==0)
+ break;
+ /* fast check for first char */
+ if(startchar && nnl==0 && c!=startchar)
+ continue;
+ tl = list[flag];
+ nl = list[flag^=1];
+ nl->inst = 0;
+ ntl = nnl;
+ nnl = 0;
+ if(sel.p[0].p1<0 && (!wrapped || p>startp)){
+ /* Add first instruction to this list */
+ if(++ntl >= NLIST)
+ Overflow:
+ error(Eoverflow);
+ /* the minus is so the optimizations in addinst work */
+ sempty.p[0].p1 = -p;
+ addinst(tl, bstartinst, &sempty);
+ }
+ /* Execute machine until this list is empty */
+ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment =…
+ Switchstmt:
+ switch(inst->type){
+ default: /* regular character */
+ if(inst->type == c){
+ Addinst:
+ if(++nnl >= NLIST)
+ goto Overflow;
+ addinst(nl, inst->next, &tlp->se);
+ }
+ break;
+ case LBRA:
+ if(inst->subid>=0)
+ tlp->se.p[inst->subid].p1 = p;
+ inst = inst->next;
+ goto Switchstmt;
+ case RBRA:
+ if(inst->subid >= 0)
+ tlp->se.p[inst->subid].p2 = p;
+ inst = inst->next;
+ goto Switchstmt;
+ case ANY:
+ if(c != '\n')
+ goto Addinst;
+ break;
+ case BOL:
+ if(c=='\n' || p==0){
+ Step:
+ inst = inst->next;
+ goto Switchstmt;
+ }
+ break;
+ case EOL:
+ if(f->getci<f->ngetc-1){
+ if(f->getcbuf[f->getci+1]=='\n')
+ goto Step;
+ }else if(p<f->nrunes-1){
+ Rune c;
+ if(Fchars(f, &c, p, p+1)==1 && c=='\n')
+ goto Step;
+ }
+ break;
+ case CCLASS:
+ if(c>=0 && classmatch(inst->rclass, c, 0))
+ goto Addinst;
+ break;
+ case NCCLASS:
+ if(c>=0 && classmatch(inst->rclass, c, 1))
+ goto Addinst;
+ break;
+ case OR:
+ /* evaluate right choice later */
+ if(++ntl >= NLIST)
+ goto Overflow;
+ addinst(tlp, inst->right, &tlp->se);
+ /* efficiency: advance and re-evaluate */
+ inst = inst->left;
+ goto Switchstmt;
+ case END: /* Match! */
+ tlp->se.p[0].p1 = -tlp->se.p[0].p1; /* minus s…
+ tlp->se.p[0].p2 = p;
+ bnewmatch(&tlp->se);
+ break;
+ }
+ }
+ }
+ Return:
+ return sel.p[0].p1>=0;
+}
+
+void
+bnewmatch(Rangeset *sp)
+{
+ int i;
+ if(sel.p[0].p1<0 || sp->p[0].p1>sel.p[0].p2 || (sp->p[0].p1==sel.p[0].…
+ for(i = 0; i<NSUBEXP; i++){ /* note the reversal; p1<=p2…
+ sel.p[i].p1 = sp->p[i].p2;
+ sel.p[i].p2 = sp->p[i].p1;
+ }
+}
diff --git a/sam/sam.c b/sam/sam.c
@@ -0,0 +1,682 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+Rune genbuf[BLOCKSIZE];
+int io;
+int panicking;
+int rescuing;
+Mod modnum;
+String genstr;
+String rhs;
+String wd;
+String cmdstr;
+Rune empty[] = { 0 };
+char *genc;
+File *curfile;
+File *flist;
+File *cmd;
+jmp_buf mainloop;
+List tempfile;
+int quitok = TRUE;
+int downloaded;
+int dflag;
+int Rflag;
+char *machine;
+char *home;
+int bpipeok;
+int termlocked;
+char *samterm = SAMTERM;
+char *rsamname = RSAM;
+
+Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
+
+void usage(void);
+
+int main(int argc, char *argv[])
+{
+ int i;
+ String *t;
+ char **ap, **arg;
+
+ arg = argv++;
+ ap = argv;
+ while(argc>1 && argv[0] && argv[0][0]=='-'){
+ switch(argv[0][1]){
+ case 'd':
+ dflag++;
+ break;
+
+ case 'r':
+ *ap++ = *argv++;
+ *ap++ = *argv;
+ --argc;
+ if(argc == 1)
+ usage();
+ machine = *argv;
+
+ rsamname = RXSAMNAME;
+ break;
+
+ case 'R':
+ Rflag++;
+ break;
+
+ case 't':
+ --argc, argv++;
+ if(argc == 1)
+ usage();
+ samterm = *argv;
+ break;
+
+ case 's':
+ --argc, argv++;
+ if(argc == 1)
+ usage();
+ rsamname = *argv;
+ break;
+
+ default:
+ dprint("sam: unknown flag %c\n", argv[0][1]);
+ exits("usage");
+ }
+ --argc, argv++;
+ }
+ Strinit(&cmdstr);
+ Strinit0(&lastpat);
+ Strinit0(&lastregexp);
+ Strinit0(&genstr);
+ Strinit0(&rhs);
+ Strinit0(&wd);
+ tempfile.listptr = emalloc(0);
+ Strinit0(&plan9cmd);
+ home = getenv(HOME);
+ if(home == 0)
+ home = "/";
+ if(!dflag)
+ startup(machine, Rflag, arg, ap);
+ Fstart();
+ notify(notifyf);
+ if(argc>1){
+ for(i=0; i<argc-1; i++)
+ if(!setjmp(mainloop)){
+ t = tmpcstr(argv[i]);
+ Straddc(t, '\0');
+ Strduplstr(&genstr, t);
+ freetmpstr(t);
+ Fsetname(newfile(), &genstr);
+ }
+ }else if(!downloaded)
+ newfile()->state = Clean;
+ modnum++;
+ if(file.nused)
+ current(file.filepptr[0]);
+ setjmp(mainloop);
+ cmdloop();
+ trytoquit(); /* if we already q'ed, quitok will be TRUE */
+ exits(0);
+}
+
+void
+usage(void)
+{
+ dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
+ exits("usage");
+}
+
+void
+rescue(void)
+{
+ int i, nblank = 0;
+ File *f;
+ char *c;
+ char buf[256];
+
+ if(rescuing++)
+ return;
+ io = -1;
+ for(i=0; i<file.nused; i++){
+ f = file.filepptr[i];
+ if(f==cmd || f->nrunes==0 || f->state!=Dirty)
+ continue;
+ if(io == -1){
+ sprint(buf, "%s/sam.save", home);
+ io = create(buf, 1, 0777);
+ if(io<0)
+ return;
+ }
+ if(f->name.s[0]){
+ c = Strtoc(&f->name);
+ strncpy(buf, c, sizeof buf-1);
+ buf[sizeof buf-1] = 0;
+ free(c);
+ }else
+ sprint(buf, "nameless.%d", nblank++);
+ fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf);
+ addr.r.p1 = 0, addr.r.p2 = f->nrunes;
+ writeio(f);
+ fprint(io, "\n---%s\n", (char *)buf);
+ }
+}
+
+void
+panic(char *s)
+{
+ int wasd;
+
+ if(!panicking++ && !setjmp(mainloop)){
+ wasd = downloaded;
+ downloaded = 0;
+ dprint("sam: panic: %s\n", s);
+ if(wasd)
+ fprint(2, "sam: panic: %s\n", s);
+ rescue();
+ abort();
+ }
+}
+
+void
+hiccough(char *s)
+{
+ if(rescuing)
+ exits("rescue");
+ if(s)
+ dprint("%s\n", s);
+ resetcmd();
+ resetxec();
+ resetsys();
+ if(io > 0)
+ close(io);
+ if(undobuf->nrunes)
+ Bdelete(undobuf, (Posn)0, undobuf->nrunes);
+ update();
+ if (curfile) {
+ if (curfile->state==Unread)
+ curfile->state = Clean;
+ else if (downloaded)
+ outTs(Hcurrent, curfile->tag);
+ }
+ longjmp(mainloop, 1);
+}
+
+void
+intr(void)
+{
+ error(Eintr);
+}
+
+void
+trytoclose(File *f)
+{
+ char *t;
+ char buf[256];
+
+ if(f == cmd) /* possible? */
+ return;
+ if(f->deleted)
+ return;
+ if(f->state==Dirty && !f->closeok){
+ f->closeok = TRUE;
+ if(f->name.s[0]){
+ t = Strtoc(&f->name);
+ strncpy(buf, t, sizeof buf-1);
+ free(t);
+ }else
+ strcpy(buf, "nameless file");
+ error_s(Emodified, buf);
+ }
+ f->deleted = TRUE;
+}
+
+void
+trytoquit(void)
+{
+ int c;
+ File *f;
+
+ if(!quitok)
+{
+ for(c = 0; c<file.nused; c++){
+ f = file.filepptr[c];
+ if(f!=cmd && f->state==Dirty){
+ quitok = TRUE;
+ eof = FALSE;
+ error(Echanges);
+ }
+ }
+}
+}
+
+void
+load(File *f)
+{
+ Address saveaddr;
+
+ Strduplstr(&genstr, &f->name);
+ filename(f);
+ if(f->name.s[0]){
+ saveaddr = addr;
+ edit(f, 'I');
+ addr = saveaddr;
+ }else
+ f->state = Clean;
+ Fupdate(f, TRUE, TRUE);
+}
+
+void
+cmdupdate(void)
+{
+ if(cmd && cmd->mod!=0){
+ Fupdate(cmd, FALSE, downloaded);
+ cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nrunes;
+ telldot(cmd);
+ }
+}
+
+void
+delete(File *f)
+{
+ if(downloaded && f->rasp)
+ outTs(Hclose, f->tag);
+ delfile(f);
+ if(f == curfile)
+ current(0);
+}
+
+void
+update(void)
+{
+ int i, anymod;
+ File *f;
+
+ settempfile();
+ for(anymod = i=0; i<tempfile.nused; i++){
+ f = tempfile.filepptr[i];
+ if(f==cmd) /* cmd gets done in main() */
+ continue;
+ if(f->deleted) {
+ delete(f);
+ continue;
+ }
+ if(f->mod==modnum && Fupdate(f, FALSE, downloaded))
+ anymod++;
+ if(f->rasp)
+ telldot(f);
+ }
+ if(anymod)
+ modnum++;
+}
+
+File *
+current(File *f)
+{
+ return curfile = f;
+}
+
+void
+edit(File *f, int cmd)
+{
+ int empty = TRUE;
+ Posn p;
+ int nulls;
+
+ if(cmd == 'r')
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ if(cmd=='e' || cmd=='I'){
+ Fdelete(f, (Posn)0, f->nrunes);
+ addr.r.p2 = f->nrunes;
+ }else if(f->nrunes!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0…
+ empty = FALSE;
+ if((io = open(genc, OREAD))<0) {
+ if (curfile && curfile->state == Unread)
+ curfile->state = Clean;
+ error_s(Eopen, genc);
+ }
+ p = readio(f, &nulls, empty);
+ closeio((cmd=='e' || cmd=='I')? -1 : p);
+ if(cmd == 'r')
+ f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
+ else
+ f->ndot.r.p1 = f->ndot.r.p2 = 0;
+ f->closeok = empty;
+ if (quitok)
+ quitok = empty;
+ else
+ quitok = FALSE;
+ state(f, empty && !nulls? Clean : Dirty);
+ if(cmd == 'e')
+ filename(f);
+}
+
+int
+getname(File *f, String *s, int save)
+{
+ int c, i;
+
+ Strzero(&genstr);
+ if(genc){
+ free(genc);
+ genc = 0;
+ }
+ if(s==0 || (c = s->s[0])==0){ /* no name provided */
+ if(f)
+ Strduplstr(&genstr, &f->name);
+ else
+ Straddc(&genstr, '\0');
+ goto Return;
+ }
+ if(c!=' ' && c!='\t')
+ error(Eblank);
+ for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
+ ;
+ while(s->s[i] > ' ')
+ Straddc(&genstr, s->s[i++]);
+ if(s->s[i])
+ error(Enewline);
+ Straddc(&genstr, '\0');
+ if(f && (save || f->name.s[0]==0)){
+ Fsetname(f, &genstr);
+ if(Strcmp(&f->name, &genstr)){
+ quitok = f->closeok = FALSE;
+ f->qid = 0;
+ f->date = 0;
+ state(f, Dirty); /* if it's 'e', fix later */
+ }
+ }
+ Return:
+ genc = Strtoc(&genstr);
+ return genstr.n-1; /* strlen(name) */
+}
+
+void
+filename(File *f)
+{
+ if(genc)
+ free(genc);
+ genc = Strtoc(&genstr);
+ dprint("%c%c%c %s\n", " '"[f->state==Dirty],
+ "-+"[f->rasp!=0], " ."[f==curfile], genc);
+}
+
+void
+undostep(File *f)
+{
+ Buffer *t;
+ int changes;
+ Mark mark;
+
+ t = f->transcript;
+ changes = Fupdate(f, TRUE, TRUE);
+ Bread(t, (Rune*)&mark, (sizeof mark)/RUNESIZE, f->markp);
+ Bdelete(t, f->markp, t->nrunes);
+ f->markp = mark.p;
+ f->dot.r = mark.dot;
+ f->ndot.r = mark.dot;
+ f->mark = mark.mark;
+ f->mod = mark.m;
+ f->closeok = mark.s1!=Dirty;
+ if(mark.s1==Dirty)
+ quitok = FALSE;
+ if(f->state==Clean && mark.s1==Clean && changes)
+ state(f, Dirty);
+ else
+ state(f, mark.s1);
+}
+
+int
+undo(void)
+{
+ File *f;
+ int i;
+ Mod max;
+ if((max = curfile->mod)==0)
+ return 0;
+ settempfile();
+ for(i = 0; i<tempfile.nused; i++){
+ f = tempfile.filepptr[i];
+ if(f!=cmd && f->mod==max)
+ undostep(f);
+ }
+ return 1;
+}
+
+int
+readcmd(String *s)
+{
+ int retcode;
+
+ if(flist == 0)
+ (flist = Fopen())->state = Clean;
+ addr.r.p1 = 0, addr.r.p2 = flist->nrunes;
+ retcode = plan9(flist, '<', s, FALSE);
+ Fupdate(flist, FALSE, FALSE);
+ flist->mod = 0;
+ if (flist->nrunes > BLOCKSIZE)
+ error(Etoolong);
+ Strzero(&genstr);
+ Strinsure(&genstr, flist->nrunes);
+ Fchars(flist, genbuf, (Posn)0, flist->nrunes);
+ memmove(genstr.s, genbuf, flist->nrunes*RUNESIZE);
+ genstr.n = flist->nrunes;
+ Straddc(&genstr, '\0');
+ return retcode;
+}
+
+void
+cd(String *str)
+{
+ int i;
+ File *f;
+ String *t;
+
+ t = tmpcstr("/bin/pwd");
+ Straddc(t, '\0');
+ if (flist) {
+ Fclose(flist);
+ flist = 0;
+ }
+ if (readcmd(t) != 0) {
+ Strduplstr(&genstr, tmprstr(baddir, sizeof(baddir)/sizeof(Rune…
+ Straddc(&genstr, '\0');
+ }
+ freetmpstr(t);
+ Strduplstr(&wd, &genstr);
+ if(wd.s[0] == 0){
+ wd.n = 0;
+ warn(Wpwd);
+ }else if(wd.s[wd.n-2] == '\n'){
+ --wd.n;
+ wd.s[wd.n-1]='/';
+ }
+ if(chdir(getname((File *)0, str, FALSE)? genc : home))
+ syserror("chdir");
+ settempfile();
+ for(i=0; i<tempfile.nused; i++){
+ f = tempfile.filepptr[i];
+ if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
+ Strinsert(&f->name, &wd, (Posn)0);
+ sortname(f);
+ }
+ }
+}
+
+int
+loadflist(String *s)
+{
+ int c, i;
+
+ c = s->s[0];
+ for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
+ ;
+ if((c==' ' || c=='\t') && s->s[i]!='\n'){
+ if(s->s[i]=='<'){
+ Strdelete(s, 0L, (long)i+1);
+ readcmd(s);
+ }else{
+ Strzero(&genstr);
+ while((c = s->s[i++]) && c!='\n')
+ Straddc(&genstr, c);
+ Straddc(&genstr, '\0');
+ }
+ }else{
+ if(c != '\n')
+ error(Eblank);
+ Strdupl(&genstr, empty);
+ }
+ if(genc)
+ free(genc);
+ genc = Strtoc(&genstr);
+ return genstr.s[0];
+}
+
+File *
+readflist(int readall, int delete)
+{
+ Posn i;
+ int c;
+ File *f;
+ String *t;
+
+ for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank…
+ Strdelete(&genstr, (Posn)0, i);
+ for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
+ ;
+ if(i >= genstr.n)
+ break;
+ Strdelete(&genstr, (Posn)0, i);
+ for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
+ ;
+
+ if(i == 0)
+ break;
+ genstr.s[i] = 0;
+ t = tmprstr(genstr.s, i+1);
+ f = lookfile(t);
+ if(delete){
+ if(f == 0)
+ warn_S(Wfile, t);
+ else
+ trytoclose(f);
+ }else if(f==0 && readall)
+ Fsetname(f = newfile(), t);
+ }
+ return f;
+}
+
+File *
+tofile(String *s)
+{
+ File *f;
+
+ if(s->s[0] != ' ')
+ error(Eblank);
+ if(loadflist(s) == 0){
+ f = lookfile(&genstr); /* empty string ==> nameless fil…
+ if(f == 0)
+ error_s(Emenu, genc);
+ }else if((f=readflist(FALSE, FALSE)) == 0)
+ error_s(Emenu, genc);
+ return current(f);
+}
+
+File *
+getfile(String *s)
+{
+ File *f;
+
+ if(loadflist(s) == 0)
+ Fsetname(f = newfile(), &genstr);
+ else if((f=readflist(TRUE, FALSE)) == 0)
+ error(Eblank);
+ return current(f);
+}
+
+void
+closefiles(File *f, String *s)
+{
+ if(s->s[0] == 0){
+ if(f == 0)
+ error(Enofile);
+ trytoclose(f);
+ return;
+ }
+ if(s->s[0] != ' ')
+ error(Eblank);
+ if(loadflist(s) == 0)
+ error(Enewline);
+ readflist(FALSE, TRUE);
+}
+
+void
+copy(File *f, Address addr2)
+{
+ Posn p;
+ int ni;
+ for(p=addr.r.p1; p<addr.r.p2; p+=ni){
+ ni = addr.r.p2-p;
+ if(ni > BLOCKSIZE)
+ ni = BLOCKSIZE;
+ Fchars(f, genbuf, p, p+ni);
+ Finsert(addr2.f, tmprstr(genbuf, ni), addr2.r.p2);
+ }
+ addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
+ addr2.f->ndot.r.p1 = addr2.r.p2;
+}
+
+void
+move(File *f, Address addr2)
+{
+ if(addr.r.p2 <= addr2.r.p2){
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ copy(f, addr2);
+ }else if(addr.r.p1 >= addr2.r.p2){
+ copy(f, addr2);
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ }else
+ error(Eoverlap);
+}
+
+Posn
+nlcount(File *f, Posn p0, Posn p1)
+{
+ Posn nl = 0;
+
+ Fgetcset(f, p0);
+ while(p0++<p1)
+ if(Fgetc(f)=='\n')
+ nl++;
+ return nl;
+}
+
+void
+printposn(File *f, int charsonly)
+{
+ Posn l1, l2;
+
+ if(!charsonly){
+ l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
+ l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
+ /* check if addr ends with '\n' */
+ if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && (Fgetcset(f, addr.r.p…
+ --l2;
+ dprint("%lu", l1);
+ if(l2 != l1)
+ dprint(",%lu", l2);
+ dprint("; ");
+ }
+ dprint("#%lu", addr.r.p1);
+ if(addr.r.p2 != addr.r.p1)
+ dprint(",#%lu", addr.r.p2);
+ dprint("\n");
+}
+
+void
+settempfile(void)
+{
+ if(tempfile.nalloc < file.nused){
+ free(tempfile.listptr);
+ tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nus…
+ tempfile.nalloc = file.nused;
+ }
+ tempfile.nused = file.nused;
+ memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(Fi…
+}
diff --git a/sam/sam.h b/sam/sam.h
@@ -0,0 +1,408 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include "errors.h"
+
+/*
+ * BLOCKSIZE is relatively small to keep memory consumption down.
+ */
+
+#define BLOCKSIZE 2048
+#define RUNESIZE sizeof(Rune)
+#define NDISC 5
+#define NBUFFILES 3+2*NDISC /* plan 9+undo+snarf+NDISC*(t…
+#define NSUBEXP 10
+
+#define TRUE 1
+#define FALSE 0
+
+#define INFINITY 0x7FFFFFFFL
+#define INCR 25
+#define STRSIZE (2*BLOCKSIZE)
+
+typedef long Posn; /* file position or address */
+typedef ushort Mod; /* modification numbe…
+
+typedef struct Address Address;
+typedef struct Block Block;
+typedef struct Buffer Buffer;
+typedef struct Disc Disc;
+typedef struct Discdesc Discdesc;
+typedef struct File File;
+typedef struct List List;
+typedef struct Mark Mark;
+typedef struct Range Range;
+typedef struct Rangeset Rangeset;
+typedef struct String String;
+
+enum State
+{
+ Clean = ' ',
+ Dirty = '\'',
+ Unread = '-',
+ Readerr = '~'
+};
+
+struct Range
+{
+ Posn p1, p2;
+};
+
+struct Rangeset
+{
+ Range p[NSUBEXP];
+};
+
+struct Address
+{
+ Range r;
+ File *f;
+};
+
+struct List /* code depends on a long being able to hold a pointer */
+{
+ int nalloc;
+ int nused;
+ union{
+ void *listp;
+ Block *blkp;
+ long *longp;
+ uchar* *ucharp;
+ String* *stringp;
+ File* *filep;
+ long listv;
+ }g;
+};
+
+#define listptr g.listp
+#define blkptr g.blkp
+#define longptr g.longp
+#define ucharpptr g.ucharp
+#define stringpptr g.stringp
+#define filepptr g.filep
+#define listval g.listv
+
+/*
+ * Block must fit in a long because the list routines manage arrays of
+ * blocks. Two problems: some machines (e.g. Cray) can't pull this off
+ * -- on them, use bitfields -- and the ushort bnum limits temp file sizes
+ * to about 200 megabytes. Advantages: small, simple code and small
+ * memory overhead. If you really want to edit huge files, making BLOCKSIZE
+ * bigger is the easiest way.
+*
+* The necessary conditions are even stronger:
+* sizeof(struct Block)==sizeof(long)
+* && the first 32 bits must hold bnum and nrunes.
+* When sizeof(ushort)+sizeof(short) < sizeof(long),
+* add padding at the beginning on a little endian and at
+* the end on a big endian, as shown below for the DEC Alpha.
+ */
+struct Block
+{
+#if USE64BITS == 1
+ char pad[sizeof(long)-sizeof(ushort)-sizeof(short)];
+#endif
+ ushort bnum; /* absolute number on disk */
+ short nrunes; /* runes stored in this block */
+#if USE64BITS == 2
+ char pad[sizeof(long)-sizeof(ushort)-sizeof(short)];
+#endif
+};
+
+struct Discdesc
+{
+ int fd; /* plan 9 file descriptor of temp file */
+ ulong nbk; /* high water mark */
+ List free; /* array of free block indices */
+};
+
+struct Disc
+{
+ Discdesc *desc; /* descriptor of temp file */
+ Posn nrunes; /* runes on disc file */
+ List block; /* list of used block indices */
+};
+
+struct String
+{
+ short n;
+ short size;
+ Rune *s;
+};
+
+struct Buffer
+{
+ Disc *disc; /* disc storage */
+ Posn nrunes; /* total length of buffer */
+ String cache; /* in-core storage for efficiency …
+ Posn c1, c2; /* cache start and end positions in…
+ /* note: if dirty, cache is really c1, c1+cach…
+ int dirty; /* cache dirty */
+};
+
+#define NGETC 128
+
+struct File
+{
+ Buffer *buf; /* cached disc storage */
+ Buffer *transcript; /* what's been done */
+ Posn markp; /* file pointer to start of latest c…
+ Mod mod; /* modification stamp */
+ Posn nrunes; /* total length of file */
+ Posn hiposn; /* highest address touched this Mod…
+ Address dot; /* current position */
+ Address ndot; /* new current position after upda…
+ Range tdot; /* what terminal thinks is current r…
+ Range mark; /* tagged spot in text (don't confus…
+ List *rasp; /* map of what terminal's got */
+ String name; /* file name */
+ short tag; /* for communicating with terminal */
+ char state; /* Clean, Dirty, Unread, or Readerr*/
+ char closeok; /* ok to close file? */
+ char deleted; /* delete at completion of command */
+ char marked; /* file has been Fmarked at least o…
+ * set, this will never go off as undo doesn't
+ * revert to the dawn of time */
+ long dev; /* file system from which it was read …
+ long qid; /* file from which it was read */
+ long date; /* time stamp of plan9 file */
+ Posn cp1, cp2; /* Write-behind cache positions and */
+ String cache; /* string */
+ Rune getcbuf[NGETC];
+ int ngetc;
+ int getci;
+ Posn getcp;
+};
+
+struct Mark
+{
+ Posn p;
+ Range dot;
+ Range mark;
+ Mod m;
+ short s1;
+};
+
+/*
+ * The precedent to any message in the transcript.
+ * The component structures must be an integral number of Runes long.
+ */
+union Hdr
+{
+ struct _csl
+ {
+ short c;
+ short s;
+ long l;
+ }csl;
+ struct _cs
+ {
+ short c;
+ short s;
+ }cs;
+ struct _cll
+ {
+ short c;
+ long l;
+ long l1;
+ }cll;
+ Mark mark;
+};
+
+#define Fgetc(f) ((--(f)->ngetc<0)? Fgetcload(f, (f)->getcp) : (f)->ge…
+#define Fbgetc(f) (((f)->getci<=0)? Fbgetcload(f, (f)->getcp) : (f)->ge…
+
+int alnum(int);
+void Bclean(Buffer*);
+void Bterm(Buffer*);
+void Bdelete(Buffer*, Posn, Posn);
+void Bflush(Buffer*);
+void Binsert(Buffer*, String*, Posn);
+Buffer *Bopen(Discdesc*);
+int Bread(Buffer*, Rune*, int, Posn);
+void Dclose(Disc*);
+void Ddelete(Disc*, Posn, Posn);
+void Dinsert(Disc*, Rune*, int, Posn);
+Disc *Dopen(Discdesc*);
+int Dread(Disc*, Rune*, int, Posn);
+void Dreplace(Disc*, Posn, Posn, Rune*, int);
+int Fbgetcload(File*, Posn);
+int Fbgetcset(File*, Posn);
+long Fchars(File*, Rune*, Posn, Posn);
+void Fclose(File*);
+void Fdelete(File*, Posn, Posn);
+int Fgetcload(File*, Posn);
+int Fgetcset(File*, Posn);
+void Finsert(File*, String*, Posn);
+File *Fopen(void);
+void Fsetname(File*, String*);
+void Fstart(void);
+int Fupdate(File*, int, int);
+int Read(int, void*, int);
+void Seek(int, long, int);
+int plan9(File*, int, String*, int);
+int Write(int, void*, int);
+int bexecute(File*, Posn);
+void cd(String*);
+void closefiles(File*, String*);
+void closeio(Posn);
+void cmdloop(void);
+void cmdupdate(void);
+void compile(String*);
+void copy(File*, Address);
+File *current(File*);
+void delete(File*);
+void delfile(File*);
+void dellist(List*, int);
+void doubleclick(File*, Posn);
+void dprint(char*, ...);
+void edit(File*, int);
+void *emalloc(ulong);
+void *erealloc(void*, ulong);
+void error(Err);
+void error_c(Err, int);
+void error_s(Err, char*);
+int execute(File*, Posn, Posn);
+int filematch(File*, String*);
+void filename(File*);
+File *getfile(String*);
+int getname(File*, String*, int);
+long getnum(void);
+void hiccough(char*);
+void inslist(List*, int, long);
+Address lineaddr(Posn, Address, int);
+void listfree(List*);
+void load(File*);
+File *lookfile(String*);
+void lookorigin(File*, Posn, Posn);
+int lookup(int);
+void move(File*, Address);
+void moveto(File*, Range);
+File *newfile(void);
+void nextmatch(File*, String*, Posn, int);
+int newtmp(int);
+void notifyf(void*, char*);
+void panic(char*);
+void printposn(File*, int);
+void print_ss(char*, String*, String*);
+void print_s(char*, String*);
+int rcv(void);
+Range rdata(List*, Posn, Posn);
+Posn readio(File*, int*, int);
+void rescue(void);
+void resetcmd(void);
+void resetsys(void);
+void resetxec(void);
+void rgrow(List*, Posn, Posn);
+void samerr(char*);
+void settempfile(void);
+int skipbl(void);
+void snarf(File*, Posn, Posn, Buffer*, int);
+void sortname(File*);
+void startup(char*, int, char**, char**);
+void state(File*, int);
+int statfd(int, ulong*, ulong*, long*, long*, long*);
+int statfile(char*, ulong*, ulong*, long*, long*, long*);
+void Straddc(String*, int);
+void Strclose(String*);
+int Strcmp(String*, String*);
+void Strdelete(String*, Posn, Posn);
+void Strdupl(String*, Rune*);
+void Strduplstr(String*, String*);
+void Strinit(String*);
+void Strinit0(String*);
+void Strinsert(String*, String*, Posn);
+void Strinsure(String*, ulong);
+void Strzero(String*);
+int Strlen(Rune*);
+char *Strtoc(String*);
+void syserror(char*);
+void telldot(File*);
+void tellpat(void);
+String *tmpcstr(char*);
+String *tmprstr(Rune*, int);
+void freetmpstr(String*);
+void termcommand(void);
+void termwrite(char*);
+File *tofile(String*);
+void toterminal(File*, int);
+void trytoclose(File*);
+void trytoquit(void);
+int undo(void);
+void update(void);
+int waitfor(int);
+void warn(Warn);
+void warn_s(Warn, char*);
+void warn_SS(Warn, String*, String*);
+void warn_S(Warn, String*);
+int whichmenu(File*);
+void writef(File*);
+Posn writeio(File*);
+Discdesc *Dstart(void);
+
+extern Rune samname[]; /* compiler dependent */
+extern Rune *left[];
+extern Rune *right[];
+
+extern char RSAM[]; /* system dependent */
+extern char SAMTERM[];
+extern char HOME[];
+extern char TMPDIR[];
+extern char SH[];
+extern char SHPATH[];
+extern char RX[];
+extern char RXPATH[];
+extern char SAMSAVECMD[];
+
+extern char *rsamname; /* globals */
+extern char *samterm;
+extern Rune genbuf[];
+extern char *genc;
+extern int io;
+extern int patset;
+extern int quitok;
+extern Address addr;
+extern Buffer *undobuf;
+extern Buffer *snarfbuf;
+extern Buffer *plan9buf;
+extern List file;
+extern List tempfile;
+extern File *cmd;
+extern File *curfile;
+extern File *lastfile;
+extern Mod modnum;
+extern Posn cmdpt;
+extern Posn cmdptadv;
+extern Rangeset sel;
+extern String cmdstr;
+extern String genstr;
+extern String lastpat;
+extern String lastregexp;
+extern String plan9cmd;
+extern int downloaded;
+extern int eof;
+extern int bpipeok;
+extern int panicking;
+extern Rune empty[];
+extern int termlocked;
+extern int noflush;
+
+#include "mesg.h"
+
+void outTs(Hmesg, int);
+void outT0(Hmesg);
+void outTl(Hmesg, long);
+void outTslS(Hmesg, int, long, String*);
+void outTS(Hmesg, String*);
+void outTsS(Hmesg, int, String*);
+void outTsllS(Hmesg, int, long, long, String*);
+void outTsll(Hmesg, int, long, long);
+void outTsl(Hmesg, int, long);
+void outTsv(Hmesg, int, long);
+void outstart(Hmesg);
+void outcopy(int, void*);
+void outshort(int);
+void outlong(long);
+void outvlong(void*);
+void outsend(void);
+void outflush(void);
diff --git a/sam/samsave b/sam/samsave
@@ -0,0 +1,14 @@
+#!/bin/sh
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+PATH=/bin:/usr/bin
+file=$1
+case "$2" in
+-f) echo "$file"
+ cat > $file
+ ;;
+"") echo "$file?"
+ read yn < /dev/tty
+ case "$yn" in
+ [Yy]*) cat > $file
+ esac
+esac
diff --git a/sam/shell.c b/sam/shell.c
@@ -0,0 +1,155 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+#include "parse.h"
+
+extern jmp_buf mainloop;
+
+char errfile[64];
+String plan9cmd; /* null terminated */
+Buffer *plan9buf;
+void checkerrs(void);
+
+int
+plan9(File *f, int type, String *s, int nest)
+{
+ long l;
+ int m;
+ int pid, fd;
+ int retcode;
+ int pipe1[2], pipe2[2];
+
+ if(s->s[0]==0 && plan9cmd.s[0]==0)
+ error(Enocmd);
+ else if(s->s[0])
+ Strduplstr(&plan9cmd, s);
+ if(downloaded)
+ samerr(errfile);
+ else
+ strcpy(errfile, "/dev/tty");
+ if(type!='!' && pipe(pipe1)==-1)
+ error(Epipe);
+ if(type=='|')
+ snarf(f, addr.r.p1, addr.r.p2, plan9buf, 1);
+ if(downloaded)
+ remove(errfile);
+ if((pid=fork()) == 0){
+ if(downloaded){ /* also put nasty fd's into errfile */
+ fd = create(errfile, 1, 0666L);
+ if(fd < 0)
+ fd = create("/dev/null", 1, 0666L);
+ dup(fd, 2);
+ close(fd);
+ /* 2 now points at err file */
+ if(type == '>')
+ dup(2, 1);
+ else if(type=='!'){
+ dup(2, 1);
+ fd = open("/dev/null", 0);
+ dup(fd, 0);
+ close(fd);
+ }
+ }
+ if(type != '!') {
+ if(type=='<' || type=='|')
+ dup(pipe1[1], 1);
+ else if(type == '>')
+ dup(pipe1[0], 0);
+ close(pipe1[0]);
+ close(pipe1[1]);
+ }
+ if(type == '|'){
+ if(pipe(pipe2) == -1)
+ exits("pipe");
+ if((pid = fork())==0){
+ /*
+ * It's ok if we get SIGPIPE here
+ */
+ close(pipe2[0]);
+ io = pipe2[1];
+ if(retcode=!setjmp(mainloop)){ /* assig…
+ char *c;
+ for(l = 0; l<plan9buf->nrunes; l+=m){
+ m = plan9buf->nrunes-l;
+ if(m>BLOCKSIZE-1)
+ m = BLOCKSIZE-1;
+ Bread(plan9buf, genbuf, m, l);
+ genbuf[m] = 0;
+ c = Strtoc(tmprstr(genbuf, m+1…
+ Write(pipe2[1], c, strlen(c));
+ free(c);
+ }
+ }
+ exits(retcode? "error" : 0);
+ }
+ if(pid==-1){
+ fprint(2, "Can't fork?!\n");
+ exits("fork");
+ }
+ dup(pipe2[0], 0);
+ close(pipe2[0]);
+ close(pipe2[1]);
+ }
+ if(type=='<'){
+ close(0); /* so it won't read from terminal */
+ open("/dev/null", 0);
+ }
+ execl(SHPATH, SH, "-c", Strtoc(&plan9cmd), (char *)0);
+ exits("exec");
+ }
+ if(pid == -1)
+ error(Efork);
+ if(type=='<' || type=='|'){
+ int nulls;
+ if(downloaded && addr.r.p1 != addr.r.p2)
+ outTl(Hsnarflen, addr.r.p2-addr.r.p1);
+ snarf(f, addr.r.p1, addr.r.p2, snarfbuf, 0);
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ close(pipe1[1]);
+ io = pipe1[0];
+ f->tdot.p1 = -1;
+ f->ndot.r.p2 = addr.r.p2+readio(f, &nulls, 0);
+ f->ndot.r.p1 = addr.r.p2;
+ closeio((Posn)-1);
+ }else if(type=='>'){
+ close(pipe1[0]);
+ io = pipe1[1];
+ bpipeok = 1;
+ writeio(f);
+ bpipeok = 0;
+ closeio((Posn)-1);
+ }
+ retcode = waitfor(pid);
+ if(type=='|' || type=='<')
+ if(retcode!=0)
+ warn(Wbadstatus);
+ if(downloaded)
+ checkerrs();
+ if(!nest)
+ dprint("!\n");
+ return retcode;
+}
+
+void
+checkerrs(void)
+{
+ char buf[256];
+ int f, n, nl;
+ char *p;
+ long l;
+
+ if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){
+ if((f=open((char *)errfile, 0)) != -1){
+ if((n=read(f, buf, sizeof buf-1)) > 0){
+ for(nl=0,p=buf; nl<3 && p<&buf[n]; p++)
+ if(*p=='\n')
+ nl++;
+ *p = 0;
+ dprint("%s", buf);
+ if(p-buf < l-1)
+ dprint("(sam: more in %s)\n", errfile);
+ }
+ close(f);
+ }
+ }else
+ remove((char *)errfile);
+}
diff --git a/sam/string.c b/sam/string.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+#define MINSIZE 16 /* minimum number of chars all…
+#define MAXSIZE 256 /* maximum number of chars fo…
+
+
+void
+Strinit(String *p)
+{
+ p->s = emalloc(MINSIZE*RUNESIZE);
+ p->n = 0;
+ p->size = MINSIZE;
+}
+
+void
+Strinit0(String *p)
+{
+ p->s = emalloc(MINSIZE*RUNESIZE);
+ p->s[0] = 0;
+ p->n = 1;
+ p->size = MINSIZE;
+}
+
+void
+Strclose(String *p)
+{
+ free(p->s);
+}
+
+void
+Strzero(String *p)
+{
+ if(p->size > MAXSIZE){
+ p->s = erealloc(p->s, RUNESIZE*MAXSIZE); /* throw away the gar…
+ p->size = MAXSIZE;
+ }
+ p->n = 0;
+}
+
+int
+Strlen(Rune *r)
+{
+ Rune *s;
+
+ for(s=r; *s; s++)
+ ;
+ return s-r;
+}
+
+void
+Strdupl(String *p, Rune *s) /* copies the null */
+{
+ p->n = Strlen(s)+1;
+ Strinsure(p, p->n);
+ memmove(p->s, s, p->n*RUNESIZE);
+}
+
+void
+Strduplstr(String *p, String *q) /* will copy the null if there's one t…
+{
+ Strinsure(p, q->n);
+ p->n = q->n;
+ memmove(p->s, q->s, q->n*RUNESIZE);
+}
+
+void
+Straddc(String *p, int c)
+{
+ Strinsure(p, p->n+1);
+ p->s[p->n++] = c;
+}
+
+void
+Strinsure(String *p, ulong n)
+{
+ if(n > STRSIZE)
+ error(Etoolong);
+ if(p->size < n){ /* p needs to grow */
+ n += 100;
+ p->s = erealloc(p->s, n*RUNESIZE);
+ p->size = n;
+ }
+}
+
+void
+Strinsert(String *p, String *q, Posn p0)
+{
+ Strinsure(p, p->n+q->n);
+ memmove(p->s+p0+q->n, p->s+p0, (p->n-p0)*RUNESIZE);
+ memmove(p->s+p0, q->s, q->n*RUNESIZE);
+ p->n += q->n;
+}
+
+void
+Strdelete(String *p, Posn p1, Posn p2)
+{
+ memmove(p->s+p1, p->s+p2, (p->n-p2)*RUNESIZE);
+ p->n -= p2-p1;
+}
+
+int
+Strcmp(String *a, String *b)
+{
+ int i, c;
+
+ for(i=0; i<a->n && i<b->n; i++)
+ if(c = (a->s[i] - b->s[i])) /* assign = */
+ return c;
+ /* damn NULs confuse everything */
+ i = a->n - b->n;
+ if(i == 1){
+ if(a->s[a->n-1] == 0)
+ return 0;
+ }else if(i == -1){
+ if(b->s[b->n-1] == 0)
+ return 0;
+ }
+ return i;
+}
+
+char*
+Strtoc(String *s)
+{
+ int i;
+ char *c, *d;
+ Rune *r;
+ c = emalloc(s->n*UTFmax + 1); /* worst case UTFmax bytes per rune, pl…
+ d = c;
+ r = s->s;
+ for(i=0; i<s->n; i++)
+ d += runetochar(d, r++);
+ if(d==c || d[-1]!=0)
+ *d = 0;
+ return c;
+
+}
+
+/*
+ * Build very temporary String from Rune*
+ */
+String*
+tmprstr(Rune *r, int n)
+{
+ static String p;
+
+ p.s = r;
+ p.n = n;
+ p.size = n;
+ return &p;
+}
+
+/*
+ * Convert null-terminated char* into String
+ */
+String*
+tmpcstr(char *s)
+{
+ String *p;
+ Rune *r;
+ int i, n;
+
+ n = utflen(s); /* don't include NUL */
+ p = emalloc(sizeof(String));
+ r = emalloc(n*RUNESIZE);
+ p->s = r;
+ for(i=0; i<n; i++,r++)
+ s += chartorune(r, s);
+ p->n = n;
+ p->size = n;
+ return p;
+}
+
+void
+freetmpstr(String *s)
+{
+ free(s->s);
+ free(s);
+}
diff --git a/sam/sys.c b/sam/sys.c
@@ -0,0 +1,61 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+
+static int inerror=FALSE;
+
+/*
+ * A reasonable interface to the system calls
+ */
+
+void
+resetsys(void)
+{
+ inerror = FALSE;
+}
+
+void
+syserror(char *a)
+{
+ char buf[ERRLEN];
+
+ if(!inerror){
+ inerror=TRUE;
+ errstr(buf);
+ dprint("%s: ", a);
+ error_s(Eio, buf);
+ }
+}
+
+int
+Read(int f, void *a, int n)
+{
+ char buf[ERRLEN];
+
+ if(read(f, (char *)a, n)!=n) {
+ if (lastfile)
+ lastfile->state = Readerr;
+ errstr(buf);
+ if (downloaded)
+ fprint(2, "read error: %s\n", buf);
+ rescue();
+ exits("read");
+ }
+ return n;
+}
+
+int
+Write(int f, void *a, int n)
+{
+ int m;
+
+ if((m=write(f, (char *)a, n))!=n)
+ syserror("write");
+ return m;
+}
+
+void
+Seek(int f, long n, int w)
+{
+ if(seek(f, n, w)==-1)
+ syserror("seek");
+}
diff --git a/sam/unix.c b/sam/unix.c
@@ -0,0 +1,220 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#ifdef NEEDVARARG
+#include <varargs.h>
+#else
+#include <stdarg.h>
+#endif
+
+Rune samname[] = { '~', '~', 's', 'a', 'm', '~', '~', 0 };
+
+static Rune l1[] = { '{', '[', '(', '<', 0253, 0};
+static Rune l2[] = { '\n', 0};
+static Rune l3[] = { '\'', '"', '`', 0};
+Rune *left[]= { l1, l2, l3, 0};
+
+static Rune r1[] = {'}', ']', ')', '>', 0273, 0};
+static Rune r2[] = {'\n', 0};
+static Rune r3[] = {'\'', '"', '`', 0};
+Rune *right[]= { r1, r2, r3, 0};
+
+char RSAM[] = RSAMNAME;
+char SAMTERM[] = TERMNAME;
+char HOME[] = HOMEDIR;
+char TMPDIR[] = TMP;
+char SH[] = SHELLNAME;
+char SHPATH[] = SHELLPATH;
+char RX[] = RXNAME;
+char RXPATH[] = RXPATHNAME;
+char SAMSAVECMD[] = SAMSAVE;
+
+void
+print_ss(char *s, String *a, String *b)
+{
+ char *ap, *bp, *cp;
+ Rune *rp;
+
+ ap = emalloc(a->n+1);
+ for (cp = ap, rp = a->s; *rp; rp++)
+ cp += runetochar(cp, rp);
+ *cp = 0;
+ bp = emalloc(b->n+1);
+ for (cp = bp, rp = b->s; *rp; rp++)
+ cp += runetochar(cp, rp);
+ *cp = 0;
+ dprint("?warning: %s `%.*s' and `%.*s'\n", s, a->n, ap, b->n, bp);
+ free(ap);
+ free(bp);
+}
+
+void
+print_s(char *s, String *a)
+{
+ char *ap, *cp;
+ Rune *rp;
+
+ ap = emalloc(a->n+1);
+ for (cp = ap, rp = a->s; *rp; rp++)
+ cp += runetochar(cp, rp);
+ *cp = 0;
+ dprint("?warning: %s `%.*s'\n", s, a->n, ap);
+ free(ap);
+}
+
+int
+statfile(char *name, ulong *dev, ulong *id, long *time, long *length, long *ap…
+{
+ struct stat dirb;
+
+ if (stat(name, &dirb) == -1)
+ return -1;
+ if (dev)
+ *dev = dirb.st_dev;
+ if (id)
+ *id = dirb.st_ino;
+ if (time)
+ *time = dirb.st_mtime;
+ if (length)
+ *length = dirb.st_size;
+ if(appendonly)
+ *appendonly = 0;
+ return 1;
+}
+
+int
+statfd(int fd, ulong *dev, ulong *id, long *time, long *length, long *appendon…
+{
+ struct stat dirb;
+
+ if (fstat(fd, &dirb) == -1)
+ return -1;
+ if (dev)
+ *dev = dirb.st_dev;
+ if (id)
+ *id = dirb.st_ino;
+ if (time)
+ *time = dirb.st_mtime;
+ if (length)
+ *length = dirb.st_size;
+ if(appendonly)
+ *appendonly = 0;
+ return 1;
+}
+
+void
+hup(int sig)
+{
+ rescue();
+ exit(1);
+}
+
+int
+notify (void(*f)(void *, char *))
+{
+ signal(SIGINT, SIG_IGN);
+ signal(SIGHUP, hup);
+ signal(SIGPIPE, SIG_IGN);
+#ifdef v10
+ close(3); /* redirect v10 /dev/tty */
+ open("/dev/null", 2);
+#endif
+ return 1;
+}
+
+void
+notifyf(void *a, char *b) /* never called */
+{
+}
+
+/*
+ * if your system doesn't have tempnam(), substitute the following
+ * code for this function:
+ * FILE *f;
+ * f = tmpfile();
+ * if (f == 0)
+ * return -1;
+ * return fileno(f);
+ *
+ * we use tempnam to allow temp files to be allocated in the
+ * most efficient place; nodes with disks may mount /usr/tmp
+ * remotely, causing excessive network traffic. place
+ * the temp files locally, if possible.
+ */
+int
+newtmp(int i)
+{
+ char s[1024] = {0};
+ sprint(s, "%s/sam.XXXXXX", TMPDIR);
+ int fd = mkstemp(s);
+ if (fd >= 0)
+ {
+ unlink(s);
+ }
+ return fd;
+}
+
+void
+samerr(char *buf)
+{
+ sprint(buf, "%s/sam.err.%.6s", TMPDIR, getuser());
+}
+
+int
+waitfor(int pid)
+{
+ int wm;
+ int rpid;
+
+ do; while((rpid = wait(&wm)) != pid && rpid != -1);
+ return (WEXITSTATUS(wm));
+}
+
+void*
+emalloc(ulong n)
+{
+ void *p;
+
+ if (n < sizeof(int))
+ n = sizeof(int);
+ p = malloc(n);
+ if(p == 0)
+ panic("malloc fails");
+ memset(p, 0, n);
+ return p;
+}
+
+void*
+erealloc(void *p, ulong n)
+{
+ p = realloc(p, n);
+ if(p == 0)
+ panic("realloc fails");
+ return p;
+}
+
+void
+exits(char *message)
+{
+
+ if (message == 0)
+ exit(0);
+ else
+ exit(1);
+}
+
+void
+dprint(char *z, ...)
+{
+ va_list args;
+ char buf[BLOCKSIZE];
+
+ va_start(args, z);
+ vsprintf(buf, z, args);
+ termwrite(buf);
+ va_end(args);
+}
+
diff --git a/sam/xec.c b/sam/xec.c
@@ -0,0 +1,492 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include "sam.h"
+#include "parse.h"
+
+int Glooping;
+int nest;
+
+int append(File*, Cmd*, Posn);
+int display(File*);
+void looper(File*, Cmd*, int);
+void filelooper(Cmd*, int);
+void linelooper(File*, Cmd*);
+
+void
+resetxec(void)
+{
+ Glooping = nest = 0;
+}
+
+int
+cmdexec(File *f, Cmd *cp)
+{
+ int i;
+ Addr *ap;
+ Address a;
+
+ if(f && f->state==Unread)
+ load(f);
+ if(f==0 && (cp->addr==0 || cp->addr->type!='"') &&
+ !utfrune("bBnqUXY!", cp->cmdc) &&
+ cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext))
+ error(Enofile);
+ i = lookup(cp->cmdc);
+ if(i >= 0 && cmdtab[i].defaddr != aNo){
+ if((ap=cp->addr)==0 && cp->cmdc!='\n'){
+ cp->addr = ap = newaddr();
+ ap->type = '.';
+ if(cmdtab[i].defaddr == aAll)
+ ap->type = '*';
+ }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
+ ap->next = newaddr();
+ ap->next->type = '.';
+ if(cmdtab[i].defaddr == aAll)
+ ap->next->type = '*';
+ }
+ if(cp->addr){ /* may be false for '\n' (only) */
+ static Address none = {0,0,0};
+ if(f)
+ addr = address(ap, f->dot, 0);
+ else /* a " */
+ addr = address(ap, none, 0);
+ f = addr.f;
+ }
+ }
+ current(f);
+ switch(cp->cmdc){
+ case '{':
+ a = cp->addr? address(cp->addr, f->dot, 0): f->dot;
+ for(cp = cp->ccmd; cp; cp = cp->next){
+ a.f->dot = a;
+ cmdexec(a.f, cp);
+ }
+ break;
+ default:
+ i=(*cmdtab[i].fn)(f, cp);
+ return i;
+ }
+ return 1;
+}
+
+
+int
+a_cmd(File *f, Cmd *cp)
+{
+ return append(f, cp, addr.r.p2);
+}
+
+int
+b_cmd(File *f, Cmd *cp)
+{
+ USED(f);
+ f = cp->cmdc=='b'? tofile(cp->ctext) : getfile(cp->ctext);
+ if(f->state == Unread)
+ load(f);
+ else if(nest == 0)
+ filename(f);
+ return TRUE;
+}
+
+int
+c_cmd(File *f, Cmd *cp)
+{
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2;
+ return append(f, cp, addr.r.p2);
+}
+
+int
+d_cmd(File *f, Cmd *cp)
+{
+ USED(cp);
+ Fdelete(f, addr.r.p1, addr.r.p2);
+ f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1;
+ return TRUE;
+}
+
+int
+D_cmd(File *f, Cmd *cp)
+{
+ closefiles(f, cp->ctext);
+ return TRUE;
+}
+
+int
+e_cmd(File *f, Cmd *cp)
+{
+ if(getname(f, cp->ctext, cp->cmdc=='e')==0)
+ error(Enoname);
+ edit(f, cp->cmdc);
+ return TRUE;
+}
+
+int
+f_cmd(File *f, Cmd *cp)
+{
+ getname(f, cp->ctext, TRUE);
+ filename(f);
+ return TRUE;
+}
+
+int
+g_cmd(File *f, Cmd *cp)
+{
+ if(f!=addr.f)panic("g_cmd f!=addr.f");
+ compile(cp->re);
+ if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){
+ f->dot = addr;
+ return cmdexec(f, cp->ccmd);
+ }
+ return TRUE;
+}
+
+int
+i_cmd(File *f, Cmd *cp)
+{
+ return append(f, cp, addr.r.p1);
+}
+
+int
+k_cmd(File *f, Cmd *cp)
+{
+ USED(cp);
+ f->mark = addr.r;
+ return TRUE;
+}
+
+int
+m_cmd(File *f, Cmd *cp)
+{
+ Address addr2;
+
+ addr2 = address(cp->caddr, f->dot, 0);
+ if(cp->cmdc=='m')
+ move(f, addr2);
+ else
+ copy(f, addr2);
+ return TRUE;
+}
+
+int
+n_cmd(File *f, Cmd *cp)
+{
+ int i;
+ USED(f);
+ USED(cp);
+ for(i = 0; i<file.nused; i++){
+ if(file.filepptr[i] == cmd)
+ continue;
+ f = file.filepptr[i];
+ Strduplstr(&genstr, &f->name);
+ filename(f);
+ }
+ return TRUE;
+}
+
+int
+p_cmd(File *f, Cmd *cp)
+{
+ USED(cp);
+ return display(f);
+}
+
+int
+q_cmd(File *f, Cmd *cp)
+{
+ USED(cp);
+ USED(f);
+ trytoquit();
+ if(downloaded){
+ outT0(Hexit);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+s_cmd(File *f, Cmd *cp)
+{
+ int i, j, c, n;
+ Posn p1, op, didsub = 0, delta = 0;
+
+ n = cp->num;
+ op= -1;
+ compile(cp->re);
+ for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){
+ if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */
+ if(sel.p[0].p1==op){
+ p1++;
+ continue;
+ }
+ p1 = sel.p[0].p2+1;
+ }else
+ p1 = sel.p[0].p2;
+ op = sel.p[0].p2;
+ if(--n>0)
+ continue;
+ Strzero(&genstr);
+ for(i = 0; i<cp->ctext->n; i++)
+ if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){
+ c = cp->ctext->s[++i];
+ if('1'<=c && c<='9') {
+ j = c-'0';
+ if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE)
+ error(Elongtag);
+ Fchars(f, genbuf, sel.p[j].p1, sel.p[j…
+ Strinsert(&genstr, tmprstr(genbuf, (se…
+ }else
+ Straddc(&genstr, c);
+ }else if(c!='&')
+ Straddc(&genstr, c);
+ else{
+ if(sel.p[0].p2-sel.p[0].p1>BLOCKSIZE)
+ error(Elongrhs);
+ Fchars(f, genbuf, sel.p[0].p1, sel.p[0].p2);
+ Strinsert(&genstr,
+ tmprstr(genbuf, (int)(sel.p[0].p2-sel.…
+ genstr.n);
+ }
+ if(sel.p[0].p1!=sel.p[0].p2){
+ Fdelete(f, sel.p[0].p1, sel.p[0].p2);
+ delta-=sel.p[0].p2-sel.p[0].p1;
+ }
+ if(genstr.n){
+ Finsert(f, &genstr, sel.p[0].p2);
+ delta+=genstr.n;
+ }
+ didsub = 1;
+ if(!cp->flag)
+ break;
+ }
+ if(!didsub && nest==0)
+ error(Enosub);
+ f->ndot.r.p1 = addr.r.p1, f->ndot.r.p2 = addr.r.p2+delta;
+ return TRUE;
+}
+
+int
+u_cmd(File *f, Cmd *cp)
+{
+ int n;
+ USED(f);
+ USED(cp);
+ n = cp->num;
+ while(n-- && undo())
+ ;
+ return TRUE;
+}
+
+int
+w_cmd(File *f, Cmd *cp)
+{
+ if(getname(f, cp->ctext, FALSE)==0)
+ error(Enoname);
+ writef(f);
+ return TRUE;
+}
+
+int
+x_cmd(File *f, Cmd *cp)
+{
+ if(cp->re)
+ looper(f, cp, cp->cmdc=='x');
+ else
+ linelooper(f, cp);
+ return TRUE;
+}
+
+int
+X_cmd(File *f, Cmd *cp)
+{
+ USED(f);
+ filelooper(cp, cp->cmdc=='X');
+ return TRUE;
+}
+
+int
+plan9_cmd(File *f, Cmd *cp)
+{
+ plan9(f, cp->cmdc, cp->ctext, nest);
+ return TRUE;
+}
+
+int
+eq_cmd(File *f, Cmd *cp)
+{
+ int charsonly;
+
+ switch(cp->ctext->n){
+ case 1:
+ charsonly = FALSE;
+ break;
+ case 2:
+ if(cp->ctext->s[0]=='#'){
+ charsonly = TRUE;
+ break;
+ }
+ default:
+ SET(charsonly);
+ error(Enewline);
+ }
+ printposn(f, charsonly);
+ return TRUE;
+}
+
+int
+nl_cmd(File *f, Cmd *cp)
+{
+ if(cp->addr == 0){
+ /* First put it on newline boundaries */
+ addr = lineaddr((Posn)0, f->dot, -1);
+ addr.r.p2 = lineaddr((Posn)0, f->dot, 1).r.p2;
+ if(addr.r.p1==f->dot.r.p1 && addr.r.p2==f->dot.r.p2)
+ addr = lineaddr((Posn)1, f->dot, 1);
+ display(f);
+ }else if(downloaded)
+ moveto(f, addr.r);
+ else
+ display(f);
+ return TRUE;
+}
+
+int
+cd_cmd(File *f, Cmd *cp)
+{
+ USED(f);
+ cd(cp->ctext);
+ return TRUE;
+}
+
+int
+append(File *f, Cmd *cp, Posn p)
+{
+ if(cp->ctext->n>0 && cp->ctext->s[cp->ctext->n-1]==0)
+ --cp->ctext->n;
+ if(cp->ctext->n>0)
+ Finsert(f, cp->ctext, p);
+ f->ndot.r.p1 = p;
+ f->ndot.r.p2 = p+cp->ctext->n;
+ return TRUE;
+}
+
+int
+display(File *f)
+{
+ Posn p1, p2;
+ int np, n;
+ char *c;
+
+ p1 = addr.r.p1;
+ p2 = addr.r.p2;
+ while(p1 < p2){
+ np = p2-p1;
+ if(np>BLOCKSIZE-1)
+ np = BLOCKSIZE-1;
+ n = Fchars(f, genbuf, p1, p1+np);
+ if(n <= 0)
+ panic("display");
+ genbuf[n] = 0;
+ c = Strtoc(tmprstr(genbuf, n+1));
+ if(downloaded)
+ termwrite(c);
+ else
+ Write(1, c, strlen(c));
+ free(c);
+ p1+=n;
+ }
+ f->dot = addr;
+ return TRUE;
+}
+
+void
+looper(File *f, Cmd *cp, int xy)
+{
+ Posn p, op;
+ Range r;
+
+ r = addr.r;
+ op= xy? -1 : r.p1;
+ nest++;
+ compile(cp->re);
+ for(p = r.p1; p<=r.p2; ){
+ if(!execute(f, p, r.p2)){ /* no match, but y should still run …
+ if(xy || op>r.p2)
+ break;
+ f->dot.r.p1 = op, f->dot.r.p2 = r.p2;
+ p = r.p2+1; /* exit next loop */
+ }else{
+ if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */
+ if(sel.p[0].p1==op){
+ p++;
+ continue;
+ }
+ p = sel.p[0].p2+1;
+ }else
+ p = sel.p[0].p2;
+ if(xy)
+ f->dot.r = sel.p[0];
+ else
+ f->dot.r.p1 = op, f->dot.r.p2 = sel.p[0].p1;
+ }
+ op = sel.p[0].p2;
+ cmdexec(f, cp->ccmd);
+ compile(cp->re);
+ }
+ --nest;
+}
+
+void
+linelooper(File *f, Cmd *cp)
+{
+ Posn p;
+ Range r, linesel;
+ Address a3;
+
+ nest++;
+ r = addr.r;
+ a3.f = f;
+ a3.r.p1 = a3.r.p2 = r.p1;
+ for(p = r.p1; p<r.p2; p = a3.r.p2){
+ a3.r.p1 = a3.r.p2;
+/*pjw if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==…
+ if(p!=r.p1 || ((linesel = lineaddr((Posn)0, a3, 1).r), linesel…
+ linesel = lineaddr((Posn)1, a3, 1).r;
+ if(linesel.p1 >= r.p2)
+ break;
+ if(linesel.p2 >= r.p2)
+ linesel.p2 = r.p2;
+ if(linesel.p2 > linesel.p1)
+ if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){
+ f->dot.r = linesel;
+ cmdexec(f, cp->ccmd);
+ a3.r = linesel;
+ continue;
+ }
+ break;
+ }
+ --nest;
+}
+
+void
+filelooper(Cmd *cp, int XY)
+{
+ File *f, *cur;
+ int i;
+
+ if(Glooping++)
+ error(EnestXY);
+ nest++;
+ settempfile();
+ cur = curfile;
+ for(i = 0; i<tempfile.nused; i++){
+ f = tempfile.filepptr[i];
+ if(f==cmd)
+ continue;
+ if(cp->re==0 || filematch(f, cp->re)==XY)
+ cmdexec(f, cp->ccmd);
+ }
+ if(cur && whichmenu(cur)>=0) /* check that cur is still a file …
+ current(cur);
+ --Glooping;
+ --nest;
+}
diff --git a/samterm/Makefile b/samterm/Makefile
@@ -0,0 +1,49 @@
+# Copyright (c) 1998 Lucent Technologies - All rights reserved.
+#
+# Prototype Makefile for samterm
+#
+# define operating system. ONE of:
+# -DIRIX -DSUNOS -DUMIPS -DSYSVR3 -DAIX -DOSF1
+# -DHPUX -DAPOLLO -DCONVEX -DDYNIX
+#
+# -DIRIX is the default and should actually work on any modern system.
+# Additionally, -D_POSIX_SOURCE (or its equivalent) may be specified
+# if your compiler supports posix-compatible compilation.
+#
+
+include ../config.mk
+
+# If your system has 64-bit addresses, add -DUSE64BITS to $(OS).
+OS=-DIRIX5 -DUSE64BITS=$(USE64BITS)
+
+# add -Iincludedir for any include directories that need to be searched
+# for posix header files (for UMIPS, add -I/usr/include/posix)
+INCS=-I../include -I$(FREETYPEINC)
+
+# SAMTERM contains the name of the file containing the samterm
+# executable.
+SAMTERM=$(BINDIR)/samterm
+
+# set this if your X libraries are in different locations
+# or if you need extra libraries to load with X11 applications
+XLIBS=-lXt -lX11 -lXft
+
+CFLAGS=$(OS) $(INCS) -D_LIBXG_EXTENSION
+
+LIBS=../libframe/libframe.a ../libXg/libXg.a
+CC=cc
+
+OBJ=main.o flayer.o icons.o io.o menu.o mesg.o rasp.o scroll.o unix.o
+
+all: samterm
+
+samterm: $(OBJ) $(LIBS)
+ $(CC) -o samterm $(OBJ) $(LIBS) $(XLIBS)
+
+clean:
+ rm -f *.o core samterm
+
+install: samterm
+ cp samterm $(SAMTERM)
+
+$(OBJ): samterm.h flayer.h ../include/frame.h ../include/libg.h ../incl…
diff --git a/samterm/flayer.c b/samterm/flayer.c
@@ -0,0 +1,436 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+#define DELTA 10
+
+static Flayer **llist; /* front to back */
+static int nllist;
+static int nlalloc;
+static Rectangle lDrect;
+
+extern Bitmap screen;
+extern Mouse mouse;
+
+Vis visibility(Flayer *);
+void newvisibilities(int);
+void llinsert(Flayer*);
+void lldelete(Flayer*);
+
+void
+flstart(Rectangle r)
+{
+ lDrect = r;
+}
+
+void
+flnew(Flayer *l, Rune *(*fn)(Flayer*, long, ulong*), int u0, void *u1)
+{
+ if(nllist == nlalloc){
+ nlalloc += DELTA;
+ llist = realloc(llist, nlalloc*sizeof(Flayer**));
+ if(llist == 0)
+ panic("flnew");
+ }
+ l->textfn = fn;
+ l->user0 = u0;
+ l->user1 = u1;
+ llinsert(l);
+}
+
+Rectangle
+flrect(Flayer *l, Rectangle r)
+{
+ rectclip(&r, lDrect);
+ l->entire = r;
+ l->scroll = inset(r, FLMARGIN);
+ r.min.x =
+ l->scroll.max.x = r.min.x+FLMARGIN+FLSCROLLWID+(FLGAP-FLMARGIN);
+ return r;
+}
+
+void
+flinit(Flayer *l, Rectangle r, XftFont *ft)
+{
+ lldelete(l);
+ llinsert(l);
+ l->visible = All;
+ l->origin = l->p0 = l->p1 = 0;
+ frinit(&l->f, inset(flrect(l, r), FLMARGIN), ft, &screen);
+ newvisibilities(1);
+ bitblt(&screen, l->entire.min, &screen, l->entire, 0);
+ scrdraw(l, 0L);
+ flborder(l, 0);
+}
+
+void
+flclose(Flayer *l)
+{
+ if(l->visible == All)
+ bitblt(&screen, l->entire.min, &screen, l->entire, 0);
+ else if(l->visible == Some){
+ if(l->f.b == 0)
+ l->f.b = balloc(l->entire, screen.ldepth);
+ if(l->f.b){
+ bitblt(l->f.b, l->entire.min, l->f.b, l->entire, 0);
+ flrefresh(l, l->entire, 0);
+ }
+ }
+ frclear(&l->f);
+ lldelete(l);
+ if(l->f.b && l->visible!=All)
+ bfree(l->f.b);
+ l->textfn = 0;
+ newvisibilities(1);
+}
+
+void
+flborder(Flayer *l, int wide)
+{
+ if(flprepare(l)){
+ border(l->f.b, l->entire, FLMARGIN, 0);
+ border(l->f.b, l->entire, wide? FLMARGIN : 1, F&~D);
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+Flayer *
+flwhich(Point p)
+{
+ int i;
+
+ if(p.x==0 && p.y==0)
+ return nllist? llist[0] : 0;
+ for(i=0; i<nllist; i++)
+ if(ptinrect(p, llist[i]->entire))
+ return llist[i];
+ return 0;
+}
+
+void
+flupfront(Flayer *l)
+{
+ int v = l->visible;
+
+ lldelete(l);
+ llinsert(l);
+ if(v!=All)
+ newvisibilities(0);
+}
+
+void
+newvisibilities(int redraw)
+ /* if redraw false, we know it's a flupfront, and needn't
+ * redraw anyone becoming partially covered */
+{
+ int i;
+ Vis ov;
+ Flayer *l;
+
+ for(i = 0; i<nllist; i++){
+ l = llist[i];
+ ov = l->visible;
+ l->visible = visibility(l);
+#define V(a, b) (((a)<<2)|((b)))
+ switch(V(ov, l->visible)){
+ case V(Some, None):
+ if(l->f.b)
+ bfree(l->f.b);
+ case V(All, None):
+ case V(All, Some):
+ l->f.b = 0;
+ frclear(&l->f);
+ break;
+
+ case V(Some, Some):
+ if(l->f.b==0 && redraw)
+ case V(None, Some):
+ flprepare(l);
+ if(l->f.b && redraw){
+ flrefresh(l, l->entire, 0);
+ bfree(l->f.b);
+ l->f.b = 0;
+ frclear(&l->f);
+ }
+ case V(None, None):
+ case V(All, All):
+ break;
+
+ case V(Some, All):
+ if(l->f.b){
+ bitblt(&screen, l->entire.min, l->f.b, l->enti…
+ bfree(l->f.b);
+ l->f.b = &screen;
+ break;
+ }
+ case V(None, All):
+ flprepare(l);
+ break;
+ }
+ if(ov==None && l->visible!=None)
+ flnewlyvisible(l);
+ }
+}
+
+void
+llinsert(Flayer *l)
+{
+ int i;
+ for(i=nllist; i>0; --i)
+ llist[i]=llist[i-1];
+ llist[0]=l;
+ nllist++;
+}
+
+void
+lldelete(Flayer *l)
+{
+ int i;
+
+ for(i=0; i<nllist; i++)
+ if(llist[i]==l){
+ --nllist;
+ for(; i<nllist; i++)
+ llist[i] = llist[i+1];
+ return;
+ }
+ panic("lldelete");
+}
+
+void
+flinsert(Flayer *l, Rune *sp, Rune *ep, long p0)
+{
+ if(flprepare(l)){
+ frinsert(&l->f, sp, ep, p0-l->origin);
+ scrdraw(l, scrtotal(l));
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+void
+fldelete(Flayer *l, long p0, long p1)
+{
+ if(flprepare(l)){
+ p0 -= l->origin;
+ if(p0 < 0)
+ p0 = 0;
+ p1 -= l->origin;
+ if(p1<0)
+ p1 = 0;
+ frdelete(&l->f, p0, p1);
+ scrdraw(l, scrtotal(l));
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+int
+flselect(Flayer *l)
+{
+ int ret = 0;
+ if(l->visible!=All)
+ flupfront(l);
+ frselect(&l->f, &mouse);
+ if(l->f.p0==l->f.p1){
+ if(mouse.msec-l->click<Clicktime && l->f.p0+l->origin==l->p0){
+ ret = 1;
+ l->click = 0;
+ }else
+ l->click = mouse.msec;
+ }else
+ l->click = 0;
+ l->p0 = l->f.p0+l->origin, l->p1 = l->f.p1+l->origin;
+ return ret;
+}
+
+void
+flsetselect(Flayer *l, long p0, long p1)
+{
+ ulong fp0, fp1;
+
+ l->click = 0;
+ if(l->visible==None || !flprepare(l)){
+ l->p0 = p0, l->p1 = p1;
+ return;
+ }
+ l->p0 = p0, l->p1 = p1;
+ flfp0p1(l, &fp0, &fp1);
+ if(fp0==l->f.p0 && fp1==l->f.p1)
+ return;
+ frselectp(&l->f, F&~D);
+ l->f.p0 = fp0, l->f.p1 = fp1;
+ frselectp(&l->f, F&~D);
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+}
+
+void
+flfp0p1(Flayer *l, ulong *pp0, ulong *pp1)
+{
+ long p0 = l->p0-l->origin, p1 = l->p1-l->origin;
+
+ if(p0 < 0)
+ p0 = 0;
+ if(p1 < 0)
+ p1 = 0;
+ if(p0 > l->f.nchars)
+ p0 = l->f.nchars;
+ if(p1 > l->f.nchars)
+ p1 = l->f.nchars;
+ *pp0 = p0;
+ *pp1 = p1;
+}
+
+Rectangle
+rscale(Rectangle r, Point old, Point new)
+{
+ r.min.x = r.min.x*new.x/old.x;
+ r.min.y = r.min.y*new.y/old.y;
+ r.max.x = r.max.x*new.x/old.x;
+ r.max.y = r.max.y*new.y/old.y;
+ return r;
+}
+
+void
+flreshape(Rectangle dr)
+{
+ int i;
+ Flayer *l;
+ Frame *f;
+ Rectangle r, olDrect;
+ int move;
+
+ olDrect = lDrect;
+ lDrect = dr;
+ move = 0;
+ /* no moving on rio; must repaint */
+ bitblt(&screen, lDrect.min, &screen, lDrect, 0);
+ for(i=0; i<nllist; i++){
+ l = llist[i];
+ f = &l->f;
+ if(move)
+ r = raddp(rsubp(l->entire, olDrect.min), dr.min);
+ else{
+ r = raddp(rscale(rsubp(l->entire, olDrect.min),
+ sub(olDrect.max, olDrect.min),
+ sub(dr.max, dr.min)), dr.min);
+ if(l->visible==Some && f->b){
+ bfree(f->b);
+ frclear(f);
+ }
+ f->b = 0;
+ if(l->visible!=None)
+ frclear(f);
+ }
+ if(!rectclip(&r, dr))
+ panic("flreshape");
+ if(r.max.x-r.min.x<100)
+ r.min.x = dr.min.x;
+ if(r.max.x-r.min.x<100)
+ r.max.x = dr.max.x;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.min.y = dr.min.y;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.max.y = dr.max.y;
+ if(!move)
+ l->visible = None;
+ frsetrects(f, inset(flrect(l, r), FLMARGIN), f->b);
+ if(!move && f->b)
+ scrdraw(l, scrtotal(l));
+ }
+ newvisibilities(1);
+}
+
+int
+flprepare(Flayer *l)
+{
+ Frame *f;
+ ulong n;
+ Rune *r;
+
+ if(l->visible == None)
+ return 0;
+ f = &l->f;
+ if(f->b == 0){
+ if(l->visible == All)
+ f->b = &screen;
+ else if((f->b = balloc(l->entire, screen.ldepth))==0)
+ return 0;
+ bitblt(f->b, l->entire.min, f->b, l->entire, 0);
+ border(f->b, l->entire, l==llist[0]? FLMARGIN : 1, F&~D);
+ n = f->nchars;
+ frinit(f, f->entire, f->font, f->b);
+ r = (*l->textfn)(l, n, &n);
+ frinsert(f, r, r+n, (ulong)0);
+ frselectp(f, F&~D);
+ flfp0p1(l, &l->f.p0, &l->f.p1);
+ frselectp(f, F&~D);
+ scrdraw(l, scrtotal(l));
+ }
+ return 1;
+}
+
+static int somevis, someinvis, justvis;
+
+Vis
+visibility(Flayer *l)
+{
+ somevis = someinvis = 0;
+ justvis = 1;
+ flrefresh(l, l->entire, 0);
+ justvis = 0;
+ if(somevis==0)
+ return None;
+ if(someinvis==0)
+ return All;
+ return Some;
+}
+
+void
+flrefresh(Flayer *l, Rectangle r, int i)
+{
+ Flayer *t;
+ Rectangle s;
+
+ Top:
+ if((t=llist[i++]) == l){
+ if(!justvis)
+ bitblt(&screen, r.min, l->f.b, r, S);
+ somevis = 1;
+ }else{
+ if(!rectXrect(t->entire, r))
+ goto Top; /* avoid stacking unnecessarily */
+ if(t->entire.min.x>r.min.x){
+ s = r;
+ s.max.x = t->entire.min.x;
+ flrefresh(l, s, i);
+ r.min.x = t->entire.min.x;
+ }
+ if(t->entire.min.y>r.min.y){
+ s = r;
+ s.max.y = t->entire.min.y;
+ flrefresh(l, s, i);
+ r.min.y = t->entire.min.y;
+ }
+ if(t->entire.max.x<r.max.x){
+ s = r;
+ s.min.x = t->entire.max.x;
+ flrefresh(l, s, i);
+ r.max.x = t->entire.max.x;
+ }
+ if(t->entire.max.y<r.max.y){
+ s = r;
+ s.min.y = t->entire.max.y;
+ flrefresh(l, s, i);
+ r.max.y = t->entire.max.y;
+ }
+ /* remaining piece of r is blocked by t; forget about it */
+ someinvis = 1;
+ }
+}
diff --git a/samterm/flayer.h b/samterm/flayer.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#undef None
+typedef enum Vis{
+ None=0,
+ Some,
+ All
+}Vis;
+
+enum{
+ Clicktime=1000 /* one second */
+};
+
+typedef struct Flayer Flayer;
+
+struct Flayer
+{
+ Frame f;
+ long origin; /* offset of first char in flayer */
+ long p0, p1;
+ long click; /* time at which selection click occ…
+ Rune *(*textfn)(Flayer*, long, ulong*);
+ int user0;
+ void *user1;
+ Rectangle entire;
+ Rectangle scroll;
+ Vis visible;
+};
+
+void flborder(Flayer*, int);
+void flclose(Flayer*);
+void fldelete(Flayer*, long, long);
+void flfp0p1(Flayer*, ulong*, ulong*);
+void flinit(Flayer*, Rectangle, XftFont*);
+void flinsert(Flayer*, Rune*, Rune*, long);
+void flnew(Flayer*, Rune *(*fn)(Flayer*, long, ulong*), int, void*);
+int flprepare(Flayer*);
+Rectangle flrect(Flayer*, Rectangle);
+void flrefresh(Flayer*, Rectangle, int);
+void flreshape(Rectangle);
+int flselect(Flayer*);
+void flsetselect(Flayer*, long, long);
+void flstart(Rectangle);
+void flupfront(Flayer*);
+Flayer *flwhich(Point);
+
+#define FLMARGIN 4
+#define FLSCROLLWID 12
+#define FLGAP 4
diff --git a/samterm/icons.c b/samterm/icons.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+
+Cursor bullseye={
+ {-7, -7},
+ {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+ 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+ 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
+ {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+ 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+ 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
+};
+Cursor deadmouse={
+ {-7, -7},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0x00, 0x8E, 0x1D, 0xC7,
+ 0xFF, 0xE3, 0xFF, 0xF3, 0xFF, 0xFF, 0x7F, 0xFE,
+ 0x3F, 0xF8, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00,},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
+ 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE,
+ 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00,}
+};
+Cursor lockarrow={
+ {-7, -7},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xC0,
+ 0x03, 0xC0, 0x07, 0xC0, 0x0E, 0xC0, 0x1C, 0xC0,
+ 0x38, 0x00, 0x70, 0x00, 0xE0, 0xDB, 0xC0, 0xDB,}
+};
+
+uchar darkgreybits[] = {
+ 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
+ 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
+ 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
+ 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
+};
+
+Bitmap *darkgrey;
+
+void
+iconinit(void)
+{
+ darkgrey = balloc(Rect(0, 0, 16, 16), 0);
+ wrbitmap(darkgrey, 0, 16, darkgreybits);
+}
diff --git a/samterm/io.c b/samterm/io.c
@@ -0,0 +1,204 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+int cursorfd;
+int input;
+int got;
+int block;
+int kbdc;
+int reshaped;
+uchar *hostp;
+uchar *hoststop;
+uchar *externbase;
+uchar *externp;
+uchar *externstop;
+void panic(char*);
+
+void
+initio(void){
+ einit(Emouse|Ekeyboard);
+ estart(Ehost, 0, 0);
+ extstart();
+}
+
+void
+frgetmouse(void)
+{
+ mouse = emouse();
+}
+
+void
+mouseunblock(void)
+{
+ got &= ~Emouse;
+}
+
+void
+kbdblock(void)
+{ /* ca suffit */
+ block = Ekeyboard|Eextern;
+}
+
+int
+button(int but)
+{
+ frgetmouse();
+ return mouse.buttons&(1<<(but-1));
+}
+
+void
+externload(Event *e)
+{
+ externbase = malloc(e->n);
+ if(externbase == 0)
+ return;
+ memmove(externbase, e->data, e->n);
+ externp = externbase;
+ externstop = externbase + e->n;
+ got |= Eextern;
+}
+
+int
+waitforio(void)
+{
+ ulong type;
+ static Event e;
+
+ if(got & ~block)
+ return got & ~block;
+ type = eread(~(got|block), &e);
+ switch(type){
+ case Ehost:
+ hostp = e.data;
+ hoststop = hostp + e.n;
+ block = 0;
+ break;
+ case Eextern:
+ externload(&e);
+ break;
+ case Ekeyboard:
+ kbdc = e.kbdc;
+ break;
+ case Emouse:
+ mouse = e.mouse;
+ break;
+ }
+ got |= type;
+ return got;
+}
+
+int
+rcvchar(void)
+{
+ int c;
+
+ if(!(got & Ehost))
+ return -1;
+ c = *hostp++;
+ if(hostp == hoststop)
+ got &= ~Ehost;
+ return c;
+}
+
+char*
+rcvstring(void)
+{
+ *hoststop = 0;
+ got &= ~Ehost;
+ return (char*)hostp;
+}
+
+int
+getch(void)
+{
+ int c;
+
+ while((c = rcvchar()) == -1){
+ block = ~Ehost;
+ waitforio();
+ block = 0;
+ }
+ return c;
+}
+
+int
+externchar(void)
+{
+ Rune r;
+
+ loop:
+ if(got & (Eextern & ~block)){
+ externp += chartorune(&r, (char*)externp);
+ if(externp >= externstop){
+ got &= ~Eextern;
+ free(externbase);
+ }
+ if(r == 0)
+ goto loop;
+ return r;
+ }
+ return -1;
+}
+
+int
+kbdchar(void)
+{
+ int c;
+ static Event e;
+
+ c = externchar();
+ if(c > 0)
+ return c;
+ if(got & Ekeyboard){
+ c = kbdc;
+ kbdc = -1;
+ got &= ~Ekeyboard;
+ return c;
+ }
+ while(ecanread(Eextern)){
+ eread(Eextern, &e);
+ externload(&e);
+ c = externchar();
+ if(c > 0)
+ return c;
+ }
+ if(!ecankbd())
+ return -1;
+ return ekbd();
+}
+
+int
+qpeekc(void)
+{
+ return kbdc;
+}
+
+void
+ereshaped(Rectangle r)
+{
+ USED(r);
+
+ reshaped = 1;
+}
+
+int
+RESHAPED(void)
+{
+ if(reshaped){
+ screen.r = bscreenrect(&screen.clipr);
+ reshaped = 0;
+ return 1;
+ }
+ return 0;
+}
+
+void
+mouseexit(void)
+{
+ exits(0);
+}
diff --git a/samterm/main.c b/samterm/main.c
@@ -0,0 +1,592 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+Text cmd;
+Rune *scratch;
+long nscralloc;
+Cursor *cursor;
+extern Bitmap screen;
+Mouse mouse;
+Flayer *which = 0;
+Flayer *work = 0;
+long snarflen;
+long typestart = -1;
+long typeend = -1;
+long typeesc = -1;
+long modified = 0; /* strange lookahead for menus */
+char lock = 1;
+char hasunlocked = 0;
+char *machine = "localhost";
+
+void
+main(int argc, char *argv[])
+{
+ int i, got, scr;
+ Text *t;
+ Rectangle r;
+ Flayer *nwhich;
+
+ int fwdbut;
+
+ if (argc >= 3 && strcmp(argv[1], "-r") == 0)
+ {
+ machine = argv[2];
+ }
+
+ getscreen(argc, argv);
+ fwdbut = scrollfwdbut();
+ iconinit();
+ initio();
+ scratch = alloc(100*RUNESIZE);
+ nscralloc = 100;
+ r = screen.r;
+ r.max.y = r.min.y+Dy(r)/5;
+ flstart(screen.clipr);
+ rinit(&cmd.rasp);
+ flnew(&cmd.l[0], stgettext, 1, &cmd);
+ flinit(&cmd.l[0], r, font);
+ cmd.nwin = 1;
+ which = &cmd.l[0];
+ cmd.tag = Untagged;
+ outTs(Tversion, VERSION);
+ startnewfile(Tstartcmdfile, &cmd);
+
+ got = 0;
+ for(;;got = waitforio()){
+ if(hasunlocked && RESHAPED())
+ reshape();
+ if(got&RHost)
+ rcv();
+ if(got&RExtern){
+ for(i=0; cmd.l[i].textfn==0; i++)
+ ;
+ current(&cmd.l[i]);
+ flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
+ type(which, RExtern);
+ }
+ if(got&RKeyboard)
+ if(which)
+ type(which, RKeyboard);
+ else
+ kbdblock();
+ if(got&RMouse){
+ if(lock==2 || !ptinrect(mouse.xy, screen.r)){
+ mouseunblock();
+ continue;
+ }
+ nwhich = flwhich(mouse.xy);
+ scr = which && ptinrect(mouse.xy, which->scroll);
+ if(mouse.buttons)
+ flushtyping(1);
+ if(mouse.buttons&1){
+ if(nwhich){
+ if(nwhich!=which)
+ current(nwhich);
+ else if(scr)
+ scroll(which, 1, fwdbut == 3 ?…
+ else{
+ t=(Text *)which->user1;
+ if(flselect(which)){
+ outTsl(Tdclick, t->tag…
+ t->lock++;
+ }else if(t!=&cmd)
+ outcmd();
+ }
+ }
+ }else if((mouse.buttons&2) && which){
+ if(scr)
+ scroll(which, 2, 2);
+ else
+ menu2hit();
+ }else if((mouse.buttons&4)){
+ if(scr)
+ scroll(which, 3, fwdbut == 3 ? 3 : 1);
+ else
+ menu3hit();
+ }
+ mouseunblock();
+ }
+ }
+}
+
+
+void
+reshape(void){
+ int i;
+
+ flreshape(screen.clipr);
+ for(i = 0; i<nname; i++)
+ if(text[i])
+ hcheck(text[i]->tag);
+}
+
+void
+current(Flayer *nw)
+{
+ Text *t;
+
+ if(which)
+ flborder(which, 0);
+ if(nw){
+ flushtyping(1);
+ flupfront(nw);
+ flborder(nw, 1);
+ buttons(Up);
+ t = (Text *)nw->user1;
+ t->front = nw-&t->l[0];
+ if(t != &cmd)
+ work = nw;
+ }
+ which = nw;
+}
+
+void
+closeup(Flayer *l)
+{
+ Text *t=(Text *)l->user1;
+ int m;
+
+ m = whichmenu(t->tag);
+ if(m < 0)
+ return;
+ flclose(l);
+ if(l == which){
+ which = 0;
+ current(flwhich(Pt(0, 0)));
+ }
+ if(l == work)
+ work = 0;
+ if(--t->nwin == 0){
+ rclear(&t->rasp);
+ free((uchar *)t);
+ text[m] = 0;
+ }else if(l == &t->l[t->front]){
+ for(m=0; m<NL; m++) /* find one; any one will do */
+ if(t->l[m].textfn){
+ t->front = m;
+ return;
+ }
+ panic("close");
+ }
+}
+
+Flayer *
+findl(Text *t)
+{
+ int i;
+ for(i = 0; i<NL; i++)
+ if(t->l[i].textfn==0)
+ return &t->l[i];
+ return 0;
+}
+
+void
+duplicate(Flayer *l, Rectangle r, XftFont *f, int close)
+{
+ Text *t=(Text *)l->user1;
+ Flayer *nl = findl(t);
+ Rune *rp;
+ ulong n;
+
+ if(nl){
+ flnew(nl, stgettext, l->user0, (char *)t);
+ flinit(nl, r, f);
+ nl->origin = l->origin;
+ rp = (*l->textfn)(l, l->f.nchars, &n);
+ flinsert(nl, rp, rp+n, l->origin);
+ flsetselect(nl, l->p0, l->p1);
+ if(close){
+ flclose(l);
+ if(l==which)
+ which = 0;
+ }else
+ t->nwin++;
+ current(nl);
+ hcheck(t->tag);
+ }
+ cursorswitch(cursor);
+}
+
+void
+buttons(int updown)
+{
+ while(((mouse.buttons&7)!=0) != updown)
+ frgetmouse();
+}
+
+int
+getr(Rectangle *rp)
+{
+ Point p;
+ Rectangle r;
+
+ *rp = getrect(3, &mouse);
+ if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
+ p = rp->min;
+ r = cmd.l[cmd.front].entire;
+ *rp = screen.r;
+ if(cmd.nwin==1){
+ if (p.y <= r.min.y)
+ rp->max.y = r.min.y;
+ else if (p.y >= r.max.y)
+ rp->min.y = r.max.y;
+ if (p.x <= r.min.x)
+ rp->max.x = r.min.x;
+ else if (p.x >= r.max.x)
+ rp->min.x = r.max.x;
+ }
+ }
+ return rectclip(rp, screen.r) &&
+ rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
+}
+
+void
+snarf(Text *t, int w)
+{
+ Flayer *l = &t->l[w];
+
+ if(l->p1>l->p0){
+ snarflen = l->p1-l->p0;
+ outTsll(Tsnarf, t->tag, l->p0, l->p1);
+ }
+}
+
+void
+cut(Text *t, int w, int save, int check)
+{
+ long p0, p1;
+ Flayer *l;
+
+ l = &t->l[w];
+ p0 = l->p0;
+ p1 = l->p1;
+ if(p0 == p1)
+ return;
+ if(p0 < 0)
+ panic("cut");
+ if(save)
+ snarf(t, w);
+ outTsll(Tcut, t->tag, p0, p1);
+ flsetselect(l, p0, p0);
+ t->lock++;
+ hcut(t->tag, p0, p1-p0);
+ if(check)
+ hcheck(t->tag);
+}
+
+void
+paste(Text *t, int w)
+{
+ if(snarflen){
+ cut(t, w, 0, 0);
+ t->lock++;
+ outTsl(Tpaste, t->tag, t->l[w].p0);
+ }
+}
+
+void
+scrorigin(Flayer *l, int but, long p0)
+{
+ Text *t=(Text *)l->user1;
+
+ switch(but){
+ case 1:
+ outTsll(Torigin, t->tag, l->origin, p0);
+ break;
+ case 2:
+ outTsll(Torigin, t->tag, p0, 1L);
+ break;
+ case 3:
+ horigin(t->tag,p0);
+ }
+}
+
+int
+alnum(int c)
+{
+ /*
+ * Hard to get absolutely right. Use what we know about ASCII
+ * and assume anything above the Latin control characters is
+ * potentially an alphanumeric.
+ */
+ if(c<=' ')
+ return 0;
+ if(0x7F<=c && c<=0xA0)
+ return 0;
+ if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
+ return 0;
+ return 1;
+}
+
+int
+raspc(Rasp *r, long p)
+{
+ ulong n;
+ rload(r, p, p+1, &n);
+ if(n)
+ return scratch[0];
+ return 0;
+}
+
+long
+ctlw(Rasp *r, long o, long p)
+{
+ int c;
+
+ if(--p < o)
+ return o;
+ if(raspc(r, p)=='\n')
+ return p;
+ for(; p>=o && !alnum(c=raspc(r, p)); --p)
+ if(c=='\n')
+ return p+1;
+ for(; p>o && alnum(raspc(r, p-1)); --p)
+ ;
+ return p>=o? p : o;
+}
+
+long
+ctlu(Rasp *r, long o, long p)
+{
+ for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
+ ;
+ return p>=o? p : o;
+}
+
+int
+center(Flayer *l, long a)
+{
+ Text *t;
+
+ t = l->user1;
+ if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
+ if(a > t->rasp.nrunes)
+ a = t->rasp.nrunes;
+ outTsll(Torigin, t->tag, a, 2L);
+ return 1;
+ }
+ return 0;
+}
+
+int
+onethird(Flayer *l, long a)
+{
+ Text *t;
+ Rectangle s;
+ long lines;
+
+ t = l->user1;
+ if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
+ if(a > t->rasp.nrunes)
+ a = t->rasp.nrunes;
+ s = inset(l->scroll, 1);
+ lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3;
+ if (lines < 2)
+ lines = 2;
+ outTsll(Torigin, t->tag, a, lines);
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+XDisplay(Display *);
+
+extern Display * _dpy;
+
+void
+flushtyping(int clearesc)
+{
+ Text *t;
+ ulong n;
+
+ if(clearesc)
+ typeesc = -1;
+ if(typestart == typeend) {
+ modified = 0;
+ return;
+ }
+ t = which->user1;
+ if(t != &cmd)
+ modified = 1;
+ rload(&t->rasp, typestart, typeend, &n);
+ scratch[n] = 0;
+ if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=…
+ setlock();
+ outcmd();
+ }
+ outTslS(Ttype, t->tag, typestart, scratch);
+ typestart = -1;
+ typeend = -1;
+ XFlush(_dpy);
+}
+
+#define SCROLLKEY 0x80
+#define UPKEY 0x81
+#define ESC 0x1B
+
+void
+type(Flayer *l, int res) /* what a bloody mess this is */
+{
+ Text *t = (Text *)l->user1;
+ Rune buf[100];
+ Rune *p = buf;
+ int c, backspacing;
+ long a;
+ int scrollkey, upkey;
+
+ scrollkey = 0;
+ upkey = 0;
+ if(res == RKeyboard) {
+ scrollkey = qpeekc()==SCROLLKEY; /* ICK */
+ upkey = qpeekc() == UPKEY;
+ }
+
+ if(lock || t->lock){
+ kbdblock();
+ return;
+ }
+ a = l->p0;
+ if(a!=l->p1 && !scrollkey && !upkey){
+ flushtyping(1);
+ cut(t, t->front, 1, 1);
+ return; /* it may now be locked */
+ }
+ backspacing = 0;
+ while((c = kbdchar())>0){
+ if(res == RKeyboard){
+ if(c == UPKEY || c==SCROLLKEY || c==ESC)
+ break;
+ /* backspace, ctrl-u, ctrl-w, del */
+ if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
+ backspacing = 1;
+ break;
+ }
+ }
+ *p++ = c;
+ if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
+ break;
+ }
+ if(p > buf){
+ if(typestart < 0)
+ typestart = a;
+ if(typeesc < 0)
+ typeesc = a;
+ hgrow(t->tag, a, p-buf, 0);
+ t->lock++; /* pretend we Trequest'ed for hdatarune*/
+ hdatarune(t->tag, a, buf, p-buf);
+ a += p-buf;
+ l->p0 = a;
+ l->p1 = a;
+ typeend = a;
+ if(c=='\n' || typeend-typestart>100)
+ flushtyping(0);
+ onethird(l, a);
+ }
+ if(c == SCROLLKEY){
+ flushtyping(0);
+ center(l, l->origin+l->f.nchars+1);
+ } else if (c == UPKEY) {
+ flushtyping(0);
+ outTsll(Torigin, t->tag, l->origin, l->f.maxlines+1);
+ /* backspacing immediately after outcmd(): sorry */
+ }else if(backspacing && !lock){
+ if(l->f.p0>0 && a>0){
+ switch(c){
+ case '\b':
+ case 0x7F: /* del */
+ l->p0 = a-1;
+ break;
+ case 0x15: /* ctrl-u */
+ l->p0 = ctlu(&t->rasp, l->origin, a);
+ break;
+ case 0x17: /* ctrl-w */
+ l->p0 = ctlw(&t->rasp, l->origin, a);
+ break;
+ }
+ l->p1 = a;
+ if(l->p1 != l->p0){
+ /* cut locally if possible */
+ if(typestart<=l->p0 && l->p1<=typeend){
+ t->lock++; /* to call hcut */
+ hcut(t->tag, l->p0, l->p1-l->p0);
+ /* hcheck is local because we know ras…
+ hcheck(t->tag);
+ }else{
+ flushtyping(0);
+ cut(t, t->front, 0, 1);
+ }
+ }
+ if(typeesc >= l->p0)
+ typeesc = l->p0;
+ if(typestart >= 0){
+ if(typestart >= l->p0)
+ typestart = l->p0;
+ typeend = l->p0;
+ if(typestart == typeend){
+ typestart = -1;
+ typeend = -1;
+ modified = 0;
+ }
+ }
+ }
+ }else{
+ if(c==ESC && typeesc>=0){
+ l->p0 = typeesc;
+ l->p1 = a;
+ flushtyping(1);
+ }
+ for(l=t->l; l<&t->l[NL]; l++)
+ if(l->textfn)
+ flsetselect(l, l->p0, l->p1);
+ }
+}
+
+
+void
+outcmd(void){
+ if(work)
+ outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work-…
+}
+
+void
+panic(char *s)
+{
+ fprint(2, "samterm:panic: ");
+ perror(s);
+ abort();
+}
+
+Rune*
+stgettext(Flayer *l, long n, ulong *np)
+{
+ Text *t;
+
+ t = l->user1;
+ rload(&t->rasp, l->origin, l->origin+n, np);
+ return scratch;
+}
+
+long
+scrtotal(Flayer *l)
+{
+ return ((Text *)l->user1)->rasp.nrunes;
+}
+
+void*
+alloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == 0)
+ panic("alloc");
+ memset(p, 0, n);
+ return p;
+}
diff --git a/samterm/menu.c b/samterm/menu.c
@@ -0,0 +1,380 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+uchar *name[MAXFILES]; /* first byte is ' ' or '\'': modified st…
+Text *text[MAXFILES]; /* pointer to Text associated with file */
+ushort tag[MAXFILES]; /* text[i].tag, even if text[i] no…
+int nname;
+int mw;
+
+char *genmenu3(int);
+char *genmenu2(int);
+char *genmenu2c(int);
+
+enum Menu2
+{
+ Cut,
+ Paste,
+ Snarf,
+ Look,
+ Exch,
+ Search,
+ NMENU2 = Search,
+ Send = Search,
+ NMENU2C
+};
+
+enum Menu3
+{
+ New,
+ Zerox,
+ Reshape,
+ Close,
+ Write,
+ NMENU3
+};
+
+char *menu2str[] = {
+ "cut",
+ "paste",
+ "snarf",
+ "look",
+ "<exch>",
+ 0, /* storage for last pattern */
+};
+
+char *menu3str[] = {
+ "new",
+ "zerox",
+ "reshape",
+ "close",
+ "write",
+};
+
+Menu menu2 = {0, genmenu2};
+Menu menu2c ={0, genmenu2c};
+Menu menu3 = {0, genmenu3};
+
+void
+menu2hit(void)
+{
+ Text *t=(Text *)which->user1;
+ int w = which-t->l;
+ int m;
+
+ m = menuhit(2, &mouse, t==&cmd? &menu2c : &menu2);
+ if(lock || t->lock)
+ return;
+
+ switch(m){
+ case Cut:
+ cut(t, w, 1, 1);
+ break;
+
+ case Paste:
+ paste(t, w);
+ break;
+
+ case Snarf:
+ snarf(t, w);
+ break;
+
+ case Exch:
+ snarf(t, w);
+ outT0(Tstartsnarf);
+ setlock();
+ break;
+
+ case Look:
+ outTsll(Tlook, t->tag, which->p0, which->p1);
+ setlock();
+ break;
+
+ case Search:
+ outcmd();
+ if(t==&cmd)
+ outTsll(Tsend, 0 /*ignored*/, which->p0, which->p1);
+ else
+ outT0(Tsearch);
+ setlock();
+ break;
+ }
+}
+
+void
+menu3hit(void)
+{
+ Rectangle r;
+ Flayer *l;
+ int m, i;
+ Text *t;
+
+ mw = -1;
+ switch(m = menuhit(3, &mouse, &menu3)){
+ case -1:
+ break;
+
+ case New:
+ if(!lock)
+ sweeptext(1, 0);
+ break;
+
+ case Zerox:
+ case Reshape:
+ if(!lock){
+ cursorswitch(&bullseye);
+ buttons(Down);
+ if((mouse.buttons&4) && (l = flwhich(mouse.xy)) && get…
+ duplicate(l, r, l->f.font, m==Reshape);
+ else
+ cursorswitch(cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Close:
+ if(!lock){
+ cursorswitch(&bullseye);
+ buttons(Down);
+ if((mouse.buttons&4) && (l = flwhich(mouse.xy)) && !lo…
+ t=(Text *)l->user1;
+ if (t->nwin>1)
+ closeup(l);
+ else if(t!=&cmd) {
+ outTs(Tclose, t->tag);
+ setlock();
+ }
+ }
+ cursorswitch(cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Write:
+ if(!lock){
+ cursorswitch(&bullseye);
+ buttons(Down);
+ if((mouse.buttons&4) && (l = flwhich(mouse.xy))){
+ outTs(Twrite, ((Text *)l->user1)->tag);
+ setlock();
+ }else
+ cursorswitch(cursor);
+ buttons(Up);
+ }
+ break;
+
+ default:
+ if(t = text[m-NMENU3]){
+ i = t->front;
+ if(t->nwin==0 || t->l[i].textfn==0)
+ return; /* not ready yet; try again lat…
+ if(t->nwin>1 && which==&t->l[i])
+ do
+ if(++i==NL)
+ i = 0;
+ while(i!=t->front && t->l[i].textfn==0);
+ current(&t->l[i]);
+ }else if(!lock)
+ sweeptext(0, tag[m-NMENU3]);
+ break;
+ }
+}
+
+
+Text *
+sweeptext(int new, int tag)
+{
+ Rectangle r;
+ Text *t;
+
+ if(getr(&r) && (t = malloc(sizeof(Text)))){
+ memset((void*)t, 0, sizeof(Text));
+ current((Flayer *)0);
+ flnew(&t->l[0], stgettext, 0, (char *)t);
+ flinit(&t->l[0], r, font); /*bnl*/
+ t->nwin = 1;
+ rinit(&t->rasp);
+ if(new)
+ startnewfile(Tstartnewfile, t);
+ else{
+ rinit(&t->rasp);
+ t->tag = tag;
+ startfile(t);
+ }
+ return t;
+ }
+ return 0;
+}
+
+int
+whichmenu(int tg)
+{
+ int i;
+
+ for(i=0; i<nname; i++)
+ if(tag[i] == tg)
+ return i;
+ return -1;
+}
+
+void
+menuins(int n, uchar *s, Text *t, int m, int tg)
+{
+ int i;
+
+ if(nname == MAXFILES)
+ panic("menuins");
+ for(i=nname; i>n; --i)
+ name[i]=name[i-1], text[i]=text[i-1], tag[i]=tag[i-1];
+ text[n] = t;
+ tag[n] = tg;
+ name[n] = alloc(strlen((char*)s)+2);
+ name[n][0] = m;
+ strcpy((char*)name[n]+1, (char*)s);
+ nname++;
+ menu3.lasthit = n+NMENU3;
+}
+
+void
+menudel(int n)
+{
+ int i;
+
+ if(nname==0 || n>=nname || text[n])
+ panic("menudel");
+ free(name[n]);
+ --nname;
+ for(i = n; i<nname; i++)
+ name[i]=name[i+1], text[i]=text[i+1], tag[i]=tag[i+1];
+}
+
+void
+setpat(char *s)
+{
+ static char pat[17];
+
+ pat[0] = '/';
+ strncpy(pat+1, s, 15);
+ menu2str[Search] = pat;
+}
+
+#define NBUF 64
+static uchar buf[NBUF*UTFmax]={' ', ' ', ' ', ' '};
+
+char *
+paren(char *s)
+{
+ uchar *t = buf;
+
+ *t++ = '(';
+ do; while(*t++ = *s++);
+ t[-1] = ')';
+ *t = 0;
+ return (char *)buf;
+}
+char*
+genmenu2(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n>=NMENU2+(menu2str[Search]!=0))
+ return 0;
+ p = menu2str[n];
+ if(!lock && !t->lock || n==Search || n==Look)
+ return p;
+ return paren(p);
+}
+char*
+genmenu2c(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n >= NMENU2C)
+ return 0;
+ if(n == Send)
+ p="send";
+ else
+ p = menu2str[n];
+ if(!lock && !t->lock)
+ return p;
+ return paren(p);
+}
+char *
+genmenu3(int n)
+{
+ Text *t;
+ int c, i, k, l, w;
+ Rune r;
+ char *p;
+
+ if(n >= NMENU3+nname)
+ return 0;
+ if(n < NMENU3){
+ p = menu3str[n];
+ if(lock)
+ p = paren(p);
+ return p;
+ }
+ n -= NMENU3;
+ if(n == 0) /* unless we've been fooled, this is cmd */
+ return (char *)&name[n][1];
+ if(mw == -1){
+ mw = 7; /* strlen("~~sam~~"); */
+ for(i=1; i<nname; i++){
+ w = utflen((char*)name[i]+1)+4; /* include "'+.…
+ if(w > mw)
+ mw = w;
+ }
+ }
+ if(mw > NBUF)
+ mw = NBUF;
+ t = text[n];
+ buf[0] = name[n][0];
+ buf[1] = '-';
+ buf[2] = ' ';
+ buf[3] = ' ';
+ if(t){
+ if(t->nwin == 1)
+ buf[1] = '+';
+ else if(t->nwin > 1)
+ buf[1] = '*';
+ if(work && t==(Text *)work->user1) {
+ buf[2]= '.';
+ if(modified)
+ buf[0] = '\'';
+ }
+ }
+ l = utflen((char*)name[n]+1);
+ if(l > NBUF-4-2){
+ i = 4;
+ k = 1;
+ while(i < NBUF/2){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ c = name[n][k];
+ name[n][k] = 0;
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ name[n][k] = c;
+ strcat((char*)buf, "...");
+ while((l-i) >= NBUF/2-4){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ strcat((char*)buf, (char*)name[n]+k);
+ }else
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ i = utflen((char*)buf);
+ k = strlen((char*)buf);
+ while(i<mw && k<sizeof buf-1){
+ buf[k++] = ' ';
+ i++;
+ }
+ buf[k] = 0;
+ return (char *)buf;
+}
diff --git a/samterm/mesg.c b/samterm/mesg.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <string.h>
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+extern char *exname;
+
+#define HSIZE 3 /* Type + short count */
+Header h;
+uchar indata[DATASIZE+1]; /* room for NUL */
+uchar outdata[DATASIZE];
+short outcount;
+int hversion;
+
+void inmesg(Hmesg, int);
+int inshort(int);
+long inlong(int);
+long invlong(int);
+void hsetdot(int, long, long);
+void hmoveto(int, long);
+void hsetsnarf(int);
+void clrlock(void);
+int snarfswap(char*, int, char**);
+
+void
+rcv(void)
+{
+ int c;
+ static state = 0;
+ static count = 0;
+ static i = 0;
+ static int errs = 0;
+
+ while((c=rcvchar()) != -1)
+ switch(state){
+ case 0:
+ h.type = c;
+ state++;
+ break;
+
+ case 1:
+ h.count0 = c;
+ state++;
+ break;
+
+ case 2:
+ h.count1 = c;
+ count = h.count0|(h.count1<<8);
+ i = 0;
+ if(count > DATASIZE){
+ if(++errs < 5){
+ dumperrmsg(count, h.type, h.count0, c);
+ state = 0;
+ continue;
+ }
+ fprint(2, "type %d count %d\n", h.type, count);
+ panic("count>DATASIZE");
+ }
+ if(count == 0)
+ goto zerocount;
+ state++;
+ break;
+
+ case 3:
+ indata[i++] = c;
+ if(i == count){
+ zerocount:
+ indata[i] = 0;
+ inmesg(h.type, count);
+ state = count = 0;
+ continue;
+ }
+ break;
+ }
+}
+
+Text *
+whichtext(int tg)
+{
+ int i;
+
+ for(i=0; i<nname; i++)
+ if(tag[i] == tg)
+ return text[i];
+ panic("whichtext");
+ return 0;
+}
+
+void
+inmesg(Hmesg type, int count)
+{
+ Text *t;
+ int i, m;
+ long l;
+ Flayer *lp;
+ char syscmd[512];
+
+ m = inshort(0);
+ l = inlong(2);
+ switch(type){
+ case -1:
+ panic("rcv error");
+ default:
+ fprint(2, "type %d\n", type);
+ panic("rcv unknown");
+
+ case Hversion:
+ hversion = m;
+ break;
+
+ case Hbindname:
+ l = invlong(2); /* for 64-bit pointers */
+ if((i=whichmenu(m)) < 0)
+ break;
+ /* in case of a race, a bindname may already have occurred */
+ if((t=whichtext(m)) == 0)
+ t=(Text *)l;
+ else /* let the old one win; clean up the new one */
+ while(((Text *)l)->nwin>0)
+ closeup(&((Text *)l)->l[((Text *)l)->front]);
+ text[i] = t;
+ text[i]->tag = m;
+ break;
+
+ case Hcurrent:
+ if(whichmenu(m)<0)
+ break;
+ t = whichtext(m);
+ i = which && ((Text *)which->user1)==&cmd && m!=cmd.tag;
+ if(t==0 && (t = sweeptext(0, m))==0)
+ break;
+ if(t->l[t->front].textfn==0)
+ panic("Hcurrent");
+ lp = &t->l[t->front];
+ if(i){
+ flupfront(lp);
+ flborder(lp, 0);
+ work = lp;
+ }else
+ current(lp);
+ break;
+
+ case Hmovname:
+ if((m=whichmenu(m)) < 0)
+ break;
+ t = text[m];
+ l = tag[m];
+ i = name[m][0];
+ text[m] = 0; /* suppress panic in menudel */
+ menudel(m);
+ if(t == &cmd)
+ m = 0;
+ else{
+ if (nname>0 && text[0]==&cmd)
+ m = 1;
+ else m = 0;
+ for(; m<nname; m++)
+ if(strcmp((char*)indata+2, (char*)name[m]+1)<0)
+ break;
+ }
+ menuins(m, indata+2, t, i, (int)l);
+ break;
+
+ case Hgrow:
+ if(whichmenu(m) >= 0)
+ hgrow(m, l, inlong(6), 1);
+ break;
+
+ case Hnewname:
+ menuins(0, (uchar *)"", (Text *)0, ' ', m);
+ break;
+
+ case Hcheck0:
+ i = whichmenu(m);
+ if(i>=0) {
+ t = text[i];
+ if (t)
+ t->lock++;
+ outTs(Tcheck, m);
+ }
+ break;
+
+ case Hcheck:
+ i = whichmenu(m);
+ if(i>=0) {
+ t = text[i];
+ if (t && t->lock)
+ t->lock--;
+ hcheck(m);
+ }
+ break;
+
+ case Hunlock:
+ clrlock();
+ break;
+
+ case Hdata:
+ if(whichmenu(m) >= 0)
+ l += hdata(m, l, indata+6, count-6);
+ Checkscroll:
+ if(m == cmd.tag){
+ for(i=0; i<NL; i++){
+ lp = &cmd.l[i];
+ if(lp->textfn)
+ center(lp, l>=0? l : lp->p1);
+ }
+ }
+ break;
+
+ case Horigin:
+ if(whichmenu(m) >= 0)
+ horigin(m, l);
+ break;
+
+ case Hunlockfile:
+ if(whichmenu(m)>=0 && (t = whichtext(m))->lock){
+ --t->lock;
+ l = -1;
+ goto Checkscroll;
+ }
+ break;
+
+ case Hsetdot:
+ if(whichmenu(m) >= 0)
+ hsetdot(m, l, inlong(6));
+ break;
+
+ case Hgrowdata:
+ if(whichmenu(m)<0)
+ break;
+ hgrow(m, l, inlong(6), 0);
+ whichtext(m)->lock++; /* fake the request */
+ l += hdata(m, l, indata+10, count-10);
+ goto Checkscroll;
+
+ case Hmoveto:
+ if(whichmenu(m)>=0)
+ hmoveto(m, l);
+ break;
+
+ case Hclean:
+ if((m = whichmenu(m)) >= 0)
+ name[m][0] = ' ';
+ break;
+
+ case Hdirty:
+ if((m = whichmenu(m))>=0)
+ name[m][0] = '\'';
+ break;
+
+ case Hdelname:
+ if((m=whichmenu(m)) >= 0)
+ menudel(m);
+ break;
+
+ case Hcut:
+ if(whichmenu(m) >= 0)
+ hcut(m, l, inlong(6));
+ break;
+
+ case Hclose:
+ if(whichmenu(m)<0 || (t = whichtext(m))==0)
+ break;
+ l = t->nwin;
+ for(i = 0,lp = t->l; l>0 && i<NL; i++,lp++)
+ if(lp->textfn){
+ closeup(lp);
+ --l;
+ }
+ break;
+
+ case Hsetpat:
+ setpat((char *)indata);
+ break;
+
+ case Hsetsnarf:
+ hsetsnarf(m);
+ break;
+
+ case Hsnarflen:
+ snarflen = inlong(0);
+ break;
+
+ case Hack:
+ outT0(Tack);
+ break;
+
+#ifndef NOFIFO
+ case Hextcmd:
+ if (exname != NULL)
+ {
+ int fifofd = open(exname, O_WRONLY);
+ if (fifofd >= 0)
+ {
+ memset(syscmd, 0, 512);
+ snprintf(syscmd, 511, "%s", (char *)indata);
+ write(fifofd, syscmd, 511);
+ close(fifofd);
+ }
+ }
+ break;
+#endif
+
+ case Hexit:
+ outT0(Texit);
+ mouseexit();
+ break;
+ }
+}
+
+void
+setlock(void)
+{
+ lock++;
+ cursorswitch(cursor = &lockarrow);
+}
+
+void
+clrlock(void)
+{
+ hasunlocked = 1;
+ if(lock > 0)
+ lock--;
+ if(lock == 0)
+ cursorswitch(cursor=(Cursor *)0);
+}
+
+void
+startfile(Text *t)
+{
+ outTsv(Tstartfile, t->tag, t); /* for 64-bit pointers */
+ setlock();
+}
+
+void
+startnewfile(int type, Text *t)
+{
+ t->tag = Untagged;
+ outTv(type, t); /* for 64-bit pointers …
+}
+
+int
+inshort(int n)
+{
+ return indata[n]|(indata[n+1]<<8);
+}
+
+long
+inlong(int n)
+{
+ return indata[n]|(indata[n+1]<<8)|
+ ((long)indata[n+2]<<16)|((long)indata[n+3]<<24);
+}
+
+long
+invlong(int n)
+{
+ long l;
+
+ l = (indata[n+7]<<24) | (indata[n+6]<<16) | (indata[n+5]<<8) | indata[…
+ l = (l<<16) | (indata[n+3]<<8) | indata[n+2];
+ l = (l<<16) | (indata[n+1]<<8) | indata[n];
+ return l;
+}
+
+void
+outT0(Tmesg type)
+{
+ outstart(type);
+ outsend();
+}
+
+void
+outTl(Tmesg type, long l)
+{
+ outstart(type);
+ outlong(l);
+ outsend();
+}
+
+void
+outTs(Tmesg type, int s)
+{
+ outstart(type);
+ outshort(s);
+ outsend();
+}
+
+void
+outTss(Tmesg type, int s1, int s2)
+{
+ outstart(type);
+ outshort(s1);
+ outshort(s2);
+ outsend();
+}
+
+void
+outTsll(Tmesg type, int s1, long l1, long l2)
+{
+ outstart(type);
+ outshort(s1);
+ outlong(l1);
+ outlong(l2);
+ outsend();
+}
+
+void
+outTsl(Tmesg type, int s1, long l1)
+{
+ outstart(type);
+ outshort(s1);
+ outlong(l1);
+ outsend();
+}
+
+void
+outTsv(Tmesg type, int s1, void *l1)
+{
+ outstart(type);
+ outshort(s1);
+ outvlong(l1);
+ outsend();
+}
+
+void
+outTv(Tmesg type, void *l1)
+{
+ outstart(type);
+ outvlong(l1);
+ outsend();
+}
+
+void
+outTslS(Tmesg type, int s1, long l1, Rune *s)
+{
+ char buf[DATASIZE*3+1];
+ char *c;
+
+ outstart(type);
+ outshort(s1);
+ outlong(l1);
+ c = buf;
+ while(*s)
+ c += runetochar(c, s++);
+ *c++ = 0;
+ outcopy(c-buf, (uchar *)buf);
+ outsend();
+}
+
+void
+outTsls(Tmesg type, int s1, long l1, int s2)
+{
+ outstart(type);
+ outshort(s1);
+ outlong(l1);
+ outshort(s2);
+ outsend();
+}
+
+void
+outstart(Tmesg type)
+{
+ outdata[0] = type;
+ outcount = 0;
+}
+
+void
+outcopy(int count, uchar *data)
+{
+ while(count--)
+ outdata[HSIZE+outcount++] = *data++;
+}
+
+void
+outshort(int s)
+{
+ uchar buf[2];
+
+ buf[0]=s;
+ buf[1]=s>>8;
+ outcopy(2, buf);
+}
+
+void
+outlong(long l)
+{
+ uchar buf[4];
+
+ buf[0]=l;
+ buf[1]=l>>8;
+ buf[2]=l>>16;
+ buf[3]=l>>24;
+ outcopy(4, buf);
+}
+
+void
+outvlong(void *v)
+{
+ int i;
+ ulong l;
+ uchar buf[8];
+
+ l = (ulong) v;
+ for(i = 0; i < sizeof(buf); i++, l >>= 8)
+ buf[i] = l;
+
+ outcopy(8, buf);
+}
+
+void
+outsend(void)
+{
+ if(outcount>DATASIZE-HSIZE)
+ panic("outcount>sizeof outdata");
+ outdata[1]=outcount;
+ outdata[2]=outcount>>8;
+ if(write(1, (char *)outdata, outcount+HSIZE)!=outcount+HSIZE)
+ exits("write error");
+}
+
+
+void
+hsetdot(int m, long p0, long p1)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+
+ flushtyping(1);
+ flsetselect(l, p0, p1);
+}
+
+void
+horigin(int m, long p0)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+ long a;
+ ulong n;
+ Rune *r;
+
+ if(!flprepare(l)){
+ l->origin = p0;
+ return;
+ }
+ a = p0-l->origin;
+ if(a>=0 && a<l->f.nchars)
+ frdelete(&l->f, 0, a);
+ else if(a<0 && -a<l->f.nchars){
+ r = rload(&t->rasp, p0, l->origin, &n);
+ frinsert(&l->f, r, r+n, 0);
+ }else
+ frdelete(&l->f, 0, l->f.nchars);
+ l->origin = p0;
+ scrdraw(l, t->rasp.nrunes);
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ hcheck(m);
+}
+
+void
+hmoveto(int m, long p0)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+
+ if(p0<l->origin || p0-l->origin>l->f.nchars*9/10)
+ outTsll(Torigin, m, p0, 2L);
+}
+
+void
+hcheck(int m)
+{
+ Flayer *l;
+ Text *t;
+ int reqd = 0, i;
+ long n, nl, a;
+ Rune *r;
+
+ if(m == Untagged)
+ return;
+ t = whichtext(m);
+ if(t == 0) /* possible in a half-built window */
+ return;
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn==0 || !flprepare(l)) /* BUG: don't
+ need this if BUG be…
+ is fixed */
+ continue;
+ a = t->l[i].origin;
+ n = rcontig(&t->rasp, a, a+l->f.nchars, 1);
+ if(n<l->f.nchars) /* text missing in middle of screen */
+ a+=n;
+ else{ /* text missing at end of screen?…
+ Again:
+ if(l->f.lastlinefull)
+ goto Checksel; /* all's well */
+ a = t->l[i].origin+l->f.nchars;
+ n = t->rasp.nrunes-a;
+ if(n==0)
+ goto Checksel;
+ if(n>TBLOCKSIZE)
+ n = TBLOCKSIZE;
+ n = rcontig(&t->rasp, a, a+n, 1);
+ if(n>0){
+ rload(&t->rasp, a, a+n, 0);
+ nl = l->f.nchars;
+ r = scratch;
+ flinsert(l, r, r+n, l->origin+nl);
+ if(nl == l->f.nchars) /* made no progre…
+ goto Checksel;
+ goto Again;
+ }
+ }
+ if(!reqd){
+ n = rcontig(&t->rasp, a, a+TBLOCKSIZE, 0);
+ if(n <= 0)
+ panic("hcheck request==0");
+ outTsls(Trequest, m, a, (int)n);
+ outTs(Tcheck, m);
+ t->lock++; /* for the Trequest */
+ t->lock++; /* for the Tcheck */
+ reqd++;
+ }
+ Checksel:
+ flsetselect(l, l->p0, l->p1);
+ }
+}
+
+void
+flnewlyvisible(Flayer *l)
+{
+ hcheck(((Text *)l->user1)->tag);
+}
+
+void
+hsetsnarf(int nc)
+{
+ char *s2;
+ char *s1;
+ int i;
+ int n;
+
+ cursorswitch(&deadmouse);
+ s2 = alloc(nc+1);
+ for(i=0; i<nc; i++)
+ s2[i] = getch();
+ s2[nc] = 0;
+ n = snarfswap(s2, nc, &s1);
+ if(n >= 0){
+ if(!s1)
+ n = 0;
+ if(n > SNARFSIZE-1)
+ n = SNARFSIZE-1;
+ s1 = realloc(s1, n+1);
+ if (!s1)
+ exits("malloc");
+ s1[n] = 0;
+ snarflen = n;
+ outTs(Tsetsnarf, n);
+ if(n>0 && write(1, s1, n)!=n)
+ exits("write error");
+ free(s1);
+ }else
+ outTs(Tsetsnarf, 0);
+ free(s2);
+ cursorswitch(cursor);
+}
+
+void
+hgrow(int m, long a, long new, int req)
+{
+ int i;
+ Flayer *l;
+ Text *t = whichtext(m);
+ long o, b;
+
+ if(new <= 0)
+ panic("hgrow");
+ rresize(&t->rasp, a, 0L, new);
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn == 0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ if(a < o)
+ l->origin+=new;
+ if(a < l->p0)
+ l->p0+=new;
+ if(a < l->p1)
+ l->p1+=new;
+ /* must prevent b temporarily becoming unsigned */
+ if(!req || a<o || (b>0 && b>l->f.nchars) ||
+ (l->f.nchars==0 && a-o>0))
+ continue;
+ if(new>TBLOCKSIZE)
+ new = TBLOCKSIZE;
+ outTsls(Trequest, m, a, (int)new);
+ t->lock++;
+ req = 0;
+ }
+}
+
+int
+hdata1(Text *t, long a, Rune *r, int len)
+{
+ int i;
+ Flayer *l;
+ long o, b;
+
+ for(l = &t->l[0], i=0; i<NL; i++, l++){
+ if(l->textfn==0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ /* must prevent b temporarily becoming unsigned */
+ if(a<o || (b>0 && b>l->f.nchars))
+ continue;
+ flinsert(l, r, r+len, o+b);
+ }
+ rdata(&t->rasp, a, a+len, r);
+ rclean(&t->rasp);
+ return len;
+}
+
+int
+hdata(int m, long a, uchar *s, int len)
+{
+ int i, w;
+ Text *t = whichtext(m);
+ Rune buf[DATASIZE], *r;
+
+ if(t->lock)
+ --t->lock;
+ if(len == 0)
+ return 0;
+ r = buf;
+ for(i=0; i<len; i+=w,s+=w)
+ w = chartorune(r++, (char*)s);
+ return hdata1(t, a, buf, r-buf);
+}
+
+int
+hdatarune(int m, long a, Rune *r, int len)
+{
+ Text *t = whichtext(m);
+
+ if(t->lock)
+ --t->lock;
+ if(len == 0)
+ return 0;
+ return hdata1(t, a, r, len);
+}
+
+void
+hcut(int m, long a, long old)
+{
+ Flayer *l;
+ Text *t = whichtext(m);
+ int i;
+ long o, b;
+
+ if(t->lock)
+ --t->lock;
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn == 0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ /* must prevent b temporarily becoming unsigned */
+ if((b<0 || b<l->f.nchars) && a+old>=o){
+ fldelete(l, b<0? o : o+b,
+ a+old-rmissing(&t->rasp, o, a+old));
+ }
+ if(a+old<o)
+ l->origin-=old;
+ else if(a<=o)
+ l->origin = a;
+ if(a+old<l->p0)
+ l->p0-=old;
+ else if(a<=l->p0)
+ l->p0 = a;
+ if(a+old<l->p1)
+ l->p1-=old;
+ else if(a<=l->p1)
+ l->p1 = a;
+ }
+ rresize(&t->rasp, a, old, 0L);
+ rclean(&t->rasp);
+}
diff --git a/samterm/plan9.c b/samterm/plan9.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+static char exname[64];
+
+void
+getscreen(int argc, char **argv)
+{
+ USED(argc);
+ USED(argv);
+ binit1(panic, 0, "sam", 1);
+ bitblt(&screen, screen.clipr.min, &screen, screen.clipr, 0);
+}
+
+int
+screensize(int *w, int *h)
+{
+ int fd, n;
+ char buf[5*12+1];
+
+ fd = open("/dev/screen", OREAD);
+ if(fd < 0)
+ return 0;
+ n = read(fd, buf, sizeof(buf)-1);
+ close(fd);
+ if (n != sizeof(buf)-1)
+ return 0;
+ buf[n] = 0;
+ if (h) {
+ *h = atoi(buf+4*12)-atoi(buf+2*12);
+ if (*h < 0)
+ return 0;
+ }
+ if (w) {
+ *w = atoi(buf+3*12)-atoi(buf+1*12);
+ if (*w < 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+snarfswap(char *fromsam, int nc, char **tosam)
+{
+ char *s1;
+ int f, n;
+
+ f = open("/dev/snarf", 0);
+ if(f < 0)
+ return -1;
+ *tosam = s1 = alloc(SNARFSIZE);
+ n = read(f, s1, SNARFSIZE-1);
+ close(f);
+ if(n < 0)
+ n = 0;
+ if (n == 0) {
+ *tosam = 0;
+ free(s1);
+ } else
+ s1[n] = 0;
+ f = create("/dev/snarf", 1, 0666);
+ if(f >= 0){
+ write(f, fromsam, nc);
+ close(f);
+ }
+ return n;
+}
+
+void
+dumperrmsg(int count, int type, int count0, int c)
+{
+ fprint(2, "samterm: host mesg: count %d %x %x %x %s...ignored\n",
+ count, type, count0, c, rcvstring());
+}
+
+void
+removeextern(void)
+{
+ remove(exname);
+}
+
+void
+extstart(void)
+{
+ char buf[32];
+ int fd, p[2];
+
+ if(pipe(p) < 0)
+ return;
+ sprint(exname, "/srv/sam.%s", getuser());
+ fd = create(exname, 1, 0600);
+ if(fd < 0){ /* assume existing guy is more important */
+ Err:
+ close(p[0]);
+ close(p[1]);
+ return;
+ }
+ sprint(buf, "%d", p[0]);
+ if(write(fd, buf, strlen(buf)) <= 0)
+ goto Err;
+ close(fd);
+ /*
+ * leave p[0] open so if the file is removed the event
+ * library won't get an error
+ */
+ estart(Eextern, p[1], 8192);
+ atexit(removeextern);
+}
diff --git a/samterm/rasp.c b/samterm/rasp.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+void
+rinit(Rasp *r)
+{
+ r->nrunes=0;
+ r->sect=0;
+}
+
+void
+rclear(Rasp *r)
+{
+ Section *s, *ns;
+
+ for(s=r->sect; s; s=ns){
+ ns = s->next;
+ free(s->text);
+ free(s);
+ }
+ r->sect = 0;
+}
+
+Section*
+rsinsert(Rasp *r, Section *s) /* insert before s */
+{
+ Section *t;
+ Section *u;
+
+ t = alloc(sizeof(Section));
+ if(r->sect == s){ /* includes empty list case: r->sect==s==0 */
+ r->sect = t;
+ t->next = s;
+ }else{
+ u = r->sect;
+ if(u == 0)
+ panic("rsinsert 1");
+ do{
+ if(u->next == s){
+ t->next = s;
+ u->next = t;
+ goto Return;
+ }
+ u=u->next;
+ }while(u);
+ panic("rsinsert 2");
+ }
+ Return:
+ return t;
+}
+
+void
+rsdelete(Rasp *r, Section *s)
+{
+ Section *t;
+
+ if(s == 0)
+ panic("rsdelete");
+ if(r->sect == s){
+ r->sect = s->next;
+ goto Free;
+ }
+ for(t=r->sect; t; t=t->next)
+ if(t->next == s){
+ t->next = s->next;
+ Free:
+ if(s->text)
+ free(s->text);
+ free(s);
+ return;
+ }
+ panic("rsdelete 2");
+}
+
+void
+splitsect(Rasp *r, Section *s, long n0)
+{
+ if(s == 0)
+ panic("splitsect");
+ rsinsert(r, s->next);
+ if(s->text == 0)
+ s->next->text = 0;
+ else{
+ s->next->text = alloc(RUNESIZE*(TBLOCKSIZE+1));
+ Strcpy(s->next->text, s->text+n0);
+ s->text[n0] = 0;
+ }
+ s->next->nrunes = s->nrunes-n0;
+ s->nrunes = n0;
+}
+
+Section *
+findsect(Rasp *r, Section *s, long p, long q) /* find sect containing q…
+{
+ if(s==0 && p!=q)
+ panic("findsect");
+ for(; s && p+s->nrunes<=q; s=s->next)
+ p += s->nrunes;
+ if(p != q){
+ splitsect(r, s, q-p);
+ s = s->next;
+ }
+ return s;
+}
+
+void
+rresize(Rasp *r, long a, long old, long new)
+{
+ Section *s, *t, *ns;
+
+ s = findsect(r, r->sect, 0L, a);
+ t = findsect(r, s, a, a+old);
+ for(; s!=t; s=ns){
+ ns=s->next;
+ rsdelete(r, s);
+ }
+ /* now insert the new piece before t */
+ if(new > 0){
+ ns=rsinsert(r, t);
+ ns->nrunes=new;
+ ns->text=0;
+ }
+ r->nrunes += new-old;
+}
+
+void
+rdata(Rasp *r, long p0, long p1, Rune *cp)
+{
+ Section *s, *t, *ns;
+
+ s = findsect(r, r->sect, 0L, p0);
+ t = findsect(r, s, p0, p1);
+ for(; s!=t; s=ns){
+ ns=s->next;
+ if(s->text)
+ panic("rdata");
+ rsdelete(r, s);
+ }
+ p1 -= p0;
+ s = rsinsert(r, t);
+ s->text = alloc(RUNESIZE*(TBLOCKSIZE+1));
+ memmove(s->text, cp, RUNESIZE*p1);
+ s->text[p1] = 0;
+ s->nrunes = p1;
+}
+
+void
+rclean(Rasp *r)
+{
+ Section *s;
+
+ for(s=r->sect; s; s=s->next)
+ while(s->next && (s->text!=0)==(s->next->text!=0)){
+ if(s->text){
+ if(s->nrunes+s->next->nrunes>TBLOCKSIZE)
+ break;
+ Strcpy(s->text+s->nrunes, s->next->text);
+ }
+ s->nrunes += s->next->nrunes;
+ rsdelete(r, s->next);
+ }
+}
+
+void
+Strcpy(Rune *to, Rune *from)
+{
+ do; while(*to++ = *from++);
+}
+
+Rune*
+rload(Rasp *r, ulong p0, ulong p1, ulong *nrp)
+{
+ Section *s;
+ long p;
+ int n, nb;
+
+ nb = 0;
+ Strgrow(&scratch, &nscralloc, p1-p0+1);
+ scratch[0] = 0;
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s){
+ /*
+ * Subtle and important. If we are preparing to handle an 'rd…
+ * call, it's because we have an 'rresize' hole here, so the
+ * screen doesn't have data for that space anyway (it got cut
+ * first). So pretend it isn't there.
+ */
+ if(s->text){
+ n = s->nrunes-(p0-p);
+ if(n>p1-p0) /* all in this section */
+ n = p1-p0;
+ memmove(scratch+nb, s->text+(p0-p), n*RUNESIZE);
+ nb += n;
+ scratch[nb] = 0;
+ }
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ if(nrp)
+ *nrp = nb;
+ return scratch;
+}
+
+int
+rmissing(Rasp *r, ulong p0, ulong p1)
+{
+ Section *s;
+ long p;
+ int n, nm=0;
+
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s){
+ if(s->text == 0){
+ n = s->nrunes-(p0-p);
+ if(n > p1-p0) /* all in this section */
+ n = p1-p0;
+ nm += n;
+ }
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ return nm;
+}
+
+int
+rcontig(Rasp *r, ulong p0, ulong p1, int text)
+{
+ Section *s;
+ long p, n;
+ int np=0;
+
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s && (text? (s->text!=0) : (s->text==0))){
+ n = s->nrunes-(p0-p);
+ if(n > p1-p0) /* all in this section */
+ n = p1-p0;
+ np += n;
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ return np;
+}
+
+void
+Strgrow(Rune **s, long *n, int want) /* can always toss the old data wh…
+{
+ if(*n >= want)
+ return;
+ free(*s);
+ *s = alloc(RUNESIZE*want);
+ *n = want;
+}
diff --git a/samterm/samterm.h b/samterm/samterm.h
@@ -0,0 +1,158 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#define SAMTERM
+
+#define RUNESIZE sizeof(Rune)
+#define MAXFILES 256
+#define NL 5
+
+enum{
+ Up,
+ Down
+};
+
+typedef struct Text Text;
+typedef struct Section Section;
+typedef struct Rasp Rasp;
+
+struct Section
+{
+ long nrunes;
+ Rune *text; /* if null, we haven't got it */
+ Section *next;
+};
+
+struct Rasp
+{
+ long nrunes;
+ Section *sect;
+};
+
+#define Untagged ((ushort)65535)
+
+struct Text
+{
+ Rasp rasp;
+ short nwin;
+ short front; /* input window */
+ ushort tag;
+ char lock;
+ Flayer l[NL]; /* screen storage */
+};
+
+enum Resource
+{
+ Eextern = 0x08,
+ Ehost = 0x04,
+ RHost = Ehost,
+ RExtern = Eextern,
+ RKeyboard = Ekeyboard,
+ RMouse = Emouse
+};
+
+extern Text *text[];
+extern uchar *name[];
+extern ushort tag[];
+extern int nname;
+extern Cursor bullseye;
+extern Cursor deadmouse;
+extern Cursor lockarrow;
+extern Cursor *cursor;
+extern Flayer *which;
+extern Flayer *work;
+extern Text cmd;
+extern Rune *scratch;
+extern long nscralloc;
+extern char lock;
+extern char hasunlocked;
+extern long snarflen;
+extern Mouse mouse;
+extern long modified;
+
+Rune *stgettext(Flayer*, long, ulong*);
+void *alloc(ulong n);
+
+void iconinit(void);
+void getscreen(int, char**);
+void initio(void);
+void setlock(void);
+void outcmd(void);
+void rinit(Rasp*);
+void startnewfile(int, Text*);
+void cursorset(Point);
+void getmouse(void);
+void mouseunblock(void);
+void kbdblock(void);
+void extstart(void);
+int button(int but);
+int load(char*, int);
+int waitforio(void);
+int rcvchar(void);
+int getch(void);
+int kbdchar(void);
+int qpeekc(void);
+void mouseexit(void);
+void cut(Text*, int, int, int);
+void paste(Text*, int);
+void snarf(Text*, int);
+int center(Flayer*, long);
+int xmenuhit(int, Menu*);
+void buttons(int);
+int getr(Rectangle*);
+void current(Flayer*);
+void duplicate(Flayer*, Rectangle, XftFont*, int);
+void startfile(Text*);
+void panic(char*);
+void closeup(Flayer*);
+void Strgrow(Rune**, long*, int);
+int RESHAPED(void);
+void reshape(void);
+void rcv(void);
+void type(Flayer*, int);
+void menu2hit(void);
+void menu3hit(void);
+void scroll(Flayer*, int, int);
+void hcheck(int);
+void rclear(Rasp*);
+int whichmenu(int);
+void hcut(int, long, long);
+void horigin(int, long);
+void hgrow(int, long, long, int);
+int hdata(int, long, uchar*, int);
+int hdatarune(int, long, Rune*, int);
+Rune *rload(Rasp*, ulong, ulong, ulong*);
+void menuins(int, uchar*, Text*, int, int);
+void menudel(int);
+Text *sweeptext(int, int);
+void setpat(char*);
+void scrdraw(Flayer*, long tot);
+int rcontig(Rasp*, ulong, ulong, int);
+int rmissing(Rasp*, ulong, ulong);
+void rresize(Rasp *, long, long, long);
+void rdata(Rasp*, long, long, Rune*);
+void rclean(Rasp*);
+void scrorigin(Flayer*, int, long);
+long scrtotal(Flayer*);
+void flnewlyvisible(Flayer*);
+char *rcvstring(void);
+void Strcpy(Rune*, Rune*);
+void Strncpy(Rune*, Rune*, long);
+void flushtyping(int);
+void dumperrmsg(int, int, int, int);
+int screensize(int*,int*);
+
+#include "../sam/mesg.h"
+
+void outTs(Tmesg, int);
+void outT0(Tmesg);
+void outTl(Tmesg, long);
+void outTslS(Tmesg, int, long, Rune*);
+void outTsll(Tmesg, int, long, long);
+void outTsl(Tmesg, int, long);
+void outTsv(Tmesg, int, void*);
+void outTv(Tmesg, void*);
+void outstart(Tmesg);
+void outcopy(int, uchar*);
+void outshort(int);
+void outlong(long);
+void outvlong(void*);
+void outsend(void);
diff --git a/samterm/scroll.c b/samterm/scroll.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+extern Bitmap *darkgrey;
+extern Mouse mouse;
+
+Rectangle
+scrpos(Rectangle r, long p0, long p1, long tot)
+{
+ long h;
+ Rectangle q;
+
+ q = inset(r, 1);
+ h = q.max.y-q.min.y;
+ if(tot == 0)
+ return q;
+ if(tot > 1024L*1024L)
+ tot>>=10, p0>>=10, p1>>=10;
+ if(p0 > 0)
+ q.min.y += h*p0/tot;
+ if(p1 < tot)
+ q.max.y -= h*(tot-p1)/tot;
+ if(q.max.y < q.min.y+2){
+ if(q.min.y+2 <= r.max.y)
+ q.max.y = q.min.y+2;
+ else
+ q.min.y = q.max.y-2;
+ }
+ return q;
+}
+
+void
+scrflip(Flayer *l, Rectangle r)
+{
+ if(rectclip(&r, l->scroll))
+ bitblt(l->f.b, r.min, l->f.b, r, F&~D);
+}
+
+void
+scrdraw(Flayer *l, long tot)
+{
+ Rectangle r, r1, r2;
+ Bitmap *b;
+ static Bitmap *x;
+ int h;
+
+ if(l->f.b == 0)
+ panic("scrdraw");
+ r = l->scroll;
+ r.min.x += 1; /* border between margin and bar */
+ r1 = r;
+ if(l->visible == All){
+ if(x == 0){
+ if (screensize(0, &h) == 0)
+ h = 2048;
+ x = balloc(Rect(0, 0, 32, h), l->f.b->ldepth);
+ if(x == 0)
+ panic("scrdraw balloc");
+ }
+ b = x;
+ r1.min.x = 0;
+ r1.max.x = Dx(r);
+ }else
+ b = l->f.b;
+ bitblt(b, r1.min, b, r1, F);
+ texture(b, inset(r1, 1), darkgrey, S);
+ r2 = scrpos(r1, l->origin, l->origin+l->f.nchars, tot);
+ bitblt(b, r2.min, b, r2, 0);
+ if(b!=l->f.b)
+ bitblt(l->f.b, r.min, b, r1, S);
+}
+
+void
+scroll(Flayer *l, int pbut, int but)
+{
+ int in = 0, oin;
+ long tot = scrtotal(l);
+ Rectangle scr, r, s, rt;
+ int x, y, my, oy, h;
+ long p0;
+
+ s = inset(l->scroll, 1);
+ x = s.min.x+FLSCROLLWID/2;
+ scr = scrpos(l->scroll, l->origin, l->origin+l->f.nchars, tot);
+ r = scr;
+ y = scr.min.y;
+ my = mouse.xy.y;
+ do{
+ oin = in;
+ in = abs(x-mouse.xy.x)<=FLSCROLLWID/2;
+ if(oin != in)
+ scrflip(l, r);
+ if(in){
+ oy = y;
+ my = mouse.xy.y;
+ if(my < s.min.y)
+ my = s.min.y;
+ if(my >= s.max.y)
+ my = s.max.y;
+ if(!eqpt(mouse.xy, Pt(x, my)))
+ cursorset(Pt(x, my));
+ if(but == 1){
+ p0 = l->origin-frcharofpt(&l->f, Pt(s.max.x, m…
+ rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot…
+ y = rt.min.y;
+ }else if(but == 2){
+ y = my;
+ if(y > s.max.y-2)
+ y = s.max.y-2;
+ }else if(but == 3){
+ p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, m…
+ rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot…
+ y = rt.min.y;
+ }
+ if(y != oy){
+ scrflip(l, r);
+ r = raddp(scr, Pt(0, y-scr.min.y));
+ scrflip(l, r);
+ }
+ }
+ }while(button(pbut));
+ if(in){
+ h = s.max.y-s.min.y;
+ scrflip(l, r);
+ p0 = 0;
+ if(but == 1)
+ p0 = (long)(my-s.min.y)/l->f.font->height+1;
+ else if(but == 2){
+ if(tot > 1024L*1024L)
+ p0 = ((tot>>10)*(y-s.min.y)/h)<<10;
+ else
+ p0 = tot*(y-s.min.y)/h;
+ }else if(but == 3){
+ p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my));
+ if(p0 > tot)
+ p0 = tot;
+ }
+ scrorigin(l, but, p0);
+ }
+}
diff --git a/samterm/unix.c b/samterm/unix.c
@@ -0,0 +1,159 @@
+/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
+#include <u.h>
+#include <libc.h>
+#include <libg.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef APOLLO
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#if defined(UMIPS) || defined(SUNOS)
+#define atexit(p) /* sigh */
+#endif
+
+char *exname = NULL;
+static char *fallbacks[] = {
+ "*scrollForwardR: true",
+ "*geometry: 740x780",
+ NULL
+};
+
+void
+getscreen(int argc, char **argv)
+{
+ int fd;
+ Rectangle r;
+
+ signal(SIGINT, SIG_IGN);
+ xtbinit(0, "Sam", &argc, argv, fallbacks);
+ r = inset(screen.r, 4);
+ bitblt(&screen, r.min, &screen, r, 0);
+}
+
+int
+screensize(int *w, int *h)
+{
+ return scrpix(w,h);
+}
+
+void
+dumperrmsg(int count, int type, int count0, int c)
+{
+ uchar *cp;
+ int i;
+
+ cp = (uchar *) rcvstring();
+ fprint(2, "samterm: host mesg: count %d %ux %ux %ux %s...ignored\n",
+ count, type, count0, c, cp);
+ i = 0;
+ while (*cp) {
+ fprint(2, "%x ", *cp);
+ if (i++ >= 20) {
+ fprint(2, "\n");
+ i = 0;
+ }
+ cp++;
+ }
+}
+
+void
+removeextern(void)
+{
+ if (exname) {
+ (void)unlink(exname);
+ exname = 0;
+ }
+}
+/*
+ * some systems do not support non-blocking i/o on named pipes
+ * or do not provide working POSIX interfaces to the pipes.
+ * in that case, add the name of the system to the 'ifdef' that
+ * disables the code at the beginning of the function.
+ * The external 'B' command will not work.
+ */
+
+void
+extstart(void)
+{
+#ifndef NOFIFO
+ extern char *machine;
+ char *disp;
+ char *user;
+ char *home;
+ int fd;
+ int flags;
+
+ user = getuser();
+ disp = getenv("DISPLAY");
+ home = getenv("HOME");
+
+ if (home == NULL)
+ {
+ return;
+ }
+
+ exname = (char *)alloc(4 + 6 + strlen(home) + 1 + strlen(user) + 1 + s…
+ sprint(exname, "%s/.sam.%s", home, machine);
+
+ /* Make the named pipe. */
+ if (mkfifo(exname, 0600) == -1) {
+ struct stat statb;
+ extern int errno;
+
+ if (errno != EEXIST || stat(exname, &statb) == -1)
+ return;
+
+ if (!S_ISFIFO(statb.st_mode)) {
+ removeextern();
+ if (mkfifo(exname, 0600) == -1)
+ return;
+ }
+ }
+
+ fd = open(exname, O_RDONLY | O_NONBLOCK);
+ if (fd == -1) {
+ removeextern();
+ return;
+ }
+
+ /*
+ * Turn off no-delay and provide ourselves as a lingering
+ * writer so as not to get end of file on read.
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1 || fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1
+ || open(exname, O_WRONLY) == -1) {
+ (void)close(fd);
+ removeextern();
+ return;
+ }
+
+ estart(Eextern, fd, 8192);
+ atexit(removeextern);
+#endif
+}
+
+/*
+ * we have to supply a dummy exit function, because some vendors can't …
+ * bothered to provide atexit(). we clean up the named pipes on a norm…
+ * exit, but leave them laying around on abnormal exits.
+ */
+void
+exits(char *message)
+{
+ if (exname) {
+ unlink(exname);
+ exname = 0;
+ }
+ if (message == 0)
+ exit (0);
+ else
+ exit(1);
+}
You are viewing proxied material from vernunftzentrum.de. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.