Initial git import. - sam - An updated version of the sam text editor.
(HTM) git clone git://vernunftzentrum.de/sam.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
(DIR) commit f8d68c3f2e8aa987821894c9b82aa63bb411e714
(HTM) Author: Rob King <jking@deadpixi.com>
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(-)
---
(DIR) 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 ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
(DIR) 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
+
(DIR) 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 ANY
+ * 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 window manager.
+
+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, jking@deadpixi.com
(DIR) diff --git a/config.mk b/config.mk
@@ -0,0 +1,23 @@
+# config.mk - makefile configuration for sam
+# copyright 2015 Rob King <jking@deadpixi.com>
+
+# 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
(DIR) 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
+
(DIR) diff --git a/doc/Makefile b/doc/Makefile
@@ -0,0 +1,18 @@
+# Copyright 2014-2014 Rob King <jking@deadpixi.com>
+
+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"
+
(DIR) 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
(DIR) 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 which it was read, and a
+.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 the current file \[en] that file to which editing commands apply \[en] whereupon its menu entry is printed.
+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, or cause commands to be sent to an instance of
+.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 of
+.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 indicated by a dash, as in
+.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 encountered.
+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 catenation operations reversed in the expression.
+.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 undefined.
+.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 end
+.Pq Li /
+or beginning
+.Pq Li ?
+of the file, and if necessary continuing the search from the other end to the starting point of the search.
+The matched substring may straddle the starting point.
+When entering a pattern containing a literal question mark for a backward search, the question mark should be specified as a member of a class.
+.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 regular expression.
+.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 printable ASCII character except alphanumerics.
+Any number of trailing delimiters may be elided, with multiple elisions then representing null strings, but the first delimiter must always be present.
+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 operation.
+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, etc.
+.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 the list are examined.
+.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 is used.
+.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, of the shell command.
+.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 dot set to the match.
+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 at the end of the range.
+.It Sy "y/regexp/ command"
+Like
+.Sy x ","
+but run the command for each substring that lies before, between, or after the matches that would be generated by
+.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 current file and run the command.
+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 required.
+.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 any other file whose most recent change was simultaneous with the current file's change.
+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 printed.
+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 command are not visible to other commands
+.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 insertion composed of the several insertions in the order applied.
+.Ss The terminal
+What follows refers to the behavior of
+.Nm
+when downloaded, that is, when operating as a display editor on a bitmap display.
+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 windows on the same file.
+Each window has an independent value of dot, indicated by a highlighted substring on the display.
+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 or more cursors to prompt for selection of a window or sweeping of a rectangle.
+.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 corner.
+.Dq Sweeping
+a null rectangle gets a large window disjoint from the command window or the whole
+.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 file current, unless it is already current, in which case selections cycle through the open windows.
+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 commands for the editor rather than passive text, and text printed by editor commands appears in it.
+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 file in the most recently current window.
+.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 current window, button 1 selects text, thus setting dot.
+Double-clicking selects text to the boundaries of words, lines, quoted strings, or bracketed strings, depending on the text at the click.
+.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 the editor's internal snarf buffer
+.Pq "currently 4K"
+without warning.
+.It Sy "/regexp"
+Search forward for the next match of the last regular expression typed in a command.
+.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 were typed to the command window.
+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 external file.
+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 is recommended.
+.Ss Remote execution
+.Nm sam
+allows the host and terminal parts of the editor to run on diffrent machines, in a process called
+.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 the remote shell command specified in the
+.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 system will
+.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 instance.
+.Pp
+.Nm B
+may also be called on a remote machine, causing the downloaded instance of sam connected to that machine to load the named files.
+.Ss Unicode Text Input
+.Nm sam
+allows the input of arbitrary Unicode characters from the Basic Multilingual Plane
+.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 naming the codepoint.
+.Pp
+Commonly used codepoints can be entered with an abbreviated two-character sequence.
+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 spaces, and a hexadecimal number.
+Holding down the compose key and typing the two listed characters will insert the codepoint indicated by the number.
+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 insert Unicode code point 0x00BD
+.Pq \[u00BD]
+into the file.
+.Pp
+After the hexadecimal codepoint specification, the rest of the line is ignored and is therefore usable as a comment field.
+.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 Codepoint Keys Codepoint
+!! \[u00A1] c$ \[u00A2] l$ \[u00A3] g$ \[u00A4]
+y$ \[u00A5] || \[u00A6] SS \[u00A7] "" \[u00A8]
+cO \[u00A9] sa \[u00AA] << \[u00AB] no \[u00AC]
+-- \[u00AD] rO \[u00AE] __ \[u00AF] de \[u00B0]
++- \[u00B1] s2 \[u00B2] s3 \[u00B3] '' \[u00B4]
+mi \[u00B5] pg \[u00B6] .. \[u00B7] ,, \[u00B8]
+s1 \[u00B9] so \[u00BA] >> \[u00BB] 14 \[u00BC]
+12 \[u00BD] 34 \[u00BE] ?? \[u00BF] `A \[u00C0]
+'A \[u00C1] ^A \[u00C2] ~A \[u00C3] "A \[u00C4]
+oA \[u00C5] AE \[u00C6] ,C \[u00C7] `E \[u00C8]
+'E \[u00C9] ^E \[u00CA] "E \[u00CB] `I \[u00CC]
+'I \[u00CD] ^I \[u00CE] "I \[u00CF] D- \[u00D0]
+~N \[u00D1] `O \[u00D2] 'O \[u00D3] ^O \[u00D4]
+~O \[u00D5] "O \[u00D6] mu \[u00D7] /O \[u00D8]
+`U \[u00D9] 'U \[u00DA] ^U \[u00DB] "U \[u00DC]
+'Y \[u00DD] |P \[u00DE] ss \[u00DF] `a \[u00E0]
+'a \[u00E1] ^a \[u00E2] ~a \[u00E3] "a \[u00E4]
+oa \[u00E5] ae \[u00E6] ,c \[u00E7] `e \[u00E8]
+'e \[u00E9] ^e \[u00EA] "e \[u00EB] `i \[u00EC]
+'i \[u00ED] ^i \[u00EE] "i \[u00EF] d- \[u00F0]
+~n \[u00F1] `o \[u00F2] 'o \[u00F3] ^o \[u00F4]
+~o \[u00F5] "o \[u00F6] -: \[u00F7] /o \[u00F8]
+`u \[u00F9] 'u \[u00FA] ^u \[u00FB] "u \[u00FC]
+'y \[u00FD] |p \[u00FE] "y \[u00FF] wk \[u2654]
+.TE
+.TS
+box;
+c | c | c | c | c | c | c | c
+- | - | - | - | - | - | - | -
+c | c | c | c | c | c | c | c.
+Keys Codepoint Keys Codepoint Keys Codepoint Keys Codepoint
+wq \[u2655] wr \[u2656] wb \[u2657] wn \[u2658]
+wp \[u2659] bk \[u265A] bq \[u265B] br \[u265C]
+bb \[u265D] bn \[u265E] bp \[u265F] *a \[u03B1]
+*b \[u03B2] *g \[u03B3] *d \[u03B4] *e \[u03B5]
+*z \[u03B6] *y \[u03B7] *h \[u03B8] *i \[u03B9]
+*k \[u03BA] *l \[u03BB] *m \[u03BC] *n \[u03BD]
+*c \[u03BE] *o \[u03BF] *p \[u03C0] *r \[u03C1]
+ts \[u03C2] *s \[u03C3] *t \[u03C4] *u \[u03C5]
+*f \[u03C6] *x \[u03C7] *q \[u03C8] *w \[u03C9]
+*A \[u0391] *B \[u0392] *G \[u0393] *D \[u0394]
+*E \[u0395] *Z \[u0396] *Y \[u0397] *H \[u0398]
+*I \[u0399] *K \[u039A] *L \[u039B] *M \[u039C]
+*N \[u039D] *C \[u039E] *O \[u039F] *P \[u03A0]
+*R \[u03A1] *S \[u03A3] *T \[u03A4] *U \[u03A5]
+*F \[u03A6] *X \[u03A7] *Q \[u03A8] *W \[u03A9]
+<- \[u2190] ua \[u2191] -> \[u2192] da \[u2193]
+ab \[u2194] V= \[u21D0] =V \[u21D2] fa \[u2200]
+te \[u2203] pd \[u2202] es \[u2205] De \[u2206]
+gr \[u2207] mo \[u2208] !m \[u2209] st \[u220D]
+** \[u2217] bu \[u2219] sr \[u221A] pt \[u221D]
+if \[u221E] an \[u2220] l& \[u2227] l| \[u2228]
+ca \[u2229] cu \[u222A] is \[u222B] tf \[u2234]
+~= \[u2243] cg \[u2245] ~~ \[u2248] != \[u2260]
+== \[u2261] <= \[u2266] >= \[u2267] sb \[u2282]
+sp \[u2283] !b \[u2284] ib \[u2286] ip \[u2287]
+O+ \[u2295] O- \[u2296] Ox \[u2297] tu \[u22A2]
+Tu \[u22A8] lz \[u22C4] el \[u22EF] :( \[u2639]
+:) \[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 color, respectively.
+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 to and the name of the remote command to execute
+.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 edited at the time of termination.
+.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 rendering glitches.
+.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 any other complex text rendering.
+\ No newline at end of file
(DIR) diff --git a/doc/sam.1.pdf b/doc/sam.1.pdf
Binary files differ.
(DIR) 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
+ } 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 widthshow} repeat}
+ {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 1666(is an interactive multi-file)3 1053 4 1291 2446 t
+( the mouse-driven, cut-and-paste interface to make complex)7 2450(mand language supplements)2 1150 2 1080 2566 t
+( language is characterized by the composi-)6 1719( The)1 208( editing tasks easy to specify.)5 1186(or repetitive)1 487 4 1080 2686 t
+( treat-)1 237( The)1 207( regular expressions to describe the structure of the text being modified.)11 2889(tion of)1 267 4 1080 2806 t
+(ment of files as a database, with changes logged as atomic transactions, guides the imple-)14 3600 1 1080 2926 t
+(mentation and makes a general `undo' mechanism straightforward.)7 2672 1 1080 3046 t
+10 CW f
+(Sam)1330 3202 w
+10 R f
+( a low-bandwidth stream, one)4 1224(is implemented as two processes connected by)6 1912 2 1544 3202 t
+( it can run)3 435( Therefore)1 453( the other the editing algorithms.)5 1360(process handling the display and)4 1352 4 1080 3322 t
+( with both pro-)3 600(with the display process in a bitmap terminal and the editor on a local host,)14 3000 2 1080 3442 t
+( process in the terminal and the edi-)7 1442(cesses on a bitmap-equipped host, or with the display)8 2158 2 1080 3562 t
+( can even run without a bitmap)6 1243( suppressing the display process, it)5 1388( By)1 167(tor in a remote host.)4 802 4 1080 3682 t
+(terminal.)1080 3802 w
+( 17, number)2 502(This paper is reprinted from Software\320Practice and Experience, Vol)8 2848 2 1330 3958 t
+(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 1494 4318 t
+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 3025(is an interactive text editor)4 1085 2 930 4594 t
+( is written as two programs: one, the `host)8 1775( It)1 123( composition of regular expressions.)4 1499(language based on the)3 923 4 720 4714 t
+( the command language and provides file access; the other,)9 2428(part,' runs on a Unix* system and implements)7 1892 2 720 4834 t
+( bitmap display and supports the)5 1337(the `terminal part,' runs asynchronously on a machine with a mouse and)11 2983 2 720 4954 t
+( host part may be even run in isolation on an ordinary terminal to edit)14 2859( The)1 211(display and interactive editing.)3 1250 3 720 5074 t
+( command language, much like a traditional line editor, without assistance from a mouse or)14 3763(text using the)2 557 2 720 5194 t
+( runs on a Blit)4 583( often, the terminal part)4 952(display. Most)1 573 3 720 5314 t
+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 ordinary 9600 bps RS232 link; on the)16 3897 2 720 5434 t
+(puter the host and display processes run on a single machine, connected by a pipe.)14 3283 1 720 5554 t
+10 CW f
+(Sam)970 5710 w
+10 R f
+( unlike)1 282( has no facilities for multiple fonts, graphics or tables,)9 2210( It)1 117(edits uninterpreted ASCII text.)3 1250 4 1181 5710 t
+(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 unlike them, it)3 816 4 2221 5830 t
+(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 2 2308 5950 t
+(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)13 2977 1 2063 6070 t
+(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 interactive)3 967 2 1973 6190 t
+( The)1 214( language driven by regular expressions.)5 1658(mouse-driven interface to a program with a solid command)8 2448 3 720 6310 t
+( language, and acquired a notation for describing the)8 2186(command language developed more than the mouse)6 2134 2 720 6430 t
+( a dataflow-like syntax for specifying)5 1594(structure of files more richly than as a sequence of lines, using)11 2726 2 720 6550 t
+(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)11 3251(The first part of this)4 819 2 970 1116 t
+( first of the host part, then of the)8 1290( is followed by a description of the implementation,)8 2076( That)1 234(how they interact.)2 720 4 720 1236 t
+( influenced the design of)4 991( principle that)2 555( A)1 122(terminal part.)1 538 4 720 1356 t
+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 efficient.)7 1841( A)1 122( length.)1 300(upper limits on file size or line)6 1233 6 720 1476 t
+( breaking them into)3 783(goals together requires a method for efficiently manipulating huge strings \(files\) without)11 3537 2 720 1596 t
+( control of the command language.)5 1407(lines, perhaps while making thousands of changes under)7 2280 2 720 1716 t
+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 changes as atomic updates.)9 2783(is to treat the file)4 701 4 720 1836 t
+( is achieved through a collection of caches that minimizes)9 2328( Efficiency)1 467( easily to `undo' changes.)4 1031(be unwound)1 494 4 720 1956 t
+(disc traffic and data motion, both within the two parts of the program and between them.)15 3542 1 720 2076 t
+(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 straightforward.)2 975 3 2006 2232 t
+( data structure that)3 746( is achieved through a)4 888( This)1 231(tor stay synchronized when either half may initiate a change.)9 2455 4 720 2352 t
+(organizes the communications and is maintained in parallel by both halves.)10 3005 1 720 2472 t
+(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)13 3164(The paper is long, but)4 906 2 970 2904 t
+(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 its implementation.)5 1576 3 1609 3024 t
+(tion is strongly influenced by the user interface, and vice versa.)10 2528 1 720 3144 t
+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 editor for multiple files.)6 1291 3 925 3540 t
+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 unneeded ones.)5 1349(and there are commands to add new)6 1465 4 720 3890 t
+( file is read; the)4 673( operations apply to an internal copy made when the)9 2214( Editing)1 359(complete some command.)2 1074 4 720 4010 t
+( simplify the discussion, the)4 1124( To)1 162( is changed only by an explicit command.)7 1672(Unix file associated with the copy)5 1362 4 720 4130 t
+(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 the)13 3446(is usually)1 400 2 1194 4406 t
+( special window, called the)4 1116( this mode, the command language is still available: text typed in a)12 2776(mouse. In)1 428 3 720 4526 t
+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 current file.)9 1961(is interpreted)1 529 4 1297 4646 t
+(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 commands.)3 1332 4 2439 4766 t
+(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 1341 2 2506 4886 t
+( even on an ordinary terminal, interactively or)7 1849(but still permits editing using the textual command language,)8 2471 2 720 5006 t
+(from a script.)2 535 1 720 5126 t
+(The following sections describe first the command language \(under)8 2783 1 970 5282 t
+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)1 293( the mouse interface.)3 846(dow\), and then)2 608 4 720 5402 t
+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 contents, which are an)8 1733 2 720 5918 t
+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 the contents match)5 1527 2 1755 6038 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 322(\(see Figures 1)2 578 4 2325 6158 t
+( 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 fields; text is)13 3453 1 1587 6398 t
+( the idea of a `line' of text as understood by most Unix programs \320 a)15 3037( Even)1 272(always one-dimensional.)1 1011 3 720 6518 t
+(sequence of characters terminated by a newline character \320 is only weakly supported.)12 3454 1 720 6638 t
+(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 which editing commands refer.)7 1784 4 1640 6794 t
+( explicitly name a particular file or piece of text, the command is)12 2827( a command doesn't)3 870( If)1 137(current file.)1 486 4 720 6914 t
+( presence of multiple files and consider)6 1629( the moment, ignore the)4 988( For)1 198(assumed to apply to the current text.)6 1505 4 720 7034 t
+(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 329(Commands have one-letter names.)3 1387 3 970 7310 t
+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 easy to create these abutting windows.\))10 2059( \(The)1 191(windows above and below.)3 877 5 720 3680 t
+( Each)1 206( typing and mouse operations apply, as indicated by its heavy border.)11 2242( uppermost window is that to which)6 1143( The)1 161(third file window.)2 568 5 720 3780 t
+( The)1 162(window has its current text highlighted in reverse video.)8 1810 2 720 3880 t
+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 null)5 1039 2 2880 3880 t
+( 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 commands make some change to the text in dot)9 2118 2 720 4220 t
+(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)15 2845 1 2195 4340 t
+( 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 2845 1 2195 4460 t
+( 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 before dot.)3 1031 3 720 5630 t
+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 commands: the newline character)10 2735 2 970 5786 t
+( it is often convenient to insert)6 1282( since)1 242( But)1 207(Within the inserted text, however, newlines are never implicit.)8 2589 4 720 5906 t
+(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 escape, so)14 3291 1 720 6926 t
+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 replacement text for the first match, in dot, of the regular)12 3012 4 720 1190 t
+(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 2947( For)1 189(nient variations.)1 644 3 720 1780 t
+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.)8 1908 3 720 2516 t
+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 result of applying the standard sorting utility to it.)12 2792 4 720 2986 t
+( 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(commands. The)1 668 2 1707 3106 t
+(sarily bounded by newlines, although for connection with Unix programs, newlines may be necessary to)14 4320 1 720 3226 t
+(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 3502 t
+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 lines so the old line 4 is now numbered)19 4209 2 720 4478 t
+(\(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.)3 679 3 720 4718 t
+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 288(prints line 3 on the terminal.)5 1163 3 1264 4838 t
+(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 address is always a)4 917 4 970 5114 t
+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 newline of the)11 2515 3 720 5234 t
+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 720 5704 t
+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 t
+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.)4 782 3 720 6920 t
+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 1247 7196 t
+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 3605 1 1077 968 t
+10 R f
+(Text commands)1 641 1 1077 1136 t
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 1204 t
+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 3605 1 1077 2160 t
+10 R f
+(Display commands)1 769 1 1077 2328 t
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 2396 t
+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 3605 1 1077 2728 t
+10 R f
+(File commands)1 614 1 1077 2896 t
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 2964 t
+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 3605 1 1077 3536 t
+10 R f
+(I/O commands)1 591 1 1077 3704 t
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 3772 t
+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 3605 1 1077 4824 t
+10 R f
+(Loops and conditionals)2 933 1 1077 4992 t
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 5060 t
+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 5324 t
+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 3605 1 1077 5872 t
+10 R f
+(Miscellany)1077 6040 w
+10 S f
+(_ ________________________________________________________________________)1 3605 1 1077 6108 t
+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 3605 1 1077 6668 t
+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 containing the end of dot.)7 1491 4 720 840 t
+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 address is a regular expression, which)9 2215 2 970 1116 t
+(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 1586 t
+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)8 2012 2 2405 1706 t
+( the search is started, and if more than one match begins at the same spot, the longest such)18 3834(point where)1 486 2 720 1826 t
+( assume familiarity with the syntax for regular expressions in Unix programs.)11 3096(match. \(I)1 385 2 720 1946 t
+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 `any)7 2014(. For)1 217 3 1924 2996 t
+(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 match across the)19 3663(matches the text)2 657 2 720 3466 t
+(line boundary.)1 577 1 720 3586 t
+( is forwards by default, so)5 1222( direction)1 416( The)1 241(Regular expressions are always relative addresses.)5 2191 4 970 3742 t
+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)1 397(before dot.)1 463 4 1727 4212 t
+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 not seem novel to people)10 2422 2 970 4608 t
+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 2897(Moreover, the kinds of editing)4 1222 2 921 4728 t
+( Indeed,)1 351( a mouse-based interface.)3 1028(sions and line numbers, are clearly more conveniently handled by)9 2659 3 720 4848 t
+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\) is the means by which simple changes are)13 3536 3 720 4968 t
+(large or repetitive changes, however, a textual language outperforms a manual interface.)11 3523 1 720 5088 t
+(Imagine that, instead of deleting just one occurrence of the string)10 2708 1 970 5244 t
+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 occurrence of)12 2656(. What's)1 367 3 1270 5364 t
+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 for each such match, sets dot to the)17 3766 2 720 5834 t
+( 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 6304 t
+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 720 6544 t
+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 t
+( 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 newlines to)14 3290 1 1750 7134 t
+(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 1248 968 t
+10 R f
+(Simple addresses)1 691 1 1248 1136 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 1248 1204 t
+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 1248 2256 t
+10 R f
+(Compound addresses)1 852 1 1248 2424 t
+10 S f
+(_ _________________________________________________________________)1 3264 1 1248 2492 t
+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 1248 3064 t
+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 1248 3552 t
+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 2353 3852 t
+( is resolved by generating the matches)6 1597( This)1 240( matches is chosen \320 matches may overlap.)7 1853(ing what set of)3 630 4 720 3972 t
+( starting)1 338(starting from the beginning of dot using the leftmost-longest rule, and searching for each match)14 3982 2 720 4092 t
+( adja-)1 228( expressions may also match null strings, but a null match)10 2363( Regular)1 371(from the end of the previous one.)6 1358 4 720 4212 t
+( example,)1 388( For)1 189(cent to a non-null match is never selected; at least one character must intervene.)13 3187 3 720 4332 t
+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 531 2 2485 5408 t
+(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 if dot contains a match of the expression.)13 3288 3 720 6724 t
+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 6844 t
+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 1010 t
+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 more involved operations.)10 3880 2 970 1286 t
+(example, to print those lines of text that contain the string)10 2307 1 720 1406 t
+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 1996 t
+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 1996 t
+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 address.)15 3066 1 925 2116 t
+(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, including the)6 1804 2 720 3018 t
+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 182(manipulating files with multi-line `records.')4 1797 4 720 3138 t
+(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, a sequence of one or more non-blank lines.)9 2010 3 720 4838 t
+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 5358 t
+10 R f
+( Tic's record, extracts the phone number from)7 1862(The latter command breaks the file into records, chooses Mr.)9 2458 2 720 5538 t
+(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 2927 1 970 5814 t
+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 1479 6874 t
+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(. This)1 257 2 2029 6994 t
+(For example, in C character and string constants, the sequence)9 2510 1 720 7114 t
+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 expressions\), or we could even)12 3478 2 1308 1010 t
+(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 3618 1 1008 1300 t
+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 consideration all character constants and)10 3292 4 999 1480 t
+(remaining problem is to deal with the possible occurrence of)9 2439 1 720 1600 t
+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 composed commands is successive refinement.)8 2648 4 970 1876 t
+( can be undone;)3 644( \(Mistakes)1 449( it can be honed by adding a clause or two.)10 1755(is tried, and if it's not good enough,)7 1472 4 720 1996 t
+( result-)1 283( The)1 208( the mouse language makes it unnecessary to retype the command each time.\))12 3138( Also,)1 267(see below.)1 424 5 720 2116 t
+(ing chains of commands are somewhat reminiscent of shell pipelines.)9 2783 1 720 2236 t
+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 3 2751 2356 t
+( by step until the correct piece is available to the final)11 2237(same, but which pieces are selected is refined step)8 2083 2 720 2476 t
+(step of the command line, which ultimately makes the change.)9 2494 1 720 2596 t
+(In other Unix programs, regular expressions are used only for selection, as in the)13 3315 1 970 2752 t
+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 to handle newline-free text.)17 3619(operated on,)1 494 3 720 2992 t
+( the structure of a piece of text rather than its contents, as in the)14 2671(use of regular expressions to describe)5 1554 2 720 3112 t
+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 example, they are pleasantly expressive.)5 1870 3 720 3352 t
+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 3784 t
+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 disc file;)15 3318 1 720 4134 t
+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(replaces dot with the contents of the named disc file.)9 2192 4 720 4834 t
+( 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 720 5304 t
+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 656 2 2433 5654 t
+( The)1 205( first three characters are a concise notation for the state of the file.)13 2677( The)1 206(\(described in the next section\).)4 1232 4 720 5774 t
+( sign indicates the number of windows open on the)9 2052( minus)1 271( The)1 206(apostrophe signifies that the file is modified.)6 1791 4 720 5894 t
+(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(indicates that this is the current file.)6 1453 4 720 6134 t
+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 be started with a set of disc files)8 1481 2 1175 6410 t
+(list of file names as arguments, and more may be added or deleted on demand.)14 3133 1 720 6530 t
+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 associated disc files\).)7 2081 3 1719 7230 t
+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 all C source)4 851 2 720 1540 t
+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 that contain matches of)5 1595 3 720 1660 t
+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 2286 t
+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, of course, the current file is usually chosen by)10 2384 3 720 2406 t
+(other way is to use a form of address that refers to files:)12 2223 1 720 2526 t
+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 evaluated in the file whose menu line)10 2397 2 720 2876 t
+( 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 3346 t
+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 lines match the expression.)11 2797 3 720 4322 t
+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 2727 1 2113 5142 t
+(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 2 720 5978 t
+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 prints)5 1104 2 2188 6658 t
+(The precise semantics of compound operations is discussed in the implementation sections below.)12 3921 1 720 6778 t
+(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 2009 2 2216 6934 t
+(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 last two commands.)5 1315 4 1952 1120 t
+( though, including for example)4 1243( else is undoable,)3 697( Everything)1 497(may any command that adds or deletes files.)7 1796 4 720 1240 t
+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, and modified bit.)9 2429(restores the state of the)4 960 4 720 1820 t
+( Only)1 259(potentially dangerous commands are not guarded by confirmations.)7 2759 2 720 1940 t
+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 necessary to restore itself, is protected.)6 1772 4 720 2060 t
+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 display and mouse for interactive editing.)13 3525 4 928 2576 t
+(ference in the command language between regular, mouse-driven)7 2637 1 720 2696 t
+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 t
+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 Figure 2\).)14 3053 1 720 2936 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 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 visible in the window.)17 3127( scroll)1 203(window. The)1 432 3 1278 4744 t
+( current text, which is highlighted, need not fit on a)10 1703( The)1 167( be manipulated by the mouse for convenient browsing.)8 1821(The scroll bar may)3 629 4 720 4844 t
+( it consists of one partial line, one complete line, and final partial line.)13 2252(line. Here)1 338 2 720 4944 t
+10 R f
+( in all)2 236( any time, only one window)5 1131( At)1 153(Each file may have zero or more windows open on the display.)11 2550 4 970 5220 t
+(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; this)11 2609 2 1965 5340 t
+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 which commands may be typed\) or one of the)11 2499 4 926 5460 t
+( current file is the last file)6 1074( The)1 213(windows, the image of the file in each window is always kept up to date.)14 3033 3 720 5580 t
+(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 2383 5700 t
+( window on a file has its own value of dot, and when switching between)14 3148( each)1 227( However,)1 460(current file.)1 485 4 720 5820 t
+( flipping between)2 711( Thus,)1 281( file, the file's value of dot is changed to that of the window.)13 2494(windows on a single)3 834 4 720 5940 t
+(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 944(The mouse on the Blit has three buttons,)7 1644 4 970 6216 t
+( as printed by the)4 708(manipulate windows, followed by a list of `menu lines' exactly)9 2579 2 720 6336 t
+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( menu lines are sorted by)5 1011( These)1 290(\(not one per window\).)3 893 6 720 6456 t
+( the)1 153( Using)1 295( manageable by generating a scrolling menu instead of an unwieldy long list.)12 3143(will make it more)3 729 4 720 6576 t
+( makes that file the current file, and the most recently current window in)13 2967(menu to select a file from the list)7 1353 2 720 6696 t
+( if that file is already current, selecting it in the menu cycles through the)14 2960( But)1 201( the current window.)3 844(that file)1 315 4 720 6816 t
+( is no)2 239( there)1 234( If)1 126(windows on the file; this simple trick avoids a special menu to choose windows on a file.)16 3721 4 720 6936 t
+(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 7056 t
+( commands)1 465(The commands on the button 3 menu are straightforward \(see Figure 3\), and are like the)15 3605 2 970 7212 t
+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 720 960 t
+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 of that window; this is how)10 2472 2 720 1080 t
+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 1560 t
+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 limited to the length shown to)18 2744( The)1 163(Figure 3. The menu on button 3.)6 1052 4 720 3992 t
+( 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 commands; beneath it is)8 1280 2 2110 4092 t
+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 from the usual arrow to a box with a small arrow.)13 2710 4 720 4588 t
+( one corner and releasing it at the opposite corner.)9 2103(indicate an arbitrary rectangle by pressing button 3 at)8 2217 2 720 4708 t
+( button 3 may simply be clicked, whereupon)7 1860(More conveniently,)1 795 2 720 4828 t
+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 regions \(one above, one below\) in which)11 2599 2 720 5068 t
+( simple user interface trick makes window creation notice-)8 2403( This)1 236(created with minimal fuss \(see Figure 1\).)6 1681 3 720 5188 t
+(ably easier.)1 454 1 720 5308 t
+(The cut-and-paste editor is essentially the same as that in Smalltalk-80.)10 2944 1 970 5464 t
+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 null string)14 2630( When)1 291(highlighted on the screen.)3 1041 4 720 5584 t
+( button, moving)2 657( 1 is used for selection: pressing the)7 1492( Button)1 332( ordinary typing inserts text.)4 1166(character. Thus,)1 673 5 720 5704 t
+( to\) the text between the points where the button was)10 2248(the mouse, and lifting the button selects \(sets dot)8 2072 2 720 5824 t
+( a null string; this is called clicking.)7 1477( and releasing at the same point selects)7 1592( Pressing)1 395(pressed and released.)2 856 4 720 5944 t
+(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 6064 t
+( clicking just inside an opening bracket selects the text contained in the brackets)13 3328(selects the word, double)3 992 2 720 6184 t
+( double-clicking)1 656( The)1 209( similarly for parentheses, quotes, and so on.)7 1809(\(handling nested brackets correctly\), and)4 1646 4 720 6304 t
+( 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 6424 t
+(would probably select linguistic structures such as sentences.)7 2441 1 720 6544 t
+( is the)2 245( This)1 231( outside the current window, it makes the indicated window current.)10 2748(If button 1 is pressed)4 846 4 970 6700 t
+(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\).)6 1472(Pressing button 2 brings up a menu)6 1465 4 970 6976 t
+(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, and)4 1191 2 1513 7096 t
+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 1047 7216 t
+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 for the)3 1007 2 1705 840 t
+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 to search forward for the next)12 3029 2 720 1080 t
+(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 literal text.)14 2834( The)1 160(Figure 4. The menu on button 2.)6 1034 3 720 2404 t
+10 R f
+( mouse language is entirely due to the equal-)8 1794(The relationship between the command language and the)7 2276 2 970 2680 t
+( example, to make a set of changes)7 1419( For)1 193( button 1 on the mouse.)5 959(ity of dot and the selected text chosen with)8 1749 4 720 2800 t
+( be set by double clicking on the left brace that begins the subroutine, which sets)15 3278(in a C subroutine, dot can)5 1042 2 720 2920 t
+( address-free command then typed in the)6 1660( An)1 178(dot for the command language.)4 1269 3 720 3040 t
+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 opening and closing braces of the function.)10 2436(to the)1 225 4 720 3160 t
+( of)1 108( And)1 222( want to do with it, whether invoked by a menu selection or by a typed command.)16 3263(then say what you)3 727 4 720 3280 t
+( relationship)1 516( This)1 247( command completes.)2 912(course, the value of dot is highlighted on the display after the)11 2645 4 720 3400 t
+( to explain, but comfortable, even natural, in)7 1859(between mouse interface and command language is clumsy)7 2461 2 720 3520 t
+(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 224 2 2405 4036 t
+( dis-)1 181( explaining how the command language is implemented, the)8 2462( After)1 267(munication, then the terminal part.)4 1410 4 720 4156 t
+( presen-)1 320( The)1 207(cussion follows \(roughly\) the path of a character from the temporary file on disc to the screen.)16 3793 3 720 4276 t
+( because that is how the program was designed and because the algo-)12 2844(tation centers on the data structures,)5 1476 2 720 4396 t
+(rithms are easy to provide, given the right data structures.)9 2299 1 720 4516 t
+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 command with a table-driven)11 3566 2 720 4912 t
+( editors instead)2 617( Most)1 262( top-down executor.)2 813(parser, and when a complete command is assembled, invoking a)9 2628 4 720 5032 t
+( and unambiguous to)3 875( of a parser makes it easy)6 1080( Use)1 217(employ a simple character-at-a-time lexical scanner.)5 2148 4 720 5152 t
+( conventions such as back-)4 1102( escape)1 299( First,)1 268(detect when a command is complete, which has two advantages.)9 2651 4 720 5272 t
+( the command isn't finished, the parser keeps)7 1850( if)1 117( multiple-line commands are unnecessary;)4 1710(slashes to quote)2 643 4 720 5392 t
+( example, a multiple-line append driven by an)7 1836(reading. For)1 513 2 720 5512 t
+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 2809 1 720 6192 t
+( advantage is specific to the two-process structure of)8 2167(The other)1 393 2 970 6348 t
+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 completed so the command interpreter can be called.)11 2993 4 720 6468 t
+(by having the lexical analyzer read the single stream of events from the terminal, directly executing all typ-)17 4320 1 720 6588 t
+(ing and mouse commands, but passing to the parser characters typed to the)12 3071 1 720 6708 t
+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(scheme is slightly)2 721 2 720 6828 t
+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 1658(between the newline and the previously typed)6 1872 4 720 7068 t
+(trary editing to be done to a command before typing newline and thereby requesting execution.)14 3796 1 720 7188 t
+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 because the syntax of addresses)11 2496 2 970 840 t
+( replacement text in a substitution, so the syn-)8 1859( are few special cases, such as the)7 1373( There)1 286(encoded compactly.)1 802 4 720 960 t
+( include whether the command allows)5 1542( These)1 293( can be encoded with a few flags.)7 1364(tax of almost all commands)4 1121 4 720 1080 t
+(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 1825 1200 t
+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 multi-line, and so on.)7 1542 4 1922 1320 t
+( Regular)1 377( parser; a regular expression is a leaf of the command parse tree.)12 2699(sions is handled by a separate)5 1244 3 720 1440 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 interpreter is always called with)14 3755 2 970 1716 t
+( example, the parser fills in the implicit)7 1676( For)1 204(plete tree.)1 408 3 720 1836 t
+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, and provides the usual)14 3517 2 1205 1956 t
+(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 1129 2076 t
+( address is evaluated left-to-right)4 1358( The)1 216( complete command is parsed, the evaluation is easy.)8 2207(Once a)1 289 4 970 2232 t
+( the)1 156( like many of)3 560( Addresses,)1 495(starting from the value of dot, with a mostly ordinary expression evaluator.)11 3109 4 720 2352 t
+(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 1008 2642 t
+(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 720 3592 t
+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 3868 t
+(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 3033 1 2007 4356 t
+( argument to)2 513( The)1 209( level of the parser.)4 783(\(see the section below on `undoing'\), and returns to the top)10 2390 4 720 4476 t
+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 type that is translated to a terse but)9 2141 2 720 4596 t
+( common messages are kept short; for example the message for a failed regular expression)14 3790(order.' Very)1 530 2 720 4716 t
+(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 position)5 1626 2 2137 5112 t
+( Except)1 330( are computed by reading the file, counting newlines.)8 2148(sive to track dynamically \320 so line addresses)7 1842 3 720 5232 t
+( access is fast enough to make the technique practical, and)10 2325(in very large files, this has proven acceptable: file)8 1995 2 720 5352 t
+(lines are not central to the structure of the command language.)10 2492 1 720 5472 t
+(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(, is)1 119 4 2725 5628 t
+( as arguments the calculated address)5 1484( function receives)2 725( That)1 241(tion to call to interpret a particular command.)7 1870 4 720 5748 t
+( 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 compound commands.)3 1359 3 720 5988 t
+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 1490 t
+(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 648 3 1440 1930 t
+(})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 section.\))11 2828 2 1825 2220 t
+(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 2838 1 1716 2340 t
+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)3 952 2 2234 2736 t
+(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.)7 2045(, including)1 433 4 2142 2976 t
+(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 3096 t
+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 too complicated for an interactive editor \320)7 2111 2 720 3456 t
+( wouldn't have peculiar mean-)4 1223(ters were two-character sequences, so that most of the punctuation characters)10 3097 2 720 3576 t
+( regular expressions are necessary, and)5 1660(ings \320 but for an interesting command language, full)8 2313 2 720 3696 t
+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 662(defines the full regular expression syntax for)6 1821 4 720 3816 t
+(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 720 4212 t
+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 of the expression in a substring of the file.)10 2868 4 720 4332 t
+(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, and)6 1617 2 1601 4452 t
+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 evaluate the expression in reverse,)9 2318 2 970 4728 t
+( 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 the expression.)7 1411(looks backwards through the)3 1166 4 720 5198 t
+( solution is to construct a machine identical to the machine for a forward search except)15 3492( The)1 207(forward search.)1 621 3 720 5318 t
+( the concatenation operators \(the other operators are symmetric under direction reversal\),)11 3562(for a reversal of all)4 758 2 720 5438 t
+(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 5558 t
+(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 one match each time it is called.)8 1960 4 1424 5834 t
+(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. For)1 547 2 720 6074 t
+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 6194 t
+(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, rejects the match and)18 3742(of its previous)2 578 2 720 6434 t
+(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 library routine,)12 3699 1 720 6950 t
+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( For)1 195(vides adequate service for simple programs.)5 1787 4 720 7070 t
+( rather, pair of allocators\) described here work in both the terminal and host)13 3190( allocator \(or)2 539(allocator. The)1 591 3 720 7190 t
+(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 freed)12 3464(. They)1 303 2 1273 7310 t
+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 length from essentially zero to 32 Kbytes)10 2677 2 720 840 t
+(More important, strings may be large and change size often, so to minimize memory usage it is helpful to)18 4320 1 720 960 t
+(reclaim and to coalesce the unused portions of strings when they are truncated.)12 3146 1 720 1080 t
+(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 variable-sized arrays of characters or)11 3452 2 720 1356 t
+( memory allocator in)3 841( The)1 207( used to access them.)4 848(pointer is always)2 679 4 720 1476 t
+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, at the cost of some bookkeeping.)12 3610(tor that)1 300 4 720 1716 t
+( allocator controlling the)3 997(types of objects are allocated from adjoining arenas, with the garbage-compacting)10 3323 2 720 1836 t
+( prevents fragmentation)2 958( into two arenas simplifies compaction and)6 1739( Separating)1 481(arena with higher addresses.)3 1142 4 720 1956 t
+( garbage-compactable objects \(discussed in the next para-)7 2358( access rules for)3 666( The)1 214(due to immovable objects.)3 1082 4 720 2076 t
+( so when the first-fit arena needs space, it moves the garbage-compacted)11 2936(graph\) allow them to be relocated,)5 1384 2 720 2196 t
+( is therefore created only at successively higher addresses,)8 2340( Storage)1 357( higher addresses to make room.)5 1302(arena to)1 321 4 720 2316 t
+(either when more garbage-compacted space is needed or when the first-fit arena pushes up the other arena.)16 4256 1 720 2436 t
+( the sole reposi-)3 647(Objects that may be compacted declare to the allocator a cell that is guaranteed to be)15 3423 2 970 2592 t
+( then update the)3 668( compactor can)2 633( The)1 217(tory of the address of the object whenever a compaction can occur.)11 2802 4 720 2712 t
+( type)1 199( example, the implementation of)4 1300( For)1 190(address when the object is moved.)5 1375 4 720 2832 t
+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 2159 3 1125 3632 t
+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 3752 t
+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 2631 3872 t
+(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)11 3220( C)1 109( Traditionally,)1 614(the code.)1 377 4 720 4268 t
+( passing is a relatively new feature of C \(it is not in the standard reference)15 3036( Structure)1 422( to use.)2 298(stack is easier)2 564 4 720 4388 t
+(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 1044(\), and is poorly supported in most)6 1377 4 1317 4508 t
+(though, and simplifies memory management by avoiding the allocator altogether and eliminating pointer)12 4320 1 720 4628 t
+(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, but strict.)10 2354(showed that the)2 627 4 1601 5144 t
+( the implementation must)3 1026( Second,)1 371( read and written quickly; adding a fresh file must be painless.)11 2507(need to be)2 416 4 720 5264 t
+( should be practical to edit many files, and)8 1719( \(It)1 147( sizes of files.)3 558(place no arbitrary upper limit on the number or)8 1896 4 720 5384 t
+( implies that files be stored on disc, not)8 1593( This)1 231(files up to megabytes in length should be handled gracefully.\))9 2496 3 720 5504 t
+( of virtual memory may argue otherwise, but the implementation of virtual)11 3046( \(Aficionados)1 577( memory.)1 389(in main)1 308 4 720 5624 t
+( changes to files need)4 879( Third,)1 302( system is not something to depend on for good performance.\))10 2533(memory in our)2 606 4 720 5744 t
+( are inverses of each other, which simplifies)7 1785( These)1 292( only two primitives: deletion and insertion.)6 1777(be made by)2 466 4 720 5864 t
+( the file, either)3 609( it must be easy and efficient to access)8 1610( Finally,)1 369(the implementation of the undo operation.)5 1732 4 720 5984 t
+(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 structures that hold arrays of)12 3150 2 1429 6260 t
+( has an insertion and deletion operator, and the insertion and deletion operators of the)14 3520(Each of these types)3 800 2 720 6380 t
+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 is used to)4 697 4 2305 6656 t
+(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 will never be longer than)7 1849 2 1375 6776 t
+(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)10 2514 2 2315 6896 t
+( are therefore used as)4 862( They)1 259( efficient to modify.)3 811(overhead, and because they are deliberately small, they are)8 2388 4 720 7016 t
+(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 970 7292 t
+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 3002(has no)1 269 2 1769 840 t
+( reduce the number of open files needed,)7 1665( To)1 167(data being held is all on the disc.)7 1352 3 720 960 t
+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 them.)1 451 4 2467 1080 t
+10 CW f
+(sam)4860 1080 w
+10 R f
+(source \(48 files\) may be edited comfortably with a single instance of)11 2819 1 720 1200 t
+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 1157(would strain the operating system's limit)5 1646 4 1141 1320 t
+(among temporary files keeps the files shorter, and shorter files are more efficiently implemented by the)15 4320 1 720 1440 t
+(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, each of which contains)9 2316 2 1338 1716 t
+( block addresses within the tempo-)5 1400( The)1 207( file system is 4096 bytes.\))5 1082( block size of our Unix)5 933( \(The)1 241(active data.)1 457 6 720 1836 t
+( stored in a)3 450(rary file and the length of each block are)8 1633 2 720 1956 t
+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 necessary to try)8 1987( Blocks)1 335(may change size.)2 698 4 720 2076 t
+( actively changing part of the)5 1172( An)1 173(and 4096 bytes.)2 632 3 720 2196 t
+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 2196 t
+( an)1 130( When)1 299( inserted or deleted without changing more than one block or affecting the block order.)14 3622(can be)1 269 4 720 2316 t
+( one is allocated to receive the overflow, and the)9 1970(insertion would overflow a block, the block is split, a new)10 2350 2 720 2436 t
+(memory-resident list of blocks is rearranged to reflect the insertion of the new block.)13 3388 1 720 2556 t
+( data)1 200( The)1 214( modification to the file is prohibitively expensive.)7 2090(Obviously, going to the disc for every)6 1566 4 970 2712 t
+(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 termi-)13 3260(provide a way to organize)4 1060 2 720 3072 t
+( idea is developed below, in the section on communications.)9 2406(nal. This)1 375 2 720 3192 t
+( 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 cache.)4 807 3 720 3468 t
+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)1 148( If)1 117(dated by the cache, it is done there.)7 1412 4 720 3588 t
+( 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 deleted from the cache.)4 1099 3 1826 3708 t
+( the new position if the change is smaller than a block; otherwise, it)13 2749( cache is only loaded at)5 949( The)1 208(is flushed.)1 414 4 720 3828 t
+( 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 2900(. This)1 264 2 1876 3948 t
+(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 data)8 1797 2 2216 4224 t
+( most important components, though, are a pair of)8 2006( The)1 206(ified bit.)1 340 3 720 4344 t
+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.)2 735 3 720 4464 t
+( times)1 243( it may seem that the data is touched many)9 1701( Although)1 428(The overall structure is shown in Figure 5.)7 1698 4 970 4620 t
+(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 2788(, it is read)3 430 2 1822 4740 t
+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( Similarly,)1 456(; no extra copy is done.)5 972 4 1080 4860 t
+( principle applied throughout)3 1172( A)1 125( operations act directly on the text in the cache.)9 1912( Most)1 260(cache to disc.)2 548 5 720 4980 t
+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 \(see also the paper by Waite)19 3930 1 720 5100 t
+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 stroke}
+ 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 host system.)15 2752( The)1 160(Figure 5. File data structures.)4 965 3 720 7052 t
+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 starting)15 3072 1 1968 840 t
+( read a byte at a time, a per-)8 1166( To)1 168(at a specified offset.)3 824 3 720 960 t
+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, and bytes may then be read from the array.)9 2100 3 720 1080 t
+(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 the)1 493 2 2027 1200 t
+( array is read-only; there is no)6 1197( This)1 228(macro allows the file to be read backwards.)7 1734 3 720 1320 t
+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(has an unusual method for managing changes)6 1837 4 928 1716 t
+( variable-length changes to a file millions of bytes long, and such changes must be made effi-)16 3842(ify multiple)1 478 2 720 1836 t
+( usual techniques for inserting and deleting strings are inadequate)9 2633( The)1 207( to be practical.)3 621(ciently if the editor is)4 859 4 720 1956 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 2076 t
+( be taken to avoid super-linear behavior when making many changes simultane-)11 3246(long strings, but care must)4 1074 2 720 2196 t
+(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 database against which)15 3859 1 1181 2472 t
+( when a command is)4 865( Instead,)1 374( the contents.)2 552( are not made directly to)5 1033( Changes)1 406(transactions are registered.)2 1090 6 720 2592 t
+(started, a `mark' containing a sequence number is placed in the transcript)11 2965 1 720 2712 t
+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 insertion or deletion or a change to the file)13 2605 2 720 2832 t
+( the command is complete, the transcript is rewound to the mark and applied to the contents.)16 3690(script. When)1 535 2 720 2952 t
+( is to simplify tracking the)5 1198(One reason for separating evaluation from application in this way)9 2872 2 970 3108 t
+( two-pass algorithm also allows all)5 1481( The)1 224(addresses of changes made in the middle of a long sequence.)10 2615 3 720 3228 t
+(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 2103 2 2080 3348 t
+( 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 over changes made earlier in the execution.)9 2917 4 720 3588 t
+( to affect each other; for example,)6 1366(cleaner than the way other Unix editors allow changes)8 2199 2 720 3708 t
+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 line depend critically)7 2027 3 720 3828 t
+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 explain and to understand.)15 4135 1 720 3948 t
+( substring from locations 123 to 456'' and)7 1825(The records in the transcript are of the form ``delete)9 2245 2 970 4104 t
+( changes are not at monotonically)5 1370( is an error if the)5 692( \(It)1 150(``insert 11 characters `hello there' at location 789.'')7 2108 4 720 4224 t
+( the update is occurring, these numbers must be offset by earlier)11 2633( While)1 301(greater positions through the file.\))4 1386 3 720 4344 t
+( to the update routine; moreover, all the numbers have been)10 2466(changes, but that is straightforward and local)6 1854 2 720 4464 t
+(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 has another advantage: undo is trivial.)12 3311 3 970 4740 t
+( deletions and vice versa, and)5 1200(invert the transcript after it has been implemented, converting insertions into)10 3120 2 720 4860 t
+(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)1 230 3 2094 4980 t
+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 2862( If)1 119(replaced by the `undo' transcript.)4 1339 3 720 5100 t
+( 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 2 2470 5220 t
+( can therefore back up the file arbitrarily, which is more helpful)11 2626( sequence of undo commands)4 1217(changes. A)1 477 3 720 5340 t
+( \()1 87( form of undo.)3 589(than the more commonly implemented self-inverse)5 2054 3 720 5460 t
+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(but if it were desired, it would be easy to provide by)11 2152 4 720 5580 t
+( offset into the transcript of the previous mark, to aid in)11 2390(transcript contains a sequence number and the)6 1930 2 720 5700 t
+( also contain the value of dot and the modified bit so these can be restored)15 3004( Marks)1 308(unwinding the transcript.)2 1008 3 720 5820 t
+( merely demands undoing all files whose latest change has the)10 2625( multiple files is easy; it)5 1024(easily. Undoing)1 671 3 720 5940 t
+(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 having a transcript is that errors)8 2053 2 970 6216 t
+( rewinding the transcript to the mark beginning)7 1933( By)1 174( files in an intermediate state.)5 1211(mand need not leave the)4 1002 4 720 6336 t
+(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 the update algorithm was first)5 1456 2 970 6612 t
+( reduced the number)3 820( This)1 229( small changes by a single larger one.)7 1504(coalesce nearby changes, replacing multiple)4 1767 4 720 6732 t
+(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 2 2418 6852 t
+( the caching method only works if changes)7 1718(impossible to handle changes in non-monotonic order in the file;)9 2602 2 720 6972 t
+( cache was added, the transaction could in principle be sorted if the changes were)14 3286( the)1 149( Before)1 323(don't overlap.)1 562 4 720 7092 t
+( therefore acceptable performance with a)5 1664( current status is)3 676( The)1 213(out of order, although this was never done.)7 1767 4 720 7212 t
+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 annoyance.)11 3309 1 720 840 t
+( simpler algorithms, but it is not pro-)7 1627(The update algorithm obviously paws the data more than)8 2443 2 970 996 t
+( principle of avoiding copying the data is still honored here,)10 2519( \(The)1 251( help.)1 235(hibitively expensive; the caches)3 1315 4 720 1116 t
+( through)1 343(although not as piously: the data is moved from contents' cache to the transcript's all at once and)17 3977 2 720 1236 t
+( dead start a hundred)4 839( read from a)3 493( To)1 164( figures confirm the efficiency.)4 1250( Performance)1 562(only one internal buffer.\))3 1012 6 720 1356 t
+( a VAX-11/750 takes 1.4 seconds of user time, 2.5 seconds of system time, and 5 seconds of)17 3709(kilobyte file on)2 611 2 720 1476 t
+( 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 1770 2 2352 1596 t
+(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 half the CPU time.)5 1254 4 1861 1716 t
+( The)1 205(above: inserting a character between every pair of characters in the file.)11 2851 2 720 1836 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 the regular expression)19 3945(and takes)1 375 2 720 2186 t
+( translates to about 500 changes per second.)7 1759(code. This)1 442 2 720 2306 t
+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 undo it.)6 1898 3 720 2426 t
+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 Berkeley, which allows one level of undoing, again)11 3064 4 720 2546 t
+10 CW f
+(sam)720 2666 w
+10 R f
+('s performance is comparable to that of other Unix editors, although it solves a harder problem.)15 3807 1 900 2666 t
+10 B f
+(Communications)720 2906 w
+10 R f
+(The discussion so far has described the implementation of the host part of)12 3138 1 720 3062 t
+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.)12 3263(explain how a)2 570 2 720 3182 t
+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 connected.)7 1801 3 2102 3458 t
+( This)1 229( command language to edit text on an ordinary terminal.)9 2256(forgo the terminal part and use the host part's)8 1835 3 720 3578 t
+(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(programs, communicating with a message protocol)5 2047 3 720 3818 t
+( display for)2 469(the connection is an RS-232 link between a Blit \(the prototypical)10 2658 2 720 3938 t
+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 4058 t
+( are discussed in the)4 828( relevant aspects)2 669( Its)1 155(ences Research Center at AT&T Bell Laboratories, where I work.)9 2668 4 720 4178 t
+(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 4298 t
+(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 the two programs.)11 2730(The low bandwidth)2 781 4 970 4574 t
+( a self-contained one,)3 859(is a mixed blessing: a program in two parts is much harder to write and to debug than)17 3461 2 720 4694 t
+( terminal may be physically separated from)6 1736( The)1 206( configurations possible.)2 982(but the split makes several unusual)5 1396 4 720 4814 t
+( to be taken home while leaving the files)8 1628(the host, allowing the conveniences of a mouse and bitmap display)10 2692 2 720 4934 t
+( is also possible to run the host part on a remote machine:)12 2298( It)1 111(at work.)1 327 3 720 5054 t
+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 the usual way, and then makes a call across)13 2817 2 720 5404 t
+(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 the two)8 1628( Finally,)1 362(on the named machine.)3 935 6 1039 5524 t
+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 1 720 5644 t
+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 2434 2 2392 5764 t
+(local host's job is simple but vital: it passes the data between the remote host and terminal.)16 3628 1 720 5884 t
+( exchange messages asynchronously \(rather than, say, as remote procedure)9 3145(The host and terminal)3 925 2 970 6040 t
+( correction because, whatever the configuration, the connection is)8 2709(calls\) but there is no error detection or)7 1611 2 720 6160 t
+( mundane interaction tasks such as popping up menus and interpret-)10 2763( the terminal handles)3 847(reliable. Because)1 710 3 720 6280 t
+( example, the host knows nothing about)6 1657( For)1 200( not actions.)2 508(ing the responses, the messages are about data,)7 1955 4 720 6400 t
+( says)1 211(what is displayed on the screen, and when the user types a character, the message sent to the host)18 4109 2 720 6520 t
+( position in the)3 601(``insert a one-byte string at location 123 in file 7,'' not ``a character was typed at the current)17 3719 2 720 6640 t
+( other words, the messages look very much like the transaction records in the transcripts.)14 3544( In)1 133(current file.'')1 531 3 720 6760 t
+( 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 1224 3 2560 6916 t
+( mouse operations are executed directly in the terminal to optimize)10 2708(ates on the host, while typing and some)7 1612 2 720 7036 t
+( \(A)1 165( the terminal, and vice versa.)5 1200( initiated by the host program must be transmitted to)9 2178(response. Changes)1 777 4 720 7156 t
+( which means that characters typed while a time-)8 1999(token is exchanged to determine which end is in control,)9 2321 2 720 7276 t
+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 complete.\))10 2475(consuming command runs must be)4 1414 4 720 840 t
+( track changes through a per-file data structure that records)9 2351(tain consistent information, the host and terminal)6 1969 2 720 960 t
+( data structure, called a)4 954( The)1 213(what portions of the file the terminal has received.)8 2074 3 720 1080 t
+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.)12 2695 2 720 1200 t
+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 the terminal, separated by counts of the)13 2905 2 720 1320 t
+( needs the lengths of the various pieces\),)7 1619(Of course, the host doesn't keep a separate copy of the data \(it only)13 2701 2 720 1440 t
+(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 terminal doubles as a cache.)6 1420 3 1431 1716 t
+(file it has displayed, it need not request data from the host when revisiting old parts of the file or redrawing)20 4320 1 720 1836 t
+(obscured windows, which speeds things up considerably over low-speed links.)9 3143 1 720 1956 t
+(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 2112 t
+( made by the host are compared against the)8 1740( Changes)1 396( already loaded there.)3 864(parts of the file)3 616 4 720 2232 t
+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( Small)1 287( each command.)2 657(update sequence after)2 867 4 720 2352 t
+( in the holes, are transmitted as messages with-)8 1894( changes, and changes that fall entirely)6 1567( Larger)1 318(their entirety.)1 541 4 720 2472 t
+( a command is)3 610( When)1 298( the lengths of the deleted and inserted strings are transmitted.)10 2577(out literal data: only)3 835 4 720 2592 t
+( in their)2 319(completed, the terminal examines its visible windows to see if any holes)11 2947 2 720 2712 t
+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 with up to)12 2569( It)1 118(ble portion of the file.)4 902 4 720 2832 t
+( technique)1 414( This)1 229( the file.)2 332(rounding data, to minimize the number of messages when visiting a new portion of)13 3345 4 720 2952 t
+( first level sends a minimum of informa-)7 1646( The)1 209(provides a kind of two-level lazy evaluation for the terminal.)9 2465 3 720 3072 t
+( the file not being edited interactively; the second level waits until a change is displayed)15 3575(tion about parts of)3 745 2 720 3192 t
+( is also helped by having the terminal respond)8 1933( course, performance)2 860( Of)1 166(before transmitting the new data.)4 1361 4 720 3312 t
+( for small changes to active pieces of the file,)9 1906( Except)1 338( and simple mouse requests.)4 1168(immediately to typing)2 908 4 720 3432 t
+(which are transmitted to the terminal without negotiation, the terminal is wholly responsible for deciding)14 4320 1 720 3552 t
+(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(When a change is initiated by the)6 1343 2 970 3828 t
+( the transcript of the changes to the contents of the)10 2023(ated by the routine that applies)5 1239 2 720 3948 t
+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 same update routine, undoing requires)7 2147 2 720 4068 t
+(sages describing changes to the file are sufficient to back up the screen image.)13 3120 1 720 4188 t
+(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 4344 t
+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 busy text in main memory.)12 2519(access to the active)3 791 4 720 4464 t
+( be)1 138( the form of data is to)6 968( Since)1 290(efficient access to a large data structure that does not fit in memory.)12 2924 4 720 4584 t
+( characters will frequently be scanned sequentially,)6 2090(imposed by the user, not by the program, and because)9 2230 2 720 4704 t
+( help keep performance good and linear when working with such)10 2724( Caches)1 350(files are stored as flat objects.)5 1246 3 720 4824 t
+(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 immediately at hand.)9 2002(with more information)2 929 4 720 5220 t
+( time to access)3 588(tures, the accesses are usually sequential, and read-ahead can significantly reduce the average)12 3732 2 720 5340 t
+( mode for people as well as programs; con-)8 1764( access is a common)4 839( Sequential)1 478(the next element of the object.)5 1239 4 720 5460 t
+(sider scrolling through a document while looking for something.)8 2579 1 720 5580 t
+( at least the implementation.)4 1158(Finally, like any good data structure, the cache guides the algorithm, or)11 2912 2 970 5736 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 control the communications)6 2145 2 1177 5856 t
+( caches were more explicitly intended to serve a)8 1957( Other)1 282( was also a form of cache.)6 1068(realized very early that it)4 1013 4 720 5976 t
+(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 t
+(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(, they also)2 408 2 1914 6216 t
+( not need to write)4 715( saved me considerable work: I did)6 1427( This)1 233(achieved in just a few messages to the terminal.)8 1945 4 720 6336 t
+( they)1 200( Also,)1 267( pay off in surprising ways.)5 1108( Caches)1 341( optimize the message traffic to the terminal.)7 1807(special code to)2 597 6 720 6456 t
+(tend to be independent, so their performance improvements are multiplicative.)9 3131 1 720 6576 t
+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 of the files being edited.)18 4320 1 720 6972 t
+( text is always in memory, the data structures are considerably simpler than those in the host)16 3831(Because the)1 489 2 720 7092 t
+(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 960 t
+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 of synchronously updated windows that are usually)7 2432 4 720 1080 t
+(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 2184 2 1658 1200 t
+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 display for each)12 3110 2 1329 1320 t
+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 720 1440 t
+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 1680 t
+(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 data)15 2509 2 2155 1800 t
+(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 represents a)5 1141 2 1376 1920 t
+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 all front-to-back on the)11 2554 2 1335 2040 t
+( in the termi-)3 519( complement)1 520( The)1 206(as an element of a per-file array that holds all the open windows for that file.)15 3075 4 720 2160 t
+(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.)5 1234 3 2096 4731 t
+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 4851 t
+(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 3 1252 4971 t
+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( When)1 297(is a kind of cache.)4 759 4 1553 5127 t
+( The)1 207( this.)1 197(will look the same in the final image, and the update algorithms exploit)12 2887 3 720 5247 t
+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 the software that updates the display is)7 1698 2 720 5487 t
+( a sort of two-dimensional string insertion)6 1713(verting the text from ASCII characters to pixels is expensive\) in)10 2607 2 720 5607 t
+( details of this process are described in the next section.)10 2221(algorithm. The)1 619 2 720 5727 t
+(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 single)14 3519 1 1521 5883 t
+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 the contents of the window can be)9 2598 2 720 6243 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 is always fully visible, by construction.)12 3627 3 720 6483 t
+( it may be necessary to)5 927(multi-file changes are being made, or when more than one window is open on a file,)15 3393 2 720 6603 t
+(update partially obscured windows.)3 1420 1 720 6723 t
+( If)1 125( fully visible, invisible \(fully obscured\), or partially visible.)8 2446(There are three cases: the window is)6 1499 3 970 6879 t
+(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(update routine, the screen will be updated directly.)7 2161 4 720 7119 t
+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 7239 t
+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 840 t
+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 960 t
+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 1080 t
+( than recreating the image off-screen for every change, or clipping all the changes)13 3301(This is much faster)3 769 2 970 1236 t
+( amounts of)2 476( these caches can also consume prohibitive)6 1731( Unfortunately,)1 638(made to the image during its update.)6 1475 4 720 1356 t
+( freed fairly liberally \320 after every change to the front-to-back order of the)13 3030(memory, so they are)3 818 2 720 1476 t
+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 1596 t
+( the user interface causes fully-)5 1278( Also,)1 271( improvement they provide is needed.)5 1544(the only time the performance)4 1227 4 720 1716 t
+( creating a canonically sized and placed window requires)8 2357(obscured windows to be the easiest to make \320)8 1963 2 720 1836 t
+(only a button click \320 which reduces the need for caching still further.)12 2803 1 720 1956 t
+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 2305 2 720 2388 t
+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 2 2602 2628 t
+( 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-contained.\))1 774 4 1933 2988 t
+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 1265 3 2680 3228 t
+( editors use horizontal scrolling to)5 1413( Some)1 288( of text, which are folded onto the next line.)9 1840(cated by long lines)3 779 4 720 3348 t
+( be)1 119(avoid this complication, but to be comfortable this technique requires that lines not)12 3329 2 720 3468 t
+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 importantly, Unix)5 1501( Also,)1 269(such restriction.)1 643 4 720 3588 t
+(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 or a tab.)9 2082 3 2167 3864 t
+( 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 2491 2 2124 3984 t
+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 934( The)1 206(to the next line.)3 625 4 926 4104 t
+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 have)2 403( Tabs)1 246(tab location.)1 496 4 720 4224 t
+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 4344 t
+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 1320 3 3050 4852 t
+(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, without backup storage.)8 2124(The update algorithms)2 911 4 720 5588 t
+( move)1 250(optimal \320 for example, they can clear a pixel that is later going to be written upon \320 but they never)20 4070 2 720 5708 t
+( that doesn't need to be moved, and they move each tile at most once.)14 2975(a tile)1 212 2 720 5828 t
+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 a few tens of characters long.)18 4174 1 720 5948 t
+(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 peculiar shape \(see Figure 2\))11 2352(. The)1 240 3 1020 6224 t
+( reference, call this the)4 937( For)1 197( line.)1 208(zero or more full lines, and possibly a final partial)9 2060 4 720 6344 t
+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 2508 6464 t
+(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, so the)12 2479(boundaries. Because)1 848 3 1267 6584 t
+( tiles \(that is, the images of)6 1092( Then,)1 282(then cleared.)1 511 3 720 6704 t
+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-shape.)12 2986 2 720 6824 t
+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 7100 t
+( new)1 221(eventually, typically when the copying algorithm encounters a tab or newline, the old and)13 3984 2 720 7220 t
+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 270(coordinates of the tile to be copied are the)8 1786 4 720 840 t
+( aligned vertically, and a sequence of at most two)9 2073(beginning and ending edges)3 1154 2 720 960 t
+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 step is to)4 573( The)1 212(copy the remaining tiles.)3 1009 4 720 1080 t
+( number of complete lines in the Z-shape closed by the final)11 2476(dow; the number of lines to be cleared is the)9 1844 2 720 1200 t
+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 construct)11 2760 2 1477 1596 t
+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 string,)1 602 2 720 1716 t
+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 the new text into place.)14 2849 3 720 1836 t
+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 lines.)15 3116 1 970 2232 t
+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 2988 t
+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 2424 3108 t
+(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 editing, it provided no)9 1992 2 1650 3228 t
+( but this was no)4 640( it provided a command to pass selected text through a shell pipeline,)12 2785( Instead,)1 365(editing tasks.)1 530 4 720 3348 t
+(more satisfactory than could be expected of a stopgap measure.)9 2527 1 720 3468 t
+10 CW f
+(Jim)970 3624 w
+10 R f
+( interface to text, and)4 877(was written primarily as a vehicle for experimenting with a mouse-based)10 2981 2 1182 3624 t
+(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 3864 t
+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)1 624 4 2653 4260 t
+( of the)2 259(the answers were essentially a list of features to be found in other editors, which did not provide any)18 4061 2 720 4380 t
+( for a ``global substitute,'' but no)6 1359( instance, one common request was)5 1434( For)1 193(guiding principles I was seeking.)4 1334 4 720 4500 t
+( was looking for a scheme that would sup-)8 1724( I)1 87( it within a cut-and-paste editor.)5 1296(one suggested how to provide)4 1213 4 720 4620 t
+( were)1 225( Ideas)1 267( context of some general command language.)6 1852(port such specialized features comfortably in the)6 1976 4 720 4740 t
+( line lengths)2 499(not forthcoming, though, particularly given my insistence on removing all limits on file sizes,)13 3821 2 720 4860 t
+( a region of the screen that)6 1088( worse, I recognized that, since the mouse could easily indicate)10 2557( Even)1 259(and so on.)2 416 4 720 4980 t
+(was not an integral number of lines, the command language would best forget about newlines altogether,)15 4320 1 720 5100 t
+(and that meant the command language had to treat the file as a single string, not an array of lines.)19 3885 1 720 5220 t
+( I)1 96( very far and it was time to try building.)9 1710(Eventually, I decided that thinking was not getting me)8 2264 3 970 5376 t
+( of)1 115(knew that the terminal part could be built easily \320 that part)11 2477 2 720 5496 t
+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 interpreter and so on.)18 3858(most of the)2 462 2 720 5616 t
+(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 structure, which I liked in principle but which hadn't)9 2377 4 720 5856 t
+( with the way)3 556(by designing the file data structure, starting)6 1770 2 720 5976 t
+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 to make the)6 1053 2 1878 6096 t
+( answer was clearly that it had to be done in two passes, and the)14 2618( The)1 210( implemented.)1 577(bal substitute could be)3 915 4 720 6216 t
+(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 bottom-up, starting from the)5 1666 2 1184 6492 t
+( retrospect, it turned out)4 973( In)1 138(through the command language and up to the code for maintaining the display.)12 3209 3 720 6612 t
+( were several times when I had)6 1247( There)1 284( general.)1 345(well, but this implementation method is not recommended in)8 2444 4 720 6732 t
+( command language, in)3 938( The)1 207( proceed with it.)3 655(a large body of interesting code assembled and no clue how to)11 2520 4 720 6852 t
+( beginning)1 429(particular, took almost a year to figure out, but can be implemented \(given what was there at the)17 3891 2 720 6972 t
+( inventing the)2 568( Similarly,)1 457(of that year\) in a day or two.)7 1199 3 720 7092 t
+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(Part of the design)3 705 2 970 996 t
+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 1116 t
+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 1116 t
+( keeping menus up to date, was unfortunately too unwieldy for such)11 2757(of the protocol, concerned mostly with)5 1563 2 720 1236 t
+( in and)2 276(analysis, and was debugged by more traditional methods, primarily by logging in a file all messages)15 4044 2 720 1356 t
+(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 used by the sixty or so members)12 3025 2 937 1872 t
+( same could not be said of)6 1055( The)1 207(research center in which I work.)5 1294 3 720 1992 t
+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 people from adopting it.)4 1210 4 720 2112 t
+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 days.)7 1282(community, almost everyone switched)3 1579 4 720 2352 t
+(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 and)13 3329 1 1711 2628 t
+(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 2748 t
+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 old ASCII terminal, it's comforting to have a true)16 3320 4 869 2868 t
+(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 3108 t
+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 sophisticated?)6 1221( Does)1 255(using all its capabilities.)3 967 5 720 3384 t
+(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)10 3074 1 1966 3540 t
+(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 1131 3660 t
+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 flag on substitutions, and the)7 2033 2 1215 3780 t
+(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 changes)7 2115 2 1466 3900 t
+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 editor)7 1999 2 970 4176 t
+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(could really be much simpler and still satisfy)7 1901 4 940 4296 t
+( sophistication)1 587( The)1 211( fancy changes.)2 629(tutes,'' and most are content to have the recipe for that and a few other)14 2893 4 720 4416 t
+( do global substitutes)3 848(of the command language is really just a veneer over a design that makes it possible to)16 3472 2 720 4536 t
+( always want something more, however, and it's gratifying to be able)11 2831( people will)2 482( Some)1 283(in a screen editor.)3 724 4 720 4656 t
+( 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 4776 t
+( other words,)2 542( In)1 142( orthogonal to the underlying model.)5 1511(which is by nature)3 759 4 720 4896 t
+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( If)1 120( things possible.)2 656(makes complex)1 629 4 720 5016 t
+(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 970 5292 t
+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 212( macros and variables and so on?)6 1361(tor, with)1 347 4 720 5412 t
+( a wor-)2 284( is one technical reason, though: programmability in editors is largely)10 2778( There)1 282(same every time I use it.)5 976 4 720 5532 t
+( usually short-)2 604( editors are used to make particular,)6 1529( Programmable)1 655(karound for insufficient interactivity.)3 1532 4 720 5652 t
+( things are generally easy)4 1038( If)1 122( providing shorthands for common actions.)5 1751(term, things easy to do, such as by)7 1409 4 720 5772 t
+(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 themselves.)10 3350(and the solutions)2 696 3 720 6012 t
+(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 commands \320 it)8 1944 2 1796 6132 t
+(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)7 1441 2 1740 6990 t
+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 interactive editing.)12 2742( The)1 164(grammable editors.)1 613 3 720 7080 t
+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 problems.)4 905(has several other good points, and)5 1379 4 929 996 t
+( were arrived at serendipi-)4 1054( They)1 258(tural regular expressions, whose usefulness has only begun to be explored.)10 3008 3 720 1116 t
+(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 1236 t
+(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 2602 1 1865 1356 t
+(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 things is its undo capability.)6 1502 4 1628 1512 t
+( 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 1781 3 2788 1632 t
+( not sure how to write some intricate command, because if you make a)13 2869(ple, it's safe to experiment if you're)6 1451 2 720 1752 t
+( from writing)2 545( learned two things about undo)5 1270( I)1 90(mistake, it can be fixed simply and reliably.)7 1800 4 720 1872 t
+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 second, it's necessary, particularly if)16 3862 2 720 1992 t
+(has some subtle properties that may be unfamiliar or error-prone for users.)11 2971 1 720 2112 t
+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 of internal limits and sizes is a virtue.)9 1886 4 1150 2268 t
+(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 cannot even read.)15 3117(is able)1 259 3 1365 2388 t
+( admit)1 255(over, the design keeps the performance linear when doing such operations, although I must)13 3648 2 720 2508 t
+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 it)6 1093( Externally,)1 497(Now, the problems.)2 800 4 970 2784 t
+( design, the user interface in)5 1135( By)1 169(window system.)1 653 3 720 2904 t
+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)1 256 2 2312 3144 t
+(figuration of the system, with a programmable terminal on the far end of an RS-232 link.)15 3554 1 720 3264 t
+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(is reliable; otherwise, people wouldn't use)5 1707 4 1178 3420 t
+( to clean up the code and remove)7 1323(so many new \(to me\) ideas in it, that I would like to see it done over again)17 2997 2 720 3540 t
+( worst part is in the interconnection of the host)9 1898( The)1 209( problems in the implementation.)4 1341(many of the lingering)3 872 4 720 3660 t
+( a redesign for a more conventional window sys-)8 1946(and terminal parts, which might even be able to go away in)11 2374 2 720 3780 t
+( of the connec-)3 598( program must be split in two to use the terminal effectively, but the low bandwidth)15 3342(tem. The)1 380 3 720 3900 t
+( design if performance is to be acceptable.)7 1712(tion forces the separation to occur in an inconvenient part of the)11 2608 2 720 4020 t
+( procedure call protocol driven by the host, emitting only graphics commands, would be)13 3638(A simple remote)2 682 2 720 4140 t
+( the other hand, if the terminal)6 1284( On)1 184(easy to write but wouldn't have nearly the necessary responsiveness.)9 2852 3 720 4260 t
+( simpler file services from the host, regular expression searches would)10 2868(were in control and requested much)5 1452 2 720 4380 t
+( A)1 131( would be unreasonably slow.)4 1226(require that the terminal read the entire file over its RS-232 link, which)12 2963 3 720 4500 t
+( retrospect, the communications protocol)4 1658( In)1 139(compromise in which either end can take control is necessary.)9 2523 3 720 4620 t
+( designed and verified formally, although I do not know of any tool that can adequately)15 3609(should have been)2 711 2 720 4740 t
+(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 language, and few are adept.)11 2884 3 1562 5016 t
+( 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(. Making)1 386 2 1895 5376 t
+( editors, such as)3 647( Some)1 281( mouse.)1 314(much of a break from the seamless model provided by the)10 2338 4 720 5496 t
+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 4 720 5616 t
+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)1 88(be the same.)2 506 4 1017 5736 t
+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( But)1 205(make it easier to learn and to accept.)7 1527 4 720 5856 t
+( hard to decide which way to resolve this problem.)9 2019(much. It's)1 430 2 720 5976 t
+(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 2444(multi-file editor, although in a different)5 1656 3 720 6252 t
+( choice)1 298( the)1 159( If)1 128(decision was made primarily because starting a new program in a Blit is time-consuming.)13 3735 4 720 6372 t
+( still choose the multi-file architecture, because it allows groups of)10 2700(could be made freely, however, I would)6 1620 2 720 6492 t
+( is delightful)2 518( It)1 117( a unit; the usefulness of the multi-file commands is incontrovertible.)10 2822(files to be handled as)4 863 4 720 6612 t
+(to have the source to an entire program available at your fingertips.)11 2683 1 720 6732 t
+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 2711 996 t
+( improved by comments from Al Aho, Jon Bentley, Chris)9 2392( paper was)2 448( This)1 239(enced the command language.)3 1241 4 720 1116 t
+(Fraser, Gerard Holzmann, Brian Kernighan, Ted Kowalski, Doug McIlroy and Dennis Ritchie.)11 3787 1 720 1236 t
+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 1648 t
+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 1975 2 2691 1920 t
+( 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 t
+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 Time-Sharing System)2 857 2 870 2600 t
+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 Manual, 4.1 Berkeley Software)7 2508 2 870 2836 t
+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 t
+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 3444 t
+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 4224 t
+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.)1 219 2 720 4360 t
+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 graphics on the Blit,')14 3180(19. R.)1 224 2 720 4496 t
+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
(DIR) 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 semantics.
+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 appears
+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 tab):
+.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 .
(DIR) 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
+ } 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 widthshow} repeat}
+ {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 systemdict
+ 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 by)6 1635 2 2046 2410 t
+( describe the `shape' of files when the typical)8 1908( is a simple notation that can)6 1222(line. There)1 470 3 1080 2530 t
+( regular)1 316( Using)1 298( is regular expressions.)3 942( notation)1 361( That)1 241(array\255of\255lines picture is inadequate.)3 1442 6 1080 2650 t
+( files has interesting)3 841(expressions to describe the structure in addition to the contents of)10 2759 2 1080 2770 t
+(applications, and yields elegant methods for dealing with some problems the current tools)12 3600 1 1080 2890 t
+( are composed, the result is)5 1157( operations using these expressions)4 1464( When)1 303(handle clumsily.)1 676 4 1080 3010 t
+(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 3646 t
+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 1259 3766 t
+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 2807 4246 t
+( 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 801 2 2198 4366 t
+( example,)1 400( For)1 201(applied to arbitrarily structured input.)4 1549 3 720 4486 t
+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)1 202( level.)1 251(function level instead of the line)5 1330 4 720 4606 t
+(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 help, or at best do poorly.)7 1409 4 1802 4726 t
+(oriented tools can implement it often obscures the original problem.)9 2714 1 720 4846 t
+( consider the problem of turning)5 1320(To see how a line oriented view of text can introduce complication,)11 2750 2 970 5002 t
+( input is an array of blank and non\255blank characters, like this:)11 2451( The)1 205(Peter into silicon.)2 703 3 720 5122 t
+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 circuits:)13 3094 1 720 6482 t
+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 non\255blank characters are in the input.)10 2904 3 720 6842 t
+(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 2898 6962 t
+(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 task,)6 1524 2 1726 7082 t
+( the input is an array of lines, as)8 1345( Since)1 281(geometry, hence arithmetic.)2 1132 3 720 7202 t
+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 program: the line\255driven nature of)17 4320 1 720 2160 t
+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 3077(the line is left to explicit code,)6 1243 2 720 2400 t
+( peculiarity becomes more evident if the problem is)8 2234( This)1 250( manipulation.)1 600(regular expressions for string)3 1236 4 720 2520 t
+(rephrased to demand that each horizontal run of rectangles be folded into a single rectangle:)14 3669 1 720 2640 t
+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 regular expression could do very simply.)19 4187 2 720 4440 t
+(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 the)6 1377 4 2856 4560 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 relationship)12 3746 1 1294 4680 t
+(between the patterns that match text and the actions that manipulate the text is still too weak.\))16 3743 1 720 4800 t
+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 4956 t
+( 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 the pattern.)13 3048 2 1796 5076 t
+( that)1 176( Imagine)1 378( can only be a line.)5 759(much of the power of this idea is being wasted, since the matched text)13 2801 4 720 5196 t
+10 CW f
+(awk)4860 5196 w
+10 R f
+( so the patterns instead passed precisely the text they matched, with no implicit line bound\255)15 3761(were changed)1 559 2 720 5316 t
+( 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 the second version could use)5 1342 2 720 2760 t
+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 than just select the input, the way they)18 4172 2 720 4920 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. Instead,)1 596 2 2069 5040 t
+( expressions are called)3 900( Such)1 250(breaking into lexical tokens\) of the input.)6 1651 3 720 5160 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, because)11 3125(These programs are not)3 945 2 970 5436 t
+( The)1 208(the structure of the input is expressed in the structure of the programs, rather than in procedural code.)17 4112 2 720 5556 t
+( between the patterns and the actions: the patterns select portions of the input)13 3102(labor has been cleanly divided)4 1218 2 720 5676 t
+( actions contain no code to disassemble the input.)8 1979( The)1 205(while the actions operate on them.)5 1370 3 720 5796 t
+(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 2280 2 2459 5952 t
+( \(its output must be run through the C)8 1594(implementation is poor, and since it is not an interactive program)10 2726 2 720 6072 t
+( conve\255)1 302( even ignoring issues of speed and)6 1400( But)1 200(compiler\) it has largely been forgotten as a day\255to\255day tool.)9 2418 4 720 6192 t
+(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 one of the most important aspects of)10 2310 4 1257 6312 t
+( be nested to describe the structure of a file recursively, with)11 2510(section illustrates, structural expressions can)4 1810 2 720 6432 t
+(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 programming that most)11 3018 2 1834 6948 t
+(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 3 3137 7068 t
+( the)1 153( Although)1 434( line lengths by most tools can be frustrating.\))8 1883(placed on)1 390 4 720 7188 t
+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 720 960 t
+10 CW f
+(/string/)1080 1140 w
+10 R f
+( there are short\255)3 646( Although)1 434( not the next line containing ``string''.)6 1565(matches the next occurrence of ``string'',)5 1675 4 720 1320 t
+(hands to simplify common actions, the idea of a line must be stated explicitly in)14 3196 1 720 1440 t
+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 1596 t
+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 not be)2 462 4 2657 1872 t
+( 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 2284 2 2488 1992 t
+(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 command is)15 3369(It is symptomatic that a)4 951 2 720 2472 t
+( deleted contains a newline, this technique doesn't work.)8 2271( if the string to be)5 718( Also,)1 266(used to delete whole lines.)4 1065 4 720 2592 t
+( just an array of characters, but some characters are more equal than others.\))13 3310(\(A file is)2 395 2 720 2712 t
+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 occurrence of the pattern, and)9 2789 2 995 3192 t
+( that this is subtly different)5 1075( Note)1 244( \(not to the line containing the match\).)7 1532(with the current text set to the match)7 1469 4 720 3312 t
+(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 the pattern.)3 832(runs the command if the current text)6 1459 4 720 4188 t
+( lines con\255)2 424( the command to print all)5 1033( Hence)1 309(the current text; it merely selects whether a command will run.)10 2554 4 720 4308 t
+(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 all the lines, and print each one that)8 1740 4 848 4788 t
+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 2790 1 720 5268 t
+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 that contain `i', reject those with two or)13 2878 2 846 5628 t
+( people have overcome the dif\255)5 1253( Some)1 282(string to `I' \(borrowing a little syntax from the substitute command\).)10 2785 3 720 5748 t
+( expressions,)1 530(ficulty of selecting words or identifiers using regular expressions by adding notation to the)13 3790 2 720 5868 t
+( the precise definition of `identifier' is immutable in the implementation.)10 3006(which has the disadvantage that)4 1314 2 720 5988 t
+(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 long\255winded.)14 3442 1 1125 6108 t
+(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 the structure of the)5 1332 4 2069 6624 t
+(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 commands because the `output' of)14 3088 2 1198 6900 t
+( 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 be nested effectively.)6 1894 3 1624 7020 t
+( problem of changing all occurrences of the variable)8 2131( the)1 152( Consider)1 416(the notions of looping and matching.\))5 1530 4 720 7140 t
+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 1080 t
+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)10 2503( To)1 164(in characters or strings.)3 940 4 1229 1200 t
+(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 t
+10 R f
+(This example illustrates the power of composing extractions and conditionals, but it is not artificial: it)15 4070 1 970 1716 t
+(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 3122 1836 t
+(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 transformed from the right end.)8 2194(the left end of a)4 650 4 720 2076 t
+( commands are operating on the same data \(except that the last element of the chain may modify the)18 4070(all the)1 250 2 720 2196 t
+( is being)2 345( What)1 269( flows through the chain.)4 1008(text\); the complete operation is done in place; and no data actually)11 2698 4 720 2316 t
+( in the)2 262(passed from link to link in the chain is a view of the data, until it looks right for the final command)21 4058 2 720 2436 t
+( data stays the same, only the structure is modified.)9 2045(chain. The)1 446 2 720 2556 t
+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 all.)15 3237 1 1803 2952 t
+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 be useful)8 1987 2 1757 3072 t
+( 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 2421 3192 t
+( sub\255pieces \(typically ordinary lines\) by explicit code.)7 2236(actions must break out the)4 1101 2 720 3312 t
+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 3127 1 720 3432 t
+( a)1 84( Consider)1 426(Structural expressions make it easy to specify multiple\255line actions.)8 2812 3 970 3588 t
+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(which has multi\255line records separated by blank lines.)7 2173 4 720 3708 t
+( the line:)2 366(a character indicating the type of information on)7 1981 2 720 3828 t
+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 sequences of non\255empty lines and print any)11 2937 2 846 4308 t
+( be compatible with the other tools, a `)8 1572( \(To)1 198( after `%A'.)2 486(ler' on a line)3 522 4 720 4428 t
+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 regular expression to define the structure of its input.)9 2685 4 720 4668 t
+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 compose expressions makes it easy to tune the)10 2480 4 970 4944 t
+(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 1509 5064 t
+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 breaks the records with author Bimmler back)8 2408 2 720 5424 t
+(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 extracted,)11 3212(There are many other)3 858 2 970 5700 t
+(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 2 720 5940 t
+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 application;)9 2658( This)1 239(gram, not the data.)3 779 4 720 6060 t
+( and sometimes it is just a byte)7 1343(program is an array of functions, but sometimes it is an array of lines,)13 2977 2 720 6180 t
+(stream.)720 6300 w
+( determine the appearance of their input,)6 1627(If the standard commands admitted a structural expression to)8 2443 2 970 6456 t
+( a version of)3 553(many currently annoying problems could become simple: imagine)7 2790 2 720 6576 t
+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 720 6696 t
+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 interesting: not only can the shape of)8 2069 2 1295 6816 t
+( current bewildering maze of options to control the)8 2062( The)1 209(tural expression, but also the shape of the sort key.)9 2049 3 720 6936 t
+( be largely replaced by a structural expression to extract the key from the record, with)15 3434(sort could in principle)3 886 2 720 7056 t
+(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 3 2830 996 t
+( For)1 199( to the actions would be defined, rather than merely selected, by the patterns.)13 3196(earlier, the text passed)3 925 3 720 1116 t
+(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, the expressions would define how the input is)8 2097 4 970 1752 t
+( instance, in)2 496( For)1 197( demarcate fields.)2 722(field separator, the iterations implied by closures in the expression can)10 2905 4 720 1872 t
+(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 999 2 720 2352 t
+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 sub\255expressions for its)6 1993 3 1727 2472 t
+( 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)7 2163( More)1 274(the second, and so on.)4 914 4 1114 2712 t
+( 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 can)7 1858( This)1 250(the original intact string.)3 1049 4 720 2952 t
+( is some subtlety involving the relationship between)7 2192( \(There)1 331(arrays, which is notationally clean.)4 1456 3 720 3072 t
+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 important here.\))16 3571 1 720 3192 t
+(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 2910(, structural)1 429 2 1701 3348 t
+( following program computes)3 1185( The)1 205( actions.)1 333(that is, we would be able to nest structural expressions inside the)11 2597 4 720 3468 t
+(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 1320 3 1330 3888 t
+( extract page numbers)3 1260(/%P.*\([0\2559]+\)\255\([0\2559]+\)/{ #)1 1740 2 1580 4008 t
+(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 5028 t
+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 expressions well.)8 2110(expressions. Obviously,)1 987 4 961 5148 t
+(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 2352 5268 t
+( separated by a dash, which is how)7 1393( innermost pattern searches for lines containing two numbers)8 2451(match. The)1 476 3 720 5388 t
+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 illustrates the basic ideas.)12 2875 3 970 5664 t
+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 114(mismatch between the patterns and the actions.)6 1901 4 720 5784 t
+( lan\255)1 185( A)1 127( pattern\255matching abilities available in the actions.)6 2047(patterns visible in the actions, and by having the)8 1961 4 720 5904 t
+(guage with regular expressions should not base its text manipulation on a)11 2928 1 720 6024 t
+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 expressions to describe the structure of files)10 2525 2 970 6420 t
+( current)1 308(miliar, way to address a number of difficulties the)8 2010 2 720 6540 t
+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 pretending to have addressed them)15 4148 2 720 6660 t
+( the possibilities,)2 678(Rather, I have skipped enthusiastically from example to example to indicate the breadth of)13 3642 2 720 6780 t
+( these ideas, and perhaps to)5 1117( hope is to encourage others to think about)8 1730( My)1 193(not the depth of the difficulties.)5 1280 4 720 6900 t
+(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 Wilson will recognize)11 3264 2 970 996 t
+( hope I have not misrepresented them.)6 1522( I)1 83(in these notes.)2 569 3 720 1116 t
+cleartomark
+showpage
+saveobj restore
+%%EndPage: 7 7
+%%Trailer
+done
+%%Pages: 7
+%%DocumentFonts: Times-Roman Times-Bold Times-Italic Times-Roman Courier
(DIR) 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 as break char */
+ 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 pixels */
+ 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)
(DIR) 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(0)
+
+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 utf sequence (<) */
+ Runeself = 0x80, /* rune and utf sequences are the same (<) */
+ 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*);
(DIR) 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 from layer */
+ int flag; /* flag used by X implementation of libg */
+};
+
+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 arbitrary file descriptor */
+};
+
+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)>>16))
+
+#endif
(DIR) 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 and LBRA */
+ 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 & LBRA */
+ }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);
(DIR) 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 const */
+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/unix.c */
+#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/unix.c */
+#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
(DIR) 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 */
(DIR) 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 forward? */
+ 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 */
(DIR) 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.o\
+ 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
(DIR) 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.1415926535897932384626433832795028841971693993751)
+
+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);
+}
+
(DIR) 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;
+}
(DIR) 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);
+}
(DIR) 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, g,
+ sx, sy, Dx(r), Dy(r), 0, 0, plane);
+ bitblt(d, p, btmp, btmp->r, f);
+ bfree(btmp);
+ }
+ }
+ XFlush(_dpy);
+}
(DIR) 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;
+}
(DIR) 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);
+ }
+}
(DIR) 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;
+}
(DIR) 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 */);
+}
(DIR) 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;
+}
(DIR) 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;
+}
(DIR) 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);
+}
(DIR) 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);
+}
(DIR) 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 both 0
+ */
+ 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, (Pixmap)bmask->id,
+ &_fgcolor, &_bgcolor, -c->offset.x, -c->offset.y);
+ }
+ XDefineCursor(_dpy, (Window)screen.id, (xCursor)c->id);
+}
(DIR) 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 */);
+}
(DIR) 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 */);
+}
(DIR) 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);
+}
+
(DIR) 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 rare).
+ */
+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);
+}
(DIR) 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;
+}
(DIR) 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)NULL);
+}
+
+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);
+}
+
(DIR) 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[j].c[0]), &(mappings[j].c[1]), &(mappings[j].l));
+ 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;
+}
(DIR) 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);
+ }
+ }
+ }
+}
(DIR) 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 Pixmap.
+ * 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
+};
(DIR) 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(scrollr);
+ 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, nitemdrawn);
+ }
+ }
+ *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;
+}
(DIR) 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);
+}
(DIR) 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);
+}
(DIR) 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);
+}
(DIR) 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;
+}
(DIR) 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, second by value */
+{
+ 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;
+}
(DIR) 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 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 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;
+}
(DIR) 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);
+}
(DIR) 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), DefaultVisual(_dpy, DefaultScreen(_dpy)), DefaultColormap(_dpy, DefaultScreen(_dpy)));
+
+ 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;
+}
(DIR) 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);
+}
(DIR) 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, S);
+ 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], DxorS);
+ 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], DxorS);
+ 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), Zero);
+ 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)
+{
+}
(DIR) 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);
+ }
+}
(DIR) 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.y, w-offset, h);
+ XDestroyImage(im);
+}
(DIR) 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);
+}
(DIR) 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 events */
+#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++; XtSetArg(args[n], XtNiconName, 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("FOREGROUND") ? getenv("FOREGROUND") : "#000000", &_fgcolor, &_fgcolor);
+ XAllocNamedColor(_dpy, DefaultColormap(_dpy, DefaultScreen(_dpy)), getenv("BACKGROUND") ? getenv("BACKGROUND") : "#ffffff", &_bgcolor, &_bgcolor);
+
+ 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("FONT") : "Mono");
+ screen.id = 0;
+ XtRealizeWidget(_toplevel);
+
+ pid_t pid = getpid();
+ XChangeProperty(_dpy, XtWindow(_toplevel), XInternAtom(_dpy, "_NET_WM_PID", False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+
+ _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)), DefaultColormap(_dpy, DefaultScreen(_dpy)), &xrcolor, &fontcolor);
+
+ xrcolor.red = _bgcolor.red;
+ xrcolor.green = _bgcolor.green;
+ xrcolor.blue = _bgcolor.blue;
+ XftColorAllocValue(_dpy, DefaultVisual(_dpy, DefaultScreen(_dpy)), DefaultColormap(_dpy, DefaultScreen(_dpy)), &xrcolor, &bgcolor);
+
+ 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->buf, e->n);
+ 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 stipple %x\n",
+ v.foreground, v.background, v.function, v.fill_style,
+ v.font, v.tile, v.stipple);
+}
+#endif
+
(DIR) 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
(DIR) 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 rest up,
+ * 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 allocation done */
+{
+ 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 done */
+{
+ 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 and put q on a box boundary */
+{
+ 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;
+}
(DIR) 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 deletion
+ * 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, q1), S);
+ bitblt(f->b, Pt(f->r.min.x, q0), f->b, Rect(f->r.min.x, q1, f->r.max.x, q2), S);
+ 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->left){
+ --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;
+}
(DIR) 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;
+}
(DIR) 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;
+}
(DIR) 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 frselectf() */
+ _frcklinewrap0(f, &ppt1, b);
+ }
+ f->modified = 1;
+ /*
+ * ppt0 and ppt1 are start and end of insertion as they will appear when
+ * 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 up.
+ */
+ 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->left){
+ --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);
+}
(DIR) 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 to next line */
+{
+ 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 frcharofpt");
+ 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;
+}
(DIR) 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 parallel */
+ 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), c);
+ 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);
+}
(DIR) 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;
+}
(DIR) 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 boxes */
+{
+ 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].wid+b[1].wid<c){
+ _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;
+}
(DIR) 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 */
(DIR) diff --git a/rsam/Makefile b/rsam/Makefile
@@ -0,0 +1,18 @@
+# Copyright (C) 2013-2015 Rob King <jking@deadpixi.com
+# 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)
(DIR) diff --git a/rsam/rsam.c b/rsam/rsam.c
@@ -0,0 +1,147 @@
+/* Copyright 2013-2015 Rob King <jking@deadpixi.com>
+ * This file may be freely redistributed in source or binary form with or without modification.
+ * 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 ? pathconf(home, _PC_PATH_MAX) : PATH_MAX;
+ 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, &readfds, NULL, NULL, NULL) >= 0)
+ {
+ 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;
+}
(DIR) 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
(DIR) 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
+
(DIR) 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 directory
+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
(DIR) 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, sign);
+ 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->type=='-')
+ 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 newline */
+ }
+ 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;
+}
(DIR) 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 time */
+ 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;
+}
(DIR) 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 defaddr count token fn */
+ '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd,
+ 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd,
+ 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
+ 'B', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
+ 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd,
+ 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd,
+ 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd,
+ 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd,
+ 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd,
+ 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
+ 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd,
+ 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd,
+ 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
+ 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd,
+ 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd,
+ 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd,
+ 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd,
+ 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd,
+ 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
+ 'u', 0, 0, 0, 0, aNo, 1, 0, u_cmd,
+ 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
+ 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd,
+ 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
+ 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
+ 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
+ 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
+ '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd,
+ '>', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
+ '<', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
+ '|', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
+ '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd,
+ 'c'|0x100,0, 0, 0, 0, aNo, 0, wordx, cd_cmd,
+ 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 does its own */
+ Straddc(s, '\\');
+ }
+ Straddc(s, c);
+ }
+ ungetch(); /* let client read whether delimeter, '\n' or whatever */
+}
+
+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;
+}
(DIR) 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() stacks */
+ 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 necessary */
+ }
+ /* 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 exists */
+ 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 exists */
+ 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)+nd)));
+ }
+ 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)<BLOCKSIZE/2){
+ 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);
+}
(DIR) 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));
+}
(DIR) 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;
(DIR) 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 together */
+ MAXCACHE=STRSIZE /* max length of cache. must be < 32K-BLOCKSIZE */
+};
+
+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 cache */
+ 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 addresses
+ * passed to Fdelete and Finsert never take into account other changes occurring
+ * 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 multifile */
+ 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->nrunes);
+ }
+ 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 throws away the undo transcript */
+ 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;
+}
(DIR) 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 && length>0)
+ 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") ? getenv("RSH") : RX, machine, rsamname, "-R", (char*)0);
+ 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);
+}
(DIR) 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);
+}
(DIR) 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 pointers */
+ 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 already */
+ 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 already */
+ 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 short */
+ 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;
+}
(DIR) 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 to terminal */
+#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol header stuff */
+#define SNARFSIZE 16384 /* maximum length of exchanged snarf buffer */
+/*
+ * 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 knows */
+ Tcut,
+ Tpaste,
+ Tsnarf,
+ Tstartnewfile, /* terminal just opened a new frame */
+ Twrite, /* write file */
+ Tclose, /* terminal requests file close; check mod. status */
+ 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 data */
+ 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
+ * }
+ */
(DIR) 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--;
+}
+
(DIR) 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;
+}
(DIR) 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.g. 'x' */
+ union{
+ Cmd *cmd; /* target of x, g, {, etc. */
+ String *text; /* text of a, c, i; rhs of s */
+ Addr *addr; /* address for m, t */
+ } g;
+ Cmd *next; /* pointer to next element in {} */
+ short num;
+ ushort flag; /* whatever */
+ ushort cmdc; /* command character; 'x' etc. */
+};
+
+#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 these */
+ int (*fn)(File*, Cmd*); /* function to call with parse tree */
+}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*);
(DIR) 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 *appendonly)
+{
+ 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 *appendonly)
+{
+ 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;
+}
(DIR) 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 with grow */
+
+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);
+ grown = 0;
+ }
+ if(grown)
+ grown += n;
+ else{
+ growpos = p1;
+ grown = n;
+ }
+ }else{
+ Rune *rp;
+ if(grown){
+ outTsll(Hgrow, f->tag, growpos, grown);
+ grown = 0;
+ }
+ rp = hdr.buf+sizeof(hdr.g.csl)/RUNESIZE;
+ 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, tmprstr(rp, n));
+ }
+ }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 life */
+ 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;
+}
(DIR) 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[0] */
+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 on stack */
+#define RBRA 0x10001 /* Right bracket, ) */
+#define LBRA 0x10002 /* Left bracket, ( */
+#define OR 0x10003 /* Alternation, | */
+#define CAT 0x10004 /* Concatentation, implicit operator */
+#define STAR 0x10005 /* Closure, * */
+#define PLUS 0x10006 /* a+ == aa* */
+#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 or 1 a's */
+#define ANY 0x20000 /* Any character but newline, . */
+#define NOP 0x20001 /* No operation, internal use only */
+#define BOL 0x20002 /* Beginning of line, ^ */
+#define EOL 0x20003 /* End of line, $ */
+#define CCLASS 0x20004 /* Character class, [] */
+#define NCCLASS 0x20005 /* Negated character class, [^] */
+#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 expression */
+#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 case */
+ 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 sign */
+ 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].p2 && sp->p[0].p2<sel.p[0].p1))
+ 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;
+ }
+}
(DIR) 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 file */
+ 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.p2-1),Fgetc(f)=='\n'))
+ --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.nused);
+ tempfile.nalloc = file.nused;
+ }
+ tempfile.nused = file.nused;
+ memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*));
+}
(DIR) 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*(transcript+buf) */
+#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 number */
+
+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 disc */
+ /* note: if dirty, cache is really c1, c1+cache.n */
+ 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 change */
+ 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 update */
+ Range tdot; /* what terminal thinks is current range */
+ Range mark; /* tagged spot in text (don't confuse with Mark) */
+ 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 once; once
+ * 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)->getcbuf[(f)->getcp++, (f)->getci++])
+#define Fbgetc(f) (((f)->getci<=0)? Fbgetcload(f, (f)->getcp) : (f)->getcbuf[--(f)->getcp, --(f)->getci])
+
+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);
(DIR) 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
(DIR) 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)){ /* assignment = */
+ 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);
+}
(DIR) 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 allocated */
+#define MAXSIZE 256 /* maximum number of chars for an empty string */
+
+
+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 garbage */
+ 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 there */
+{
+ 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, plus NUL */
+ 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);
+}
(DIR) 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");
+}
(DIR) 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 *appendonly)
+{
+ 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 *appendonly)
+{
+ 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);
+}
+
(DIR) 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].p2);
+ Strinsert(&genstr, tmprstr(genbuf, (sel.p[j].p2-sel.p[j].p1)), genstr.n);
+ }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.p[0].p1)),
+ 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==p)*/
+ if(p!=r.p1 || ((linesel = lineaddr((Posn)0, a3, 1).r), linesel.p2==p))
+ 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;
+}
(DIR) 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 ../include/u.h ../include/libc.h ../sam/mesg.h
(DIR) 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->entire, S);
+ 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;
+ }
+}
(DIR) 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 occurred, in HZ */
+ 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
(DIR) 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);
+}
(DIR) 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);
+}
(DIR) 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 ? 1 : 3);
+ else{
+ t=(Text *)which->user1;
+ if(flselect(which)){
+ outTsl(Tdclick, t->tag, which->p0);
+ 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]=='\n'){
+ 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 rasp is contiguous */
+ 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->p1);
+}
+
+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;
+}
(DIR) 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 state */
+Text *text[MAXFILES]; /* pointer to Text associated with file */
+ushort tag[MAXFILES]; /* text[i].tag, even if text[i] not defined */
+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)) && getr(&r))
+ 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)) && !lock){
+ 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 later */
+ 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;
+}
(DIR) 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[n+4];
+ 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 below
+ 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 progress */
+ 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);
+}
(DIR) 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);
+}
(DIR) 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 and put q on a sect boundary */
+{
+ 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 'rdata'
+ * 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 when called */
+{
+ if(*n >= want)
+ return;
+ free(*s);
+ *s = alloc(RUNESIZE*want);
+ *n = want;
+}
(DIR) 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);
(DIR) 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, my));
+ 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, my));
+ 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);
+ }
+}
(DIR) 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 + strlen(machine) + 100);
+ 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 be
+ * bothered to provide atexit(). we clean up the named pipes on a normal
+ * 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);
+}